001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.oozie.util; 019 020 021 import org.apache.commons.logging.Log; 022 import org.apache.commons.logging.LogFactory; 023 024 import java.text.MessageFormat; 025 import java.util.ArrayList; 026 import java.util.HashMap; 027 import java.util.List; 028 import java.util.Map; 029 030 /** 031 * The <code>XLog</code> class extends the functionality of the Apache common-logging <code>Log</code> interface. <p/> 032 * It provides common prefix support, message templating with variable parameters and selective tee logging to multiple 033 * logs. <p/> It provides also the LogFactory functionality. 034 */ 035 public class XLog implements Log { 036 037 /** 038 * <code>LogInfo</code> stores contextual information to create log prefixes. <p/> <code>LogInfo</code> uses a 039 * <code>ThreadLocal</code> to propagate the context. <p/> <code>LogInfo</code> context parameters are configurable 040 * singletons. 041 */ 042 public static class Info { 043 private static String template = ""; 044 private static List<String> parameterNames = new ArrayList<String>(); 045 046 private static ThreadLocal<Info> tlLogInfo = new ThreadLocal<Info>() { 047 @Override 048 protected Info initialValue() { 049 return new Info(); 050 } 051 052 }; 053 054 /** 055 * Define a <code>LogInfo</code> context parameter. <p/> The parameter name and its contextual value will be 056 * used to create all prefixes. 057 * 058 * @param name name of the context parameter. 059 */ 060 public static void defineParameter(String name) { 061 ParamChecker.notEmpty(name, "name"); 062 int count = parameterNames.size(); 063 if (count > 0) { 064 template += " "; 065 } 066 template += name + "[{" + count + "}]"; 067 parameterNames.add(name); 068 } 069 070 /** 071 * Remove all defined context parameters. <p/> 072 */ 073 public static void reset() { 074 template = ""; 075 parameterNames.clear(); 076 } 077 078 /** 079 * Return the <code>LogInfo</code> instance in context. 080 * 081 * @return The thread local instance of LogInfo 082 */ 083 public static Info get() { 084 return tlLogInfo.get(); 085 } 086 087 /** 088 * Remove the <code>LogInfo</code> instance in context. 089 */ 090 public static void remove() { 091 tlLogInfo.remove(); 092 } 093 094 private Map<String, String> parameters = new HashMap<String, String>(); 095 096 /** 097 * Constructs an empty LogInfo. 098 */ 099 public Info() { 100 } 101 102 103 /** 104 * Construct a new LogInfo object from an existing one. 105 * 106 * @param logInfo LogInfo object to copy parameters from. 107 */ 108 public Info(Info logInfo) { 109 setParameters(logInfo); 110 } 111 112 /** 113 * Clear all parameters set in this logInfo instance. 114 */ 115 public void clear() { 116 parameters.clear(); 117 } 118 119 /** 120 * Set a parameter value in the <code>LogInfo</code> context. 121 * 122 * @param name parameter name. 123 * @param value parameter value. 124 */ 125 public void setParameter(String name, String value) { 126 if (!parameterNames.contains(name)) { 127 throw new IllegalArgumentException(format("Parameter[{0}] not defined", name)); 128 } 129 parameters.put(name, value); 130 } 131 132 /** 133 * Returns the specified parameter. 134 * 135 * @param name parameter name. 136 * @return the parameter value. 137 */ 138 public String getParameter(String name) { 139 return parameters.get(name); 140 } 141 142 /** 143 * Clear a parameter value from the <code>LogInfo</code> context. 144 * 145 * @param name parameter name. 146 */ 147 public void clearParameter(String name) { 148 if (!parameterNames.contains(name)) { 149 throw new IllegalArgumentException(format("Parameter[{0}] not defined", name)); 150 } 151 parameters.remove(name); 152 } 153 154 /** 155 * Set all the parameter values from the given <code>LogInfo</code>. 156 * 157 * @param logInfo <code>LogInfo</code> to copy all parameter values from. 158 */ 159 public void setParameters(Info logInfo) { 160 parameters.clear(); 161 parameters.putAll(logInfo.parameters); 162 } 163 164 /** 165 * Create the <code>LogInfo</code> prefix using the current parameter values. 166 * 167 * @return the <code>LogInfo</code> prefix. 168 */ 169 public String createPrefix() { 170 String[] params = new String[parameterNames.size()]; 171 for (int i = 0; i < params.length; i++) { 172 params[i] = parameters.get(parameterNames.get(i)); 173 if (params[i] == null) { 174 params[i] = "-"; 175 } 176 } 177 return MessageFormat.format(template, (Object[]) params); 178 } 179 180 } 181 182 /** 183 * Return the named logger configured with the {@link org.apache.oozie.util.XLog.Info} prefix. 184 * 185 * @param name logger name. 186 * @return the named logger configured with the {@link org.apache.oozie.util.XLog.Info} prefix. 187 */ 188 public static XLog getLog(String name) { 189 return getLog(name, true); 190 } 191 192 /** 193 * Return the named logger configured with the {@link org.apache.oozie.util.XLog.Info} prefix. 194 * 195 * @param clazz from which the logger name will be derived. 196 * @return the named logger configured with the {@link org.apache.oozie.util.XLog.Info} prefix. 197 */ 198 public static XLog getLog(Class clazz) { 199 return getLog(clazz, true); 200 } 201 202 /** 203 * Return the named logger. 204 * 205 * @param name logger name. 206 * @param prefix indicates if the {@link org.apache.oozie.util.XLog.Info} prefix has to be used or not. 207 * @return the named logger. 208 */ 209 public static XLog getLog(String name, boolean prefix) { 210 return new XLog(LogFactory.getLog(name), (prefix) ? Info.get().createPrefix() : ""); 211 } 212 213 /** 214 * Return the named logger. 215 * 216 * @param clazz from which the logger name will be derived. 217 * @param prefix indicates if the {@link org.apache.oozie.util.XLog.Info} prefix has to be used or not. 218 * @return the named logger. 219 */ 220 public static XLog getLog(Class clazz, boolean prefix) { 221 return new XLog(LogFactory.getLog(clazz), (prefix) ? Info.get().createPrefix() : ""); 222 } 223 224 /** 225 * Reset the logger prefix 226 * 227 * @param log the named logger 228 * @return the named logger with reset prefix 229 */ 230 public static XLog resetPrefix(XLog log) { 231 log.setMsgPrefix(Info.get().createPrefix()); 232 return log; 233 } 234 235 /** 236 * Mask for logging to the standard log. 237 */ 238 public static final int STD = 1; 239 240 /** 241 * Mask for tee logging to the OPS log. 242 */ 243 public static final int OPS = 4; 244 245 private static final int ALL = STD | OPS; 246 247 private static final int[] LOGGER_MASKS = {STD, OPS}; 248 249 //package private for testing purposes. 250 Log[] loggers; 251 252 private String prefix = ""; 253 254 /** 255 * Create a <code>XLog</code> with no prefix. 256 * 257 * @param log Log instance to use for logging. 258 */ 259 public XLog(Log log) { 260 this(log, ""); 261 } 262 263 /** 264 * Create a <code>XLog</code> with a common prefix. <p/> The prefix will be prepended to all log messages. 265 * 266 * @param log Log instance to use for logging. 267 * @param prefix common prefix to use for all log messages. 268 */ 269 public XLog(Log log, String prefix) { 270 this.prefix = prefix; 271 loggers = new Log[2]; 272 loggers[0] = log; 273 loggers[1] = LogFactory.getLog("oozieops"); 274 } 275 276 /** 277 * Return the common prefix. 278 * 279 * @return the common prefix. 280 */ 281 public String getMsgPrefix() { 282 return prefix; 283 } 284 285 /** 286 * Set the common prefix. 287 * 288 * @param prefix the common prefix to set. 289 */ 290 public void setMsgPrefix(String prefix) { 291 this.prefix = (prefix != null) ? prefix : ""; 292 } 293 294 //All the methods from the commonsLogging Log interface will log to the default logger only. 295 296 /** 297 * Log a debug message to the common <code>Log</code>. 298 * 299 * @param o message. 300 */ 301 @Override 302 public void debug(Object o) { 303 log(Level.DEBUG, STD, "{0}", o); 304 } 305 306 /** 307 * Log a debug message and <code>Exception</code> to the common <code>Log</code>. 308 * 309 * @param o message. 310 * @param throwable exception. 311 */ 312 @Override 313 public void debug(Object o, Throwable throwable) { 314 log(Level.DEBUG, STD, "{0}", o, throwable); 315 } 316 317 /** 318 * Log a error message to the common <code>Log</code>. 319 * 320 * @param o message. 321 */ 322 @Override 323 public void error(Object o) { 324 log(Level.ERROR, STD, "{0}", o); 325 } 326 327 /** 328 * Log a error message and <code>Exception</code> to the common <code>Log</code>. 329 * 330 * @param o message. 331 * @param throwable exception. 332 */ 333 @Override 334 public void error(Object o, Throwable throwable) { 335 log(Level.ERROR, STD, "{0}", o, throwable); 336 } 337 338 /** 339 * Log a fatal message to the common <code>Log</code>. 340 * 341 * @param o message. 342 */ 343 @Override 344 public void fatal(Object o) { 345 log(Level.FATAL, STD, "{0}", o); 346 } 347 348 /** 349 * Log a fatal message and <code>Exception</code> to the common <code>Log</code>. 350 * 351 * @param o message. 352 * @param throwable exception. 353 */ 354 @Override 355 public void fatal(Object o, Throwable throwable) { 356 log(Level.FATAL, STD, "{0}", o, throwable); 357 } 358 359 /** 360 * Log a info message to the common <code>Log</code>. 361 * 362 * @param o message. 363 */ 364 @Override 365 public void info(Object o) { 366 log(Level.INFO, STD, "{0}", o); 367 } 368 369 /** 370 * Log a info message and <code>Exception</code> to the common <code>Log</code>. 371 * 372 * @param o message. 373 * @param throwable exception. 374 */ 375 @Override 376 public void info(Object o, Throwable throwable) { 377 log(Level.INFO, STD, "{0}", o, throwable); 378 } 379 380 /** 381 * Log a trace message to the common <code>Log</code>. 382 * 383 * @param o message. 384 */ 385 @Override 386 public void trace(Object o) { 387 log(Level.TRACE, STD, "{0}", o); 388 } 389 390 /** 391 * Log a trace message and <code>Exception</code> to the common <code>Log</code>. 392 * 393 * @param o message. 394 * @param throwable exception. 395 */ 396 @Override 397 public void trace(Object o, Throwable throwable) { 398 log(Level.TRACE, STD, "{0}", o, throwable); 399 } 400 401 /** 402 * Log a warn message to the common <code>Log</code>. 403 * 404 * @param o message. 405 */ 406 @Override 407 public void warn(Object o) { 408 log(Level.WARN, STD, "{0}", o); 409 } 410 411 /** 412 * Log a warn message and <code>Exception</code> to the common <code>Log</code>. 413 * 414 * @param o message. 415 * @param throwable exception. 416 */ 417 @Override 418 public void warn(Object o, Throwable throwable) { 419 log(Level.WARN, STD, "{0}", o, throwable); 420 } 421 422 /** 423 * Return if debug logging is enabled. 424 * 425 * @return <code>true</code> if debug logging is enable, <code>false</code> if not. 426 */ 427 @Override 428 public boolean isDebugEnabled() { 429 return isEnabled(Level.DEBUG, ALL); 430 } 431 432 /** 433 * Return if error logging is enabled. 434 * 435 * @return <code>true</code> if error logging is enable, <code>false</code> if not. 436 */ 437 @Override 438 public boolean isErrorEnabled() { 439 return isEnabled(Level.ERROR, ALL); 440 } 441 442 /** 443 * Return if fatal logging is enabled. 444 * 445 * @return <code>true</code> if fatal logging is enable, <code>false</code> if not. 446 */ 447 @Override 448 public boolean isFatalEnabled() { 449 return isEnabled(Level.FATAL, ALL); 450 } 451 452 /** 453 * Return if info logging is enabled. 454 * 455 * @return <code>true</code> if info logging is enable, <code>false</code> if not. 456 */ 457 @Override 458 public boolean isInfoEnabled() { 459 return isEnabled(Level.INFO, ALL); 460 } 461 462 /** 463 * Return if trace logging is enabled. 464 * 465 * @return <code>true</code> if trace logging is enable, <code>false</code> if not. 466 */ 467 @Override 468 public boolean isTraceEnabled() { 469 return isEnabled(Level.TRACE, ALL); 470 } 471 472 /** 473 * Return if warn logging is enabled. 474 * 475 * @return <code>true</code> if warn logging is enable, <code>false</code> if not. 476 */ 477 @Override 478 public boolean isWarnEnabled() { 479 return isEnabled(Level.WARN, ALL); 480 } 481 482 public enum Level { 483 FATAL, ERROR, INFO, WARN, DEBUG, TRACE 484 } 485 486 private boolean isEnabled(Level level, int loggerMask) { 487 for (int i = 0; i < loggers.length; i++) { 488 if ((LOGGER_MASKS[i] & loggerMask) != 0) { 489 boolean enabled = false; 490 switch (level) { 491 case FATAL: 492 enabled = loggers[i].isFatalEnabled(); 493 break; 494 case ERROR: 495 enabled = loggers[i].isErrorEnabled(); 496 break; 497 case INFO: 498 enabled = loggers[i].isInfoEnabled(); 499 break; 500 case WARN: 501 enabled = loggers[i].isWarnEnabled(); 502 break; 503 case DEBUG: 504 enabled = loggers[i].isDebugEnabled(); 505 break; 506 case TRACE: 507 enabled = loggers[i].isTraceEnabled(); 508 break; 509 } 510 if (enabled) { 511 return true; 512 } 513 } 514 } 515 return false; 516 } 517 518 519 private void log(Level level, int loggerMask, String msgTemplate, Object... params) { 520 loggerMask |= STD; 521 if (isEnabled(level, loggerMask)) { 522 String prefix = getMsgPrefix(); 523 prefix = (prefix != null && prefix.length() > 0) ? prefix + " " : ""; 524 525 String msg = prefix + format(msgTemplate, params); 526 Throwable throwable = getCause(params); 527 528 for (int i = 0; i < LOGGER_MASKS.length; i++) { 529 if (isEnabled(level, loggerMask & LOGGER_MASKS[i])) { 530 Log log = loggers[i]; 531 switch (level) { 532 case FATAL: 533 log.fatal(msg, throwable); 534 break; 535 case ERROR: 536 log.error(msg, throwable); 537 break; 538 case INFO: 539 log.info(msg, throwable); 540 break; 541 case WARN: 542 log.warn(msg, throwable); 543 break; 544 case DEBUG: 545 log.debug(msg, throwable); 546 break; 547 case TRACE: 548 log.trace(msg, throwable); 549 break; 550 } 551 } 552 } 553 } 554 } 555 556 /** 557 * Log a fatal message <code>Exception</code> to the common <code>Log</code>. 558 * 559 * @param msgTemplate message template. 560 * @param params parameters for the message template. If the last parameter is an exception it is logged as such. 561 */ 562 public void fatal(String msgTemplate, Object... params) { 563 log(Level.FATAL, STD, msgTemplate, params); 564 } 565 566 /** 567 * Log a error message <code>Exception</code> to the common <code>Log</code>. 568 * 569 * @param msgTemplate message template. 570 * @param params parameters for the message template. If the last parameter is an exception it is logged as such. 571 */ 572 public void error(String msgTemplate, Object... params) { 573 log(Level.ERROR, STD, msgTemplate, params); 574 } 575 576 /** 577 * Log a info message <code>Exception</code> to the common <code>Log</code>. 578 * 579 * @param msgTemplate message template. 580 * @param params parameters for the message template. If the last parameter is an exception it is logged as such. 581 */ 582 public void info(String msgTemplate, Object... params) { 583 log(Level.INFO, STD, msgTemplate, params); 584 } 585 586 /** 587 * Log a warn message <code>Exception</code> to the common <code>Log</code>. 588 * 589 * @param msgTemplate message template. 590 * @param params parameters for the message template. If the last parameter is an exception it is logged as such. 591 */ 592 public void warn(String msgTemplate, Object... params) { 593 log(Level.WARN, STD, msgTemplate, params); 594 } 595 596 /** 597 * Log a debug message <code>Exception</code> to the common <code>Log</code>. 598 * 599 * @param msgTemplate message template. 600 * @param params parameters for the message template. If the last parameter is an exception it is logged as such. 601 */ 602 public void debug(String msgTemplate, Object... params) { 603 log(Level.DEBUG, STD, msgTemplate, params); 604 } 605 606 /** 607 * Log a trace message <code>Exception</code> to the common <code>Log</code>. 608 * 609 * @param msgTemplate message template. 610 * @param params parameters for the message template. If the last parameter is an exception it is logged as such. 611 */ 612 public void trace(String msgTemplate, Object... params) { 613 log(Level.TRACE, STD, msgTemplate, params); 614 } 615 616 /** 617 * Tee Log a fatal message <code>Exception</code> to the common log and specified <code>Log</code>s. 618 * 619 * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. 620 * @param msgTemplate message template. 621 * @param params parameters for the message template. 622 */ 623 public void fatal(int loggerMask, String msgTemplate, Object... params) { 624 log(Level.FATAL, loggerMask, msgTemplate, params); 625 } 626 627 /** 628 * Tee Log a error message <code>Exception</code> to the common log and specified <code>Log</code>s. 629 * 630 * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. 631 * @param msgTemplate message template. 632 * @param params parameters for the message template. 633 */ 634 public void error(int loggerMask, String msgTemplate, Object... params) { 635 log(Level.ERROR, loggerMask, msgTemplate, params); 636 } 637 638 /** 639 * Tee Log a info message <code>Exception</code> to the common log and specified <code>Log</code>s. 640 * 641 * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. 642 * @param msgTemplate message template. 643 * @param params parameters for the message template. 644 */ 645 public void info(int loggerMask, String msgTemplate, Object... params) { 646 log(Level.INFO, loggerMask, msgTemplate, params); 647 } 648 649 /** 650 * Tee Log a warn message <code>Exception</code> to the common log and specified <code>Log</code>s. 651 * 652 * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. 653 * @param msgTemplate message template. 654 * @param params parameters for the message template. 655 */ 656 public void warn(int loggerMask, String msgTemplate, Object... params) { 657 log(Level.WARN, loggerMask, msgTemplate, params); 658 } 659 660 /** 661 * Tee Log a debug message <code>Exception</code> to the common log and specified <code>Log</code>s. 662 * 663 * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. 664 * @param msgTemplate message template. 665 * @param params parameters for the message template. 666 */ 667 public void debug(int loggerMask, String msgTemplate, Object... params) { 668 log(Level.DEBUG, loggerMask, msgTemplate, params); 669 } 670 671 /** 672 * Tee Log a trace message <code>Exception</code> to the common log and specified <code>Log</code>s. 673 * 674 * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. 675 * @param msgTemplate message template. 676 * @param params parameters for the message template. 677 */ 678 public void trace(int loggerMask, String msgTemplate, Object... params) { 679 log(Level.TRACE, loggerMask, msgTemplate, params); 680 } 681 682 /** 683 * Utility method that does uses the <code>StringFormat</code> to format the message template using the provided 684 * parameters. <p/> In addition to the <code>StringFormat</code> syntax for message templates, it supports 685 * <code>{E}</code> for ENTER. <p/> The last parameter is ignored for the formatting if it is an Exception. 686 * 687 * @param msgTemplate message template. 688 * @param params paramaters to use in the template. If the last parameter is an Exception, it is ignored. 689 * @return formatted message. 690 */ 691 public static String format(String msgTemplate, Object... params) { 692 ParamChecker.notEmpty(msgTemplate, "msgTemplate"); 693 msgTemplate = msgTemplate.replace("{E}", System.getProperty("line.separator")); 694 if (params != null && params.length > 0) { 695 msgTemplate = MessageFormat.format(msgTemplate, params); 696 } 697 return msgTemplate; 698 } 699 700 /** 701 * Utility method that extracts the <code>Throwable</code>, if present, from the parameters. 702 * 703 * @param params parameters. 704 * @return a <code>Throwable</code> instance if it is the last parameter, <code>null</code> otherwise. 705 */ 706 public static Throwable getCause(Object... params) { 707 Throwable throwable = null; 708 if (params != null && params.length > 0 && params[params.length - 1] instanceof Throwable) { 709 throwable = (Throwable) params[params.length - 1]; 710 } 711 return throwable; 712 } 713 714 }