001: /*
002: * Copyright (C) The Apache Software Foundation. All rights reserved.
003: *
004: * This software is published under the terms of the Apache Software License
005: * version 1.1, a copy of which has been included with this distribution in
006: * the LICENSE file.
007: */
008: package org.jivesoftware.util.log;
009:
010: /**
011: * The object interacted with by client objects to perform logging.
012: *
013: * @author <a href="mailto:peter@apache.org">Peter Donald</a>
014: */
015: public class Logger {
016: ///Separator character use to separate different categories
017: public final static char CATEGORY_SEPARATOR = '.';
018:
019: ///The ErrorHandler associated with Logger
020: private final ErrorHandler m_errorHandler;
021:
022: ///Logger to inherit logtargets and priorities from
023: private final Logger m_parent;
024:
025: ///the fully qualified name of category
026: private final String m_category;
027:
028: ///The list of child loggers associated with this logger
029: private Logger[] m_children;
030:
031: ///The log-targets this logger writes to
032: private LogTarget[] m_logTargets;
033:
034: ///Indicate that logTargets were set with setLogTargets() rather than inherited
035: private boolean m_logTargetsForceSet;
036:
037: ///The priority threshold associated with logger
038: private Priority m_priority;
039:
040: ///Indicate that priority was set with setPriority() rather than inherited
041: private boolean m_priorityForceSet;
042:
043: /**
044: * True means LogEvents will be sent to parents LogTargets
045: * aswell as the ones set for this Logger.
046: */
047: private boolean m_additivity;
048:
049: /**
050: * Protected constructor for use inside the logging toolkit.
051: * You should not be using this constructor directly.
052: *
053: * @param errorHandler the ErrorHandler logger uses to log errors
054: * @param category the fully qualified name of category
055: * @param logTargets the LogTargets associated with logger
056: * @param parent the parent logger (used for inheriting from)
057: */
058: Logger(final ErrorHandler errorHandler, final String category,
059: final LogTarget[] logTargets, final Logger parent) {
060: m_errorHandler = errorHandler;
061: m_category = category;
062: m_logTargets = logTargets;
063: m_parent = parent;
064:
065: if (null == m_logTargets) {
066: unsetLogTargets();
067: }
068:
069: unsetPriority();
070: }
071:
072: /**
073: * Determine if messages of priority DEBUG will be logged.
074: *
075: * @return true if DEBUG messages will be logged
076: */
077: public final boolean isDebugEnabled() {
078: return m_priority.isLowerOrEqual(Priority.DEBUG);
079: }
080:
081: /**
082: * Log a debug priority event.
083: *
084: * @param message the message
085: * @param throwable the throwable
086: */
087: public final void debug(final String message,
088: final Throwable throwable) {
089: if (isDebugEnabled()) {
090: output(Priority.DEBUG, message, throwable);
091: }
092: }
093:
094: /**
095: * Log a debug priority event.
096: *
097: * @param message the message
098: */
099: public final void debug(final String message) {
100: if (isDebugEnabled()) {
101: output(Priority.DEBUG, message, null);
102: }
103: }
104:
105: /**
106: * Determine if messages of priority INFO will be logged.
107: *
108: * @return true if INFO messages will be logged
109: */
110: public final boolean isInfoEnabled() {
111: return m_priority.isLowerOrEqual(Priority.INFO);
112: }
113:
114: /**
115: * Log a info priority event.
116: *
117: * @param message the message
118: */
119: public final void info(final String message,
120: final Throwable throwable) {
121: if (isInfoEnabled()) {
122: output(Priority.INFO, message, throwable);
123: }
124: }
125:
126: /**
127: * Log a info priority event.
128: *
129: * @param message the message
130: */
131: public final void info(final String message) {
132: if (isInfoEnabled()) {
133: output(Priority.INFO, message, null);
134: }
135: }
136:
137: /**
138: * Determine if messages of priority WARN will be logged.
139: *
140: * @return true if WARN messages will be logged
141: */
142: public final boolean isWarnEnabled() {
143: return m_priority.isLowerOrEqual(Priority.WARN);
144: }
145:
146: /**
147: * Log a warn priority event.
148: *
149: * @param message the message
150: * @param throwable the throwable
151: */
152: public final void warn(final String message,
153: final Throwable throwable) {
154: if (isWarnEnabled()) {
155: output(Priority.WARN, message, throwable);
156: }
157: }
158:
159: /**
160: * Log a warn priority event.
161: *
162: * @param message the message
163: */
164: public final void warn(final String message) {
165: if (isWarnEnabled()) {
166: output(Priority.WARN, message, null);
167: }
168: }
169:
170: /**
171: * Determine if messages of priority ERROR will be logged.
172: *
173: * @return true if ERROR messages will be logged
174: */
175: public final boolean isErrorEnabled() {
176: return m_priority.isLowerOrEqual(Priority.ERROR);
177: }
178:
179: /**
180: * Log a error priority event.
181: *
182: * @param message the message
183: * @param throwable the throwable
184: */
185: public final void error(final String message,
186: final Throwable throwable) {
187: if (isErrorEnabled()) {
188: output(Priority.ERROR, message, throwable);
189: }
190: }
191:
192: /**
193: * Log a error priority event.
194: *
195: * @param message the message
196: */
197: public final void error(final String message) {
198: if (isErrorEnabled()) {
199: output(Priority.ERROR, message, null);
200: }
201: }
202:
203: /**
204: * Determine if messages of priority FATAL_ERROR will be logged.
205: *
206: * @return true if FATAL_ERROR messages will be logged
207: */
208: public final boolean isFatalErrorEnabled() {
209: return m_priority.isLowerOrEqual(Priority.FATAL_ERROR);
210: }
211:
212: /**
213: * Log a fatalError priority event.
214: *
215: * @param message the message
216: * @param throwable the throwable
217: */
218: public final void fatalError(final String message,
219: final Throwable throwable) {
220: if (isFatalErrorEnabled()) {
221: output(Priority.FATAL_ERROR, message, throwable);
222: }
223: }
224:
225: /**
226: * Log a fatalError priority event.
227: *
228: * @param message the message
229: */
230: public final void fatalError(final String message) {
231: if (isFatalErrorEnabled()) {
232: output(Priority.FATAL_ERROR, message, null);
233: }
234: }
235:
236: /**
237: * Make this logger additive, which means send all log events to parent
238: * loggers LogTargets regardless of whether or not the
239: * LogTargets have been overidden.
240: * <p/>
241: * This is derived from Log4js notion of Additivity.
242: *
243: * @param additivity true to make logger additive, false otherwise
244: */
245: public final void setAdditivity(final boolean additivity) {
246: m_additivity = additivity;
247: }
248:
249: /**
250: * Determine if messages of priority will be logged.
251: *
252: * @return true if messages will be logged
253: */
254: public final boolean isPriorityEnabled(final Priority priority) {
255: return m_priority.isLowerOrEqual(priority);
256: }
257:
258: /**
259: * Log a event at specific priority with a certain message and throwable.
260: *
261: * @param message the message
262: * @param priority the priority
263: * @param throwable the throwable
264: */
265: public final void log(final Priority priority,
266: final String message, final Throwable throwable) {
267: if (m_priority.isLowerOrEqual(priority)) {
268: output(priority, message, throwable);
269: }
270: }
271:
272: /**
273: * Log a event at specific priority with a certain message.
274: *
275: * @param message the message
276: * @param priority the priority
277: */
278: public final void log(final Priority priority, final String message) {
279: log(priority, message, null);
280: }
281:
282: /**
283: * Set the priority for this logger.
284: *
285: * @param priority the priority
286: */
287: public synchronized void setPriority(final Priority priority) {
288: m_priority = priority;
289: m_priorityForceSet = true;
290: resetChildPriorities(false);
291: }
292:
293: /**
294: * Unset the priority of Logger.
295: * (Thus it will use it's parent's priority or DEBUG if no parent.
296: */
297: public synchronized void unsetPriority() {
298: unsetPriority(false);
299: }
300:
301: /**
302: * Unset the priority of Logger.
303: * (Thus it will use it's parent's priority or DEBUG if no parent.
304: * If recursive is true unset priorities of all child loggers.
305: *
306: * @param recursive true to unset priority of all child loggers
307: */
308: public synchronized void unsetPriority(final boolean recursive) {
309: if (null != m_parent)
310: m_priority = m_parent.m_priority;
311: else
312: m_priority = Priority.DEBUG;
313:
314: m_priorityForceSet = false;
315: resetChildPriorities(recursive);
316: }
317:
318: /**
319: * Set the log targets for this logger.
320: *
321: * @param logTargets the Log Targets
322: */
323: public synchronized void setLogTargets(final LogTarget[] logTargets) {
324: m_logTargets = logTargets;
325: setupErrorHandlers();
326: m_logTargetsForceSet = true;
327: resetChildLogTargets(false);
328: }
329:
330: /**
331: * Unset the logtargets for this logger.
332: * This logger (and thus all child loggers who don't specify logtargets) will
333: * inherit from the parents LogTargets.
334: */
335: public synchronized void unsetLogTargets() {
336: unsetLogTargets(false);
337: }
338:
339: /**
340: * Unset the logtargets for this logger and all child loggers if recursive is set.
341: * The loggers unset (and all child loggers who don't specify logtargets) will
342: * inherit from the parents LogTargets.
343: */
344: public synchronized void unsetLogTargets(final boolean recursive) {
345: if (null != m_parent)
346: m_logTargets = m_parent.safeGetLogTargets();
347: else
348: m_logTargets = null;
349:
350: m_logTargetsForceSet = false;
351: resetChildLogTargets(recursive);
352: }
353:
354: /**
355: * Get all the child Loggers of current logger.
356: *
357: * @return the child loggers
358: */
359: public synchronized Logger[] getChildren() {
360: if (null == m_children)
361: return new Logger[0];
362:
363: final Logger[] children = new Logger[m_children.length];
364:
365: for (int i = 0; i < children.length; i++) {
366: children[i] = m_children[i];
367: }
368:
369: return children;
370: }
371:
372: /**
373: * Create a new child logger.
374: * The category of child logger is [current-category].subcategory
375: *
376: * @param subCategory the subcategory of this logger
377: * @return the new logger
378: * @throws IllegalArgumentException if subCategory has an empty element name
379: */
380: public synchronized Logger getChildLogger(final String subCategory)
381: throws IllegalArgumentException {
382: final int end = subCategory.indexOf(CATEGORY_SEPARATOR);
383:
384: String nextCategory = null;
385: String remainder = null;
386:
387: if (-1 == end)
388: nextCategory = subCategory;
389: else {
390: if (end == 0) {
391: throw new IllegalArgumentException(
392: "Logger categories MUST not have empty elements");
393: }
394:
395: nextCategory = subCategory.substring(0, end);
396: remainder = subCategory.substring(end + 1);
397: }
398:
399: //Get FQN for category
400: String category = null;
401: if (m_category.equals(""))
402: category = nextCategory;
403: else {
404: category = m_category + CATEGORY_SEPARATOR + nextCategory;
405: }
406:
407: //Check existing children to see if they
408: //contain next Logger for step in category
409: if (null != m_children) {
410: for (int i = 0; i < m_children.length; i++) {
411: if (m_children[i].m_category.equals(category)) {
412: if (null == remainder)
413: return m_children[i];
414: else
415: return m_children[i].getChildLogger(remainder);
416: }
417: }
418: }
419:
420: //Create new logger
421: final Logger child = new Logger(m_errorHandler, category, null,
422: this );
423:
424: //Add new logger to child list
425: if (null == m_children) {
426: m_children = new Logger[] { child };
427: } else {
428: final Logger[] children = new Logger[m_children.length + 1];
429: System.arraycopy(m_children, 0, children, 0,
430: m_children.length);
431: children[m_children.length] = child;
432: m_children = children;
433: }
434:
435: if (null == remainder)
436: return child;
437: else
438: return child.getChildLogger(remainder);
439: }
440:
441: /**
442: * Retrieve priority associated with Logger.
443: *
444: * @return the loggers priority
445: * @deprecated This method violates Inversion of Control principle.
446: * It will downgraded to protected access in a future
447: * release. When user needs to check priority it is advised
448: * that they use the is[Priority]Enabled() functions.
449: */
450: public final Priority getPriority() {
451: return m_priority;
452: }
453:
454: /**
455: * Retrieve category associated with logger.
456: *
457: * @return the Category
458: * @deprecated This method violates Inversion of Control principle.
459: * If you are relying on its presence then there may be
460: * something wrong with the design of your system
461: */
462: public final String getCategory() {
463: return m_category;
464: }
465:
466: /**
467: * Get a copy of log targets for this logger.
468: *
469: * @return the child loggers
470: */
471: public LogTarget[] getLogTargets() {
472: // Jive change - we ignore the deprecated warning above and just return the log targets
473: // since it's a closed system for us anyways
474: return m_logTargets;
475: }
476:
477: /**
478: * Internal method to do actual outputting.
479: *
480: * @param priority the priority
481: * @param message the message
482: * @param throwable the throwable
483: */
484: private final void output(final Priority priority,
485: final String message, final Throwable throwable) {
486: final LogEvent event = new LogEvent();
487: event.setCategory(m_category);
488: // event.setContextStack( ContextStack.getCurrentContext( false ) );
489: event.setContextMap(ContextMap.getCurrentContext(false));
490:
491: if (null != message) {
492: event.setMessage(message);
493: } else {
494: event.setMessage("");
495: }
496:
497: event.setThrowable(throwable);
498: event.setPriority(priority);
499:
500: //this next line can kill performance. It may be wise to
501: //disable it sometimes and use a more granular approach
502: event.setTime(System.currentTimeMillis());
503:
504: output(event);
505: }
506:
507: private final void output(final LogEvent event) {
508: //cache a copy of targets for thread safety
509: //It is now possible for another thread
510: //to replace m_logTargets
511: final LogTarget[] targets = m_logTargets;
512:
513: if (null == targets) {
514: final String message = "LogTarget is null for category '"
515: + m_category + "'";
516: m_errorHandler.error(message, null, event);
517: } else if (!m_additivity) {
518: fireEvent(event, targets);
519: } else {
520: //If log targets were not inherited, additivity is true
521: //then fire an event to local targets
522: if (m_logTargetsForceSet) {
523: fireEvent(event, targets);
524: }
525:
526: //if we have a parent Logger then send log event to parent
527: if (null != m_parent) {
528: m_parent.output(event);
529: }
530: }
531: }
532:
533: private final void fireEvent(final LogEvent event,
534: final LogTarget[] targets) {
535: for (int i = 0; i < targets.length; i++) {
536: //No need to clone array as addition of a log-target
537: //will result in changin whole array
538: targets[i].processEvent(event);
539: }
540: }
541:
542: /**
543: * Update priority of children if any.
544: */
545: private synchronized void resetChildPriorities(
546: final boolean recursive) {
547: if (null == m_children)
548: return;
549:
550: final Logger[] children = m_children;
551:
552: for (int i = 0; i < children.length; i++) {
553: children[i].resetPriority(recursive);
554: }
555: }
556:
557: /**
558: * Update priority of this Logger.
559: * If this loggers priority was manually set then ignore
560: * otherwise get parents priority and update all children's priority.
561: */
562: private synchronized void resetPriority(final boolean recursive) {
563: if (recursive) {
564: m_priorityForceSet = false;
565: } else if (m_priorityForceSet) {
566: return;
567: }
568:
569: m_priority = m_parent.m_priority;
570: resetChildPriorities(recursive);
571: }
572:
573: /**
574: * Retrieve logtarget array contained in logger.
575: * This method is provided so that child Loggers can access a
576: * copy of parents LogTargets.
577: *
578: * @return the array of LogTargets
579: */
580: private synchronized LogTarget[] safeGetLogTargets() {
581: if (null == m_logTargets) {
582: if (null == m_parent)
583: return new LogTarget[0];
584: else
585: return m_parent.safeGetLogTargets();
586: } else {
587: final LogTarget[] logTargets = new LogTarget[m_logTargets.length];
588:
589: for (int i = 0; i < logTargets.length; i++) {
590: logTargets[i] = m_logTargets[i];
591: }
592:
593: return logTargets;
594: }
595: }
596:
597: /**
598: * Update logTargets of children if any.
599: */
600: private synchronized void resetChildLogTargets(
601: final boolean recursive) {
602: if (null == m_children)
603: return;
604:
605: for (int i = 0; i < m_children.length; i++) {
606: m_children[i].resetLogTargets(recursive);
607: }
608: }
609:
610: /**
611: * Set ErrorHandlers of LogTargets if necessary.
612: */
613: private synchronized void setupErrorHandlers() {
614: if (null == m_logTargets)
615: return;
616:
617: for (int i = 0; i < m_logTargets.length; i++) {
618: final LogTarget target = m_logTargets[i];
619: if (target instanceof ErrorAware) {
620: ((ErrorAware) target).setErrorHandler(m_errorHandler);
621: }
622: }
623: }
624:
625: /**
626: * Update logTarget of this Logger.
627: * If this loggers logTarget was manually set then ignore
628: * otherwise get parents logTarget and update all children's logTarget.
629: */
630: private synchronized void resetLogTargets(final boolean recursive) {
631: if (recursive) {
632: m_logTargetsForceSet = false;
633: } else if (m_logTargetsForceSet) {
634: return;
635: }
636:
637: m_logTargets = m_parent.safeGetLogTargets();
638: resetChildLogTargets(recursive);
639: }
640: }
|