001: /* ====================================================================
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 1997-2003 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution,
020: * if any, must include the following acknowledgment:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowledgment may appear in the software
024: * itself, if and wherever such third-party acknowledgments
025: * normally appear.
026: *
027: * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
028: * must not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org.
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation. For more
052: * information on the Apache Software Foundation, please see
053: * <http://www.apache.org/>.
054: */
055: package org.apache.log;
056:
057: import org.apache.log.util.LoggerListener;
058:
059: /**
060: * The object interacted with by client objects to perform logging.
061: *
062: * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
063: * @author <a href="mailto:peter@apache.org">Peter Donald</a>
064: */
065: public class Logger {
066: private static final Logger[] EMPTY_SET = new Logger[0];
067:
068: /**
069: * Separator character use to separate different categories
070: */
071: public static final char CATEGORY_SEPARATOR = '.';
072:
073: // The ErrorHandler associated with Logger
074: private final ErrorHandler m_errorHandler;
075:
076: ///The ErrorHandler associated with Logger
077: private final LoggerListener m_loggerListener;
078:
079: ///Logger to inherit logtargets and priorities from
080: private final Logger m_parent;
081:
082: ///the fully qualified name of category
083: private final String m_category;
084:
085: ///The list of child loggers associated with this logger
086: private Logger[] m_children;
087:
088: ///The log-targets this logger writes to
089: private LogTarget[] m_logTargets;
090:
091: ///Indicate that logTargets were set with setLogTargets() rather than inherited
092: private boolean m_logTargetsForceSet;
093:
094: ///The priority threshold associated with logger
095: private Priority m_priority;
096:
097: ///Indicate that priority was set with setPriority() rather than inherited
098: private boolean m_priorityForceSet;
099:
100: /**
101: * True means LogEvents will be sent to parents LogTargets
102: * aswell as the ones set for this Logger.
103: */
104: private boolean m_additivity;
105:
106: /**
107: * Protected constructor for use inside the logging toolkit.
108: * You should not be using this constructor directly.
109: *
110: * @param errorHandler the ErrorHandler logger uses to log errors
111: * @param category the fully qualified name of category
112: * @param logTargets the LogTargets associated with logger
113: * @param parent the parent logger (used for inheriting from)
114: */
115: Logger(final ErrorHandler errorHandler,
116: final LoggerListener loggerListener, final String category,
117: final LogTarget[] logTargets, final Logger parent) {
118: m_errorHandler = errorHandler;
119: m_loggerListener = loggerListener;
120: m_category = category;
121: m_logTargets = logTargets;
122: m_parent = parent;
123:
124: if (null == m_logTargets) {
125: unsetLogTargets();
126: }
127:
128: unsetPriority();
129: }
130:
131: /**
132: * Determine if messages of priority DEBUG will be logged.
133: *
134: * @return true if DEBUG messages will be logged
135: */
136: public final boolean isDebugEnabled() {
137: return m_priority.isLowerOrEqual(Priority.DEBUG);
138: }
139:
140: /**
141: * Log a debug priority event.
142: *
143: * @param message the message
144: * @param throwable the throwable
145: */
146: public final void debug(final String message,
147: final Throwable throwable) {
148: if (isDebugEnabled()) {
149: output(Priority.DEBUG, message, throwable);
150: }
151: }
152:
153: /**
154: * Log a debug priority event.
155: *
156: * @param message the message
157: */
158: public final void debug(final String message) {
159: if (isDebugEnabled()) {
160: output(Priority.DEBUG, message, null);
161: }
162: }
163:
164: /**
165: * Determine if messages of priority INFO will be logged.
166: *
167: * @return true if INFO messages will be logged
168: */
169: public final boolean isInfoEnabled() {
170: return m_priority.isLowerOrEqual(Priority.INFO);
171: }
172:
173: /**
174: * Log a info priority event.
175: *
176: * @param message the message
177: * @param throwable the throwable
178: */
179: public final void info(final String message,
180: final Throwable throwable) {
181: if (isInfoEnabled()) {
182: output(Priority.INFO, message, throwable);
183: }
184: }
185:
186: /**
187: * Log a info priority event.
188: *
189: * @param message the message
190: */
191: public final void info(final String message) {
192: if (isInfoEnabled()) {
193: output(Priority.INFO, message, null);
194: }
195: }
196:
197: /**
198: * Determine if messages of priority WARN will be logged.
199: *
200: * @return true if WARN messages will be logged
201: */
202: public final boolean isWarnEnabled() {
203: return m_priority.isLowerOrEqual(Priority.WARN);
204: }
205:
206: /**
207: * Log a warn priority event.
208: *
209: * @param message the message
210: * @param throwable the throwable
211: */
212: public final void warn(final String message,
213: final Throwable throwable) {
214: if (isWarnEnabled()) {
215: output(Priority.WARN, message, throwable);
216: }
217: }
218:
219: /**
220: * Log a warn priority event.
221: *
222: * @param message the message
223: */
224: public final void warn(final String message) {
225: if (isWarnEnabled()) {
226: output(Priority.WARN, message, null);
227: }
228: }
229:
230: /**
231: * Determine if messages of priority ERROR will be logged.
232: *
233: * @return true if ERROR messages will be logged
234: */
235: public final boolean isErrorEnabled() {
236: return m_priority.isLowerOrEqual(Priority.ERROR);
237: }
238:
239: /**
240: * Log a error priority event.
241: *
242: * @param message the message
243: * @param throwable the throwable
244: */
245: public final void error(final String message,
246: final Throwable throwable) {
247: if (isErrorEnabled()) {
248: output(Priority.ERROR, message, throwable);
249: }
250: }
251:
252: /**
253: * Log a error priority event.
254: *
255: * @param message the message
256: */
257: public final void error(final String message) {
258: if (isErrorEnabled()) {
259: output(Priority.ERROR, message, null);
260: }
261: }
262:
263: /**
264: * Determine if messages of priority FATAL_ERROR will be logged.
265: *
266: * @return true if FATAL_ERROR messages will be logged
267: */
268: public final boolean isFatalErrorEnabled() {
269: return m_priority.isLowerOrEqual(Priority.FATAL_ERROR);
270: }
271:
272: /**
273: * Log a fatalError priority event.
274: *
275: * @param message the message
276: * @param throwable the throwable
277: */
278: public final void fatalError(final String message,
279: final Throwable throwable) {
280: if (isFatalErrorEnabled()) {
281: output(Priority.FATAL_ERROR, message, throwable);
282: }
283: }
284:
285: /**
286: * Log a fatalError priority event.
287: *
288: * @param message the message
289: */
290: public final void fatalError(final String message) {
291: if (isFatalErrorEnabled()) {
292: output(Priority.FATAL_ERROR, message, null);
293: }
294: }
295:
296: /**
297: * Make this logger additive. I.e. Send all log events to parent
298: * loggers LogTargets regardless of whether or not the
299: * LogTargets have been overidden.
300: *
301: * This is derived from Log4js notion of Additivity.
302: *
303: * @param additivity true to make logger additive, false otherwise
304: */
305: public final void setAdditivity(final boolean additivity) {
306: m_additivity = additivity;
307: }
308:
309: /**
310: * Determine if messages of priority will be logged.
311: * @param priority the priority
312: * @return true if messages will be logged
313: */
314: public final boolean isPriorityEnabled(final Priority priority) {
315: return m_priority.isLowerOrEqual(priority);
316: }
317:
318: /**
319: * Log a event at specific priority with a certain message and throwable.
320: *
321: * @param priority the priority
322: * @param message the message
323: * @param throwable the throwable
324: */
325: public final void log(final Priority priority,
326: final String message, final Throwable throwable) {
327: if (m_priority.isLowerOrEqual(priority)) {
328: output(priority, message, throwable);
329: }
330: }
331:
332: /**
333: * Log a event at specific priority with a certain message.
334: *
335: * @param priority the priority
336: * @param message the message
337: */
338: public final void log(final Priority priority, final String message) {
339: log(priority, message, null);
340: }
341:
342: /**
343: * Set the priority for this logger.
344: *
345: * @param priority the priority
346: */
347: public synchronized void setPriority(final Priority priority) {
348: m_priority = priority;
349: m_priorityForceSet = true;
350: resetChildPriorities(false);
351: }
352:
353: /**
354: * Unset the priority of Logger.
355: * (Thus it will use it's parent's priority or DEBUG if no parent.
356: */
357: public synchronized void unsetPriority() {
358: unsetPriority(false);
359: }
360:
361: /**
362: * Unset the priority of Logger.
363: * (Thus it will use it's parent's priority or DEBUG if no parent.
364: * If recursive is true unset priorities of all child loggers.
365: *
366: * @param recursive true to unset priority of all child loggers
367: */
368: public synchronized void unsetPriority(final boolean recursive) {
369: if (null != m_parent) {
370: m_priority = m_parent.m_priority;
371: } else {
372: m_priority = Priority.DEBUG;
373: }
374:
375: m_priorityForceSet = false;
376: resetChildPriorities(recursive);
377: }
378:
379: /**
380: * Set the log targets for this logger.
381: *
382: * @param logTargets the Log Targets
383: */
384: public synchronized void setLogTargets(final LogTarget[] logTargets) {
385: if (null != logTargets) {
386: //Make sure that the array passed in does not have any
387: //nulls in it before we actually do the assignment
388: for (int i = 0; i < logTargets.length; i++) {
389: if (null == logTargets[i]) {
390: final String message = "logTargets[ " + i + " ]";
391: throw new NullPointerException(message);
392: }
393: }
394: }
395:
396: m_logTargets = logTargets;
397:
398: setupErrorHandlers();
399: m_logTargetsForceSet = true;
400: resetChildLogTargets(false);
401: }
402:
403: /**
404: * Unset the logtargets for this logger.
405: * This logger (and thus all child loggers who don't specify logtargets) will
406: * inherit from the parents LogTargets.
407: */
408: public synchronized void unsetLogTargets() {
409: unsetLogTargets(false);
410: }
411:
412: /**
413: * Unset the logtargets for this logger and all child loggers if recursive is set.
414: * The loggers unset (and all child loggers who don't specify logtargets) will
415: * inherit from the parents LogTargets.
416: * @param recursive the recursion policy
417: */
418: public synchronized void unsetLogTargets(final boolean recursive) {
419: if (null != m_parent) {
420: m_logTargets = m_parent.safeGetLogTargets();
421: } else {
422: m_logTargets = null;
423: }
424:
425: m_logTargetsForceSet = false;
426: resetChildLogTargets(recursive);
427: }
428:
429: /**
430: * Get all the child Loggers of current logger.
431: *
432: * @return the child loggers
433: */
434: public synchronized Logger[] getChildren() {
435: if (null == m_children) {
436: return EMPTY_SET;
437: }
438:
439: final Logger[] children = new Logger[m_children.length];
440:
441: for (int i = 0; i < children.length; i++) {
442: children[i] = m_children[i];
443: }
444:
445: return children;
446: }
447:
448: /**
449: * Create a new child logger.
450: * The category of child logger is [current-category].subcategory
451: *
452: * @param subCategory the subcategory of this logger
453: * @return the new logger
454: * @exception IllegalArgumentException if subCategory has an empty element name
455: */
456: public synchronized Logger getChildLogger(final String subCategory)
457: throws IllegalArgumentException {
458: final int end = subCategory.indexOf(CATEGORY_SEPARATOR);
459:
460: String nextCategory = null;
461: String remainder = null;
462:
463: if (-1 == end) {
464: nextCategory = subCategory;
465: } else {
466: if (end == 0) {
467: throw new IllegalArgumentException(
468: "Logger categories MUST not have empty elements");
469: }
470:
471: nextCategory = subCategory.substring(0, end);
472: remainder = subCategory.substring(end + 1);
473: }
474:
475: //Get FQN for category
476: String category = null;
477: if (m_category.equals("")) {
478: category = nextCategory;
479: } else {
480: category = m_category + CATEGORY_SEPARATOR + nextCategory;
481: }
482:
483: //Check existing children to see if they
484: //contain next Logger for step in category
485: if (null != m_children) {
486: for (int i = 0; i < m_children.length; i++) {
487: if (m_children[i].m_category.equals(category)) {
488: if (null == remainder) {
489: return m_children[i];
490: } else {
491: return m_children[i].getChildLogger(remainder);
492: }
493: }
494: }
495: }
496:
497: //Create new logger
498: final Logger child = new Logger(m_errorHandler,
499: m_loggerListener, category, null, this );
500:
501: if (m_additivity) {
502: child.setAdditivity(true);
503: }
504:
505: m_loggerListener.loggerCreated(child.m_category, child);
506:
507: //Add new logger to child list
508: if (null == m_children) {
509: m_children = new Logger[] { child };
510: } else {
511: final Logger[] children = new Logger[m_children.length + 1];
512: System.arraycopy(m_children, 0, children, 0,
513: m_children.length);
514: children[m_children.length] = child;
515: m_children = children;
516: }
517:
518: if (null == remainder) {
519: return child;
520: } else {
521: return child.getChildLogger(remainder);
522: }
523: }
524:
525: /**
526: * Retrieve priority associated with Logger.
527: *
528: * @return the loggers priority
529: * @deprecated This method violates Inversion of Control principle.
530: * It will downgraded to protected access in a future
531: * release. When user needs to check priority it is advised
532: * that they use the is[Priority]Enabled() functions.
533: */
534: public final Priority getPriority() {
535: return m_priority;
536: }
537:
538: /**
539: * Retrieve category associated with logger.
540: *
541: * @return the Category
542: * @deprecated This method violates Inversion of Control principle.
543: * If you are relying on its presence then there may be
544: * something wrong with the design of your system
545: */
546: public final String getCategory() {
547: return m_category;
548: }
549:
550: /**
551: * Get a copy of log targets for this logger.
552: *
553: * @return the child loggers
554: * @deprecated This method is deprecated and will be removed in Future version.
555: * Previously it allowed unsafe access to logtargets which permitted
556: * masqurade attacks. It currently returns a zero sized array.
557: */
558: public LogTarget[] getLogTargets() {
559: return new LogTarget[0];
560: }
561:
562: /**
563: * Internal method to do actual outputting.
564: *
565: * @param priority the priority
566: * @param message the message
567: * @param throwable the throwable
568: */
569: private final void output(final Priority priority,
570: final String message, final Throwable throwable) {
571: final LogEvent event = new LogEvent();
572: event.setCategory(m_category);
573: event.setContextStack(ContextStack.getCurrentContext(false));
574: event.setContextMap(ContextMap.getCurrentContext(false));
575:
576: if (null != message) {
577: event.setMessage(message);
578: } else {
579: event.setMessage("");
580: }
581:
582: event.setThrowable(throwable);
583: event.setPriority(priority);
584:
585: //this next line can kill performance. It may be wise to
586: //disable it sometimes and use a more granular approach
587: event.setTime(System.currentTimeMillis());
588:
589: output(event);
590: }
591:
592: private final void output(final LogEvent event) {
593: //cache a copy of targets for thread safety
594: //It is now possible for another thread
595: //to replace m_logTargets
596: final LogTarget[] targets = m_logTargets;
597:
598: if (null == targets) {
599: final String message = "LogTarget is null for category '"
600: + m_category + "'";
601: m_errorHandler.error(message, null, event);
602: } else if (!m_additivity) {
603: fireEvent(event, targets);
604: } else {
605: //If log targets were not inherited, additivity is true
606: //then fire an event to local targets
607: if (m_logTargetsForceSet) {
608: fireEvent(event, targets);
609: }
610:
611: //if we have a parent Logger then send log event to parent
612: if (null != m_parent) {
613: m_parent.output(event);
614: }
615: }
616: }
617:
618: private final void fireEvent(final LogEvent event,
619: final LogTarget[] targets) {
620: for (int i = 0; i < targets.length; i++) {
621: //No need to clone array as addition of a log-target
622: //will result in changin whole array
623: targets[i].processEvent(event);
624: }
625: }
626:
627: /**
628: * Update priority of children if any.
629: */
630: private synchronized void resetChildPriorities(
631: final boolean recursive) {
632: if (null == m_children) {
633: return;
634: }
635:
636: final Logger[] children = m_children;
637:
638: for (int i = 0; i < children.length; i++) {
639: children[i].resetPriority(recursive);
640: }
641: }
642:
643: /**
644: * Update priority of this Logger.
645: * If this loggers priority was manually set then ignore
646: * otherwise get parents priority and update all children's priority.
647: *
648: */
649: private synchronized void resetPriority(final boolean recursive) {
650: if (recursive) {
651: m_priorityForceSet = false;
652: } else if (m_priorityForceSet) {
653: return;
654: }
655:
656: m_priority = m_parent.m_priority;
657: resetChildPriorities(recursive);
658: }
659:
660: /**
661: * Retrieve logtarget array contained in logger.
662: * This method is provided so that child Loggers can access a
663: * copy of parents LogTargets.
664: *
665: * @return the array of LogTargets
666: */
667: private synchronized LogTarget[] safeGetLogTargets() {
668: if (null == m_logTargets) {
669: if (null == m_parent) {
670: return new LogTarget[0];
671: } else {
672: return m_parent.safeGetLogTargets();
673: }
674: } else {
675: final LogTarget[] logTargets = new LogTarget[m_logTargets.length];
676: for (int i = 0; i < logTargets.length; i++) {
677: logTargets[i] = m_logTargets[i];
678: }
679:
680: return logTargets;
681: }
682: }
683:
684: /**
685: * Update logTargets of children if any.
686: */
687: private synchronized void resetChildLogTargets(
688: final boolean recursive) {
689: if (null == m_children) {
690: return;
691: }
692:
693: for (int i = 0; i < m_children.length; i++) {
694: m_children[i].resetLogTargets(recursive);
695: }
696: }
697:
698: /**
699: * Set ErrorHandlers of LogTargets if necessary.
700: */
701: private synchronized void setupErrorHandlers() {
702: if (null == m_logTargets) {
703: return;
704: }
705:
706: for (int i = 0; i < m_logTargets.length; i++) {
707: final LogTarget target = m_logTargets[i];
708: if (target instanceof ErrorAware) {
709: ((ErrorAware) target).setErrorHandler(m_errorHandler);
710: }
711: }
712: }
713:
714: /**
715: * Update logTarget of this Logger.
716: * If this loggers logTarget was manually set then ignore
717: * otherwise get parents logTarget and update all children's logTarget.
718: *
719: */
720: private synchronized void resetLogTargets(final boolean recursive) {
721: if (recursive) {
722: m_logTargetsForceSet = false;
723: } else if (m_logTargetsForceSet) {
724: return;
725: }
726:
727: m_logTargets = m_parent.safeGetLogTargets();
728: resetChildLogTargets(recursive);
729: }
730: }
|