This project has retired. For details please refer to its
Attic page.
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 }