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