001 /**
002 * Source code taken from {@link http://wiki.eclipse.org/EclipseLink/Foundation/Logging}.
003 * Supports use of log4j with eclipselink.
004 */
005 package org.eclipse.persistence.logging;
006
007 import java.io.OutputStream;
008 import java.security.AccessController;
009 import java.security.PrivilegedAction;
010 import java.util.ArrayList;
011 import java.util.HashMap;
012 import java.util.List;
013 import java.util.Map;
014 import java.util.logging.Level;
015
016 import org.apache.commons.logging.Log;
017 import org.apache.commons.logging.LogFactory;
018 import org.apache.commons.logging.impl.Log4JLogger;
019 import org.apache.log4j.Logger;
020 import org.eclipse.persistence.logging.AbstractSessionLog;
021 import org.eclipse.persistence.logging.EclipseLinkLogRecord;
022 import org.eclipse.persistence.logging.JavaLog;
023 import org.eclipse.persistence.logging.SessionLog;
024 import org.eclipse.persistence.logging.SessionLogEntry;
025 import org.eclipse.persistence.sessions.Session;
026
027 /**
028 * PUBLIC:
029 * This is a wrapper class for org.apache.commons.logging.Log.
030 * It is used when messages need to be logged through apache commons logging 1.1.
031 *
032 * History :
033 *
034 * - 05/05/2009 : Updated API to EclipseLink 1.1 version + Javadoc fixes
035 * - 08/05/2009 : Fix for log4j levels in conflict with eclipselink jpa levels (shouldLog)
036 *
037 * TODO : Use Enum instead of int values for Levels (OO Design)
038 *
039 * Requires :
040 * - Jakarta Commons Logging 1.1 : http://commons.apache.org/logging/
041 * - commons-logging-1.1.1.jar
042 *
043 * - Log4j 1.2 : http://logging.apache.org/log4j/
044 * - log4j-1.2.15.jar
045 *
046 *
047 * @author laurent bourges (voparis) : bourges.laurent@gmail.com
048 *
049 * @see AbstractSessionLog
050 * @see JavaLog
051 * @see SessionLogEntry
052 */
053 public final class CommonsLoggingSessionLog extends AbstractSessionLog {
054 //~ Constants --------------------------------------------------------------------------------------------------------
055
056 /**
057 * internal debugger FLAG : use System.out
058 */
059 public static final boolean FORCE_INTERNAL_DEBUG = false;
060 /**
061 * internal debugger FLAG : use a stack trace to find caller class
062 */
063 public static final boolean FORCE_INTERNAL_DEBUG_STACK = false;
064 /**
065 * internal cache FLAG for LogWrapper's levels
066 */
067 public static final boolean USE_INTERNAL_CACHE = true;
068 /**
069 * internal apache commons Logging diagnostic FLAG : use System.out
070 */
071 public static final boolean FORCE_APACHE_COMMONS_LOGGING_DIAGNOSTICS = false;
072 /**
073 * value -1 corresponds to an undefined Level
074 */
075 public static final int UNDEFINED_LEVEL = -1;
076 /**
077 * Stores the default session name in case there is the session name is missing. org.eclipse.persistence
078 * package used by Log4J configuration
079 */
080 public static final String ECLIPSELINK_NAMESPACE = "org.eclipse.persistence";
081 /**
082 * org.eclipse.persistence.default used by Log4J configuration
083 */
084 public static final String DEFAULT_ECLIPSELINK_NAMESPACE = ECLIPSELINK_NAMESPACE + ".default";
085 /**
086 * org.eclipse.persistence.session used by Log4J configuration
087 */
088 public static final String SESSION_ECLIPSELINK_NAMESPACE = ECLIPSELINK_NAMESPACE + ".session";
089 /**
090 * Copied from JavaLog for compatibility issues
091 */
092 public static final String LOGGING_LOCALIZATION_STRING = "org.eclipse.persistence.internal.localization.i18n.LoggingLocalizationResource";
093 /**
094 * Copied from JavaLog for compatibility issues
095 */
096 public static final String TRACE_LOCALIZATION_STRING = "org.eclipse.persistence.internal.localization.i18n.TraceLocalizationResource";
097 /**
098 * Stores all the java.util.logging.Levels. The indexes are EclipseLink logging levels.
099 */
100 public static final Level[] JAVA_LEVELS = new Level[]{
101 Level.ALL, Level.FINEST, Level.FINER, Level.FINE, Level.CONFIG, Level.INFO,
102 Level.WARNING, Level.SEVERE, Level.OFF
103 };
104
105
106 static {
107 /* static Initializer to call onInit method */
108 onInit();
109 }
110
111 //~ Members ----------------------------------------------------------------------------------------------------------
112 /**
113 * formats the EclipseLinkLogRecords. Acts as a static variable but not declared static to avoid classLoader
114 * leaks (clone does not deep clone this instance)
115 */
116 private final FastLogFormatter LOG_FORMATTER = new FastLogFormatter();
117 /**
118 * Represents the HashMap that stores all the name space strings. The keys are category names. The values are
119 * namespace strings. Acts as a static variable but not declared static to avoid classLoader leaks (clone does not
120 * deep clone this map). Note : Unsynchronized Map = should be thread-safe !
121 */
122 private final Map<String, String> NAMESPACE_MAP = new HashMap<String, String>(32);
123 /**
124 * LogWrapper instances. Acts as a static variable but not declared static to avoid classLoader leaks (clone
125 * does not deep clone this map) Note : Unsynchronized Map = should be thread-safe !
126 */
127 private final Map<String, LogWrapper> CATEGORY_LOGGERS = new HashMap<String, LogWrapper>(32);
128 /**
129 * Stores the namespace for session, i.e."org.eclipse.persistence.session.#sessionname#".
130 */
131 private String sessionNameSpace;
132
133 //~ Constructors -----------------------------------------------------------------------------------------------------
134 /**
135 * PUBLIC:
136 *
137 * CommonsLoggingSessionLog Constructor.
138 *
139 * Used by :
140 * org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.updateLoggers :
141 * creates an instance for singletonLog and sessionLog
142 *
143 * This adds a root logger for DEFAULT_ECLIPSELINK_NAMESPACE.
144 */
145 public CommonsLoggingSessionLog() {
146 super();
147
148 if (FORCE_INTERNAL_DEBUG) {
149 debug("CommonsLoggingSessionLog.new : instance : " + this, true);
150 }
151
152 addLogger(DEFAULT_ECLIPSELINK_NAMESPACE, DEFAULT_ECLIPSELINK_NAMESPACE);
153 }
154
155 //~ Methods ----------------------------------------------------------------------------------------------------------
156 /**
157 * PUBLIC:
158 *
159 * OnInit method : define system properties for org.apache.commons.logging
160 */
161 public static final void onInit() {
162
163 if (FORCE_APACHE_COMMONS_LOGGING_DIAGNOSTICS) {
164 /**
165 * The name (<code>org.apache.commons.logging.diagnostics.dest</code>)
166 * of the property used to enable internal commons-logging
167 * diagnostic output, in order to get information on what logging
168 * implementations are being discovered, what classloaders they
169 * are loaded through, etc.
170 * <p>
171 * If a system property of this name is set then the value is
172 * assumed to be the name of a file. The special strings
173 * STDOUT or STDERR (case-sensitive) indicate output to
174 * System.out and System.err respectively.
175 * <p>
176 * Diagnostic logging should be used only to debug problematic
177 * configurations and should not be set in normal production use.
178 */
179 System.setProperty("org.apache.commons.logging.diagnostics.dest", "STDOUT");
180 }
181 }
182
183 /**
184 * PUBLIC:
185 *
186 * onExit method : release all ClassLoader references due to apache commons logging LogFactory
187 *
188 * NOTE : <b>This method must be called in the context of a web application via ServletContextListener.contextDestroyed(ServletContextEvent)</b>
189 *
190 * @see org.apache.commons.logging.LogFactory#release(ClassLoader)
191 */
192 public static final void onExit() {
193 // Classloader unload problem with commons-logging :
194 LogFactory.release(Thread.currentThread().getContextClassLoader());
195 }
196
197 /**
198 * PUBLIC: Return the effective log level for the name space extracted from session and category. If a
199 * Logger's level is set to be null then the Logger will use an effective Level that will be obtained by walking up
200 * the parent tree and using the first non-null Level.
201 *
202 * @param category category
203 *
204 * @return the effective level according to the java.util.logging.Levels
205 */
206 @Override
207 public final int getLevel(final String category) {
208 if (FORCE_INTERNAL_DEBUG) {
209 debug("CommonsLoggingSessionLog.getLevel : IN : category : " + category);
210 }
211
212 final LogWrapper lw = getLogWrapper(category);
213
214 int l = OFF;
215
216 if (lw != null) {
217 l = lw.getLevel();
218 }
219
220 if (FORCE_INTERNAL_DEBUG) {
221 debug("CommonsLoggingSessionLog.getLevel : OUT : category : " + category + " : level : " + getLevelString(l));
222 }
223
224 return l;
225 }
226
227 /**
228 * PUBLIC: Set the log level to a logger with name space extracted from the given category.
229 *
230 * @param level value according to the java.util.logging.Levels
231 * @param category category
232 */
233 @Override
234 public final void setLevel(final int level, final String category) {
235 AccessController.doPrivileged(
236 new PrivilegedAction<Object>() {
237
238 public Object run() {
239 if (FORCE_INTERNAL_DEBUG) {
240 debug("CommonsLoggingSessionLog.setLevel : IN : category : " + category + " to level : " +
241 getLevelString(level), true);
242 }
243
244 final LogWrapper lw = getLogWrapper(category);
245
246 if (lw == null) {
247 error("CommonsLoggingSessionLog.setLevel : category not found : " + category);
248 } else {
249 final Logger logger = getLog4JLogger(lw.getLog());
250
251 if (logger == null) {
252 error("CommonsLoggingSessionLog.setLevel : Logger not found : " + category + " : " + lw.getLog());
253 } else {
254
255 final org.apache.log4j.Level realLevel = getLevelFor(level);
256
257 if (realLevel == org.apache.log4j.Level.OFF) {
258 // force level to OFF :
259 lw.setLevel(OFF);
260 error("CommonsLoggingSessionLog.setLevel : unknown level : " + getLevelString(level) + " : level set to OFF");
261 } else {
262 lw.setLevel(level);
263 }
264 logger.setLevel(realLevel);
265
266 if (logger.isEnabledFor(org.apache.log4j.Level.WARN)) {
267 logger.warn("CommonsLoggingSessionLog.setLevel : Logger Level set to : " + logger.getLevel());
268 }
269
270 if (FORCE_INTERNAL_DEBUG) {
271 debug("CommonsLoggingSessionLog.setLevel : OUT : category : " + category + " to level : " +
272 logger.getLevel());
273 }
274 }
275 }
276
277 // nothing to return :
278 return null;
279 }
280 });
281 }
282
283 /**
284 * PUBLIC: Set the output stream that will receive the formatted log entries. DO nothing as Log4J manages the
285 * appenders (console or files) via Log4J configuration
286 *
287 * @param fileOutputStream the file output stream will receive the formatted log entries.
288 */
289 @Override
290 public final void setWriter(final OutputStream fileOutputStream) {
291 if (FORCE_INTERNAL_DEBUG) {
292 debug("CommonsLoggingSessionLog.setWriter : stream : " + fileOutputStream);
293 }
294
295 // do nothing
296 }
297
298 /**
299 * PUBLIC: Set the session and session namespace.
300 *
301 * @param pSession an eclipselink Session
302 */
303 @Override
304 public final void setSession(final Session pSession) {
305 super.setSession(pSession);
306
307 if (pSession != null) {
308 final String sessionName = pSession.getName();
309
310 if ((sessionName != null) && (sessionName.length() != 0)) {
311 this.sessionNameSpace = SESSION_ECLIPSELINK_NAMESPACE + "." + sessionName;
312 } else {
313 this.sessionNameSpace = DEFAULT_ECLIPSELINK_NAMESPACE;
314 }
315
316 if (FORCE_INTERNAL_DEBUG) {
317 debug("CommonsLoggingSessionLog.setSession : sessionNameSpace : " + this.sessionNameSpace);
318 }
319
320 //Initialize loggers eagerly
321 addLogger(this.sessionNameSpace, this.sessionNameSpace);
322
323 addDefaultLoggers(this.sessionNameSpace);
324 }
325 }
326
327 /**
328 * PUBLIC: Check if a message of the given lev would actually be logged by the logger with name space built
329 * from the given session and category. Return the shouldLog for the given category Note : this method is very very
330 * used so optimized for performance.
331 *
332 * @param level value according to the java.util.logging.Levels
333 * @param category category
334 *
335 * @return true if the given message will be logged
336 */
337 @Override
338 public final boolean shouldLog(final int level, final String category) {
339 if (FORCE_INTERNAL_DEBUG) {
340 debug("CommonsLoggingSessionLog.shouldLog : IN : category : " + category + " : " + getLevelString(level));
341 }
342
343 boolean res = false;
344
345 switch (level) {
346 case OFF:
347 res = false;
348
349 break;
350
351 case ALL:
352 res = true;
353
354 break;
355
356 default:
357
358 final LogWrapper lw = getLogWrapper(category);
359
360 if (lw == null) {
361 error("CommonsLoggingSessionLog.shouldLog : category : " + category + " - NO LOGGER FOUND");
362 res = false;
363 } else {
364 res = level >= lw.getLevel();
365 }
366 }
367
368 if (FORCE_INTERNAL_DEBUG) {
369 debug("CommonsLoggingSessionLog.shouldLog : OUT : category : " + category + " : " + res);
370 }
371
372 return res;
373 }
374
375 /**
376 * PUBLIC: Log a SessionLogEntry
377 *
378 * @param entry SessionLogEntry that holds all the information for a EclipseLink logging event
379 */
380 public final void log(final SessionLogEntry entry) {
381 if (shouldLog(entry.getLevel(), entry.getNameSpace())) {
382 if (FORCE_INTERNAL_DEBUG) {
383 debug("CommonsLoggingSessionLog.log : namespace : " + entry.getNameSpace() + " message : " + entry.getMessage());
384 }
385
386 final Log log = getLog(entry.getNameSpace());
387
388 if (log == null) {
389 error("CommonsLoggingSessionLog.log : no Log found for : " + entry.getNameSpace());
390 } else {
391 final Level javaLevel = getJavaLevel(entry.getLevel());
392
393 internalLog(entry, javaLevel, log);
394 }
395 }
396 }
397
398 /**
399 * PUBLIC: Log a throwable.
400 *
401 * @param throwable a throwable
402 */
403 @Override
404 public final void throwing(final Throwable throwable) {
405 final Log log = getLog(null);
406
407 if (log != null) {
408 log.error(null, throwable);
409 }
410 }
411
412 /**
413 * INTERNAL: Each session owns its own session log because session is stored in the session log
414 *
415 * @return value TODO : Value Description
416 */
417 @Override
418 public final Object clone() {
419 // There is no special treatment required for cloning here
420 // The state of this object is described by member variables sessionLogger and categoryLoggers.
421 // This state depends on session.
422 // If session for the clone is going to be the same as session for this there is no
423 // need to do "deep" cloning.
424 // If not, the session being cloned should call setSession() on its JavaLog object to initialize it correctly.
425 final CommonsLoggingSessionLog cloneLog = (CommonsLoggingSessionLog) super.clone();
426
427 if (FORCE_INTERNAL_DEBUG) {
428 debug("CommonsLoggingSessionLog.clone : cloned instance : " + cloneLog, true);
429 }
430
431 return cloneLog;
432 }
433
434 /**
435 * INTERNAL: Add Logger to the categoryloggers.
436 *
437 * @param loggerCategory category
438 * @param loggerNameSpace name space
439 */
440 private final void addLogger(final String loggerCategory, final String loggerNameSpace) {
441 if (FORCE_INTERNAL_DEBUG) {
442 debug("CommonsLoggingSessionLog.addLogger : category : " + loggerCategory + " in name space : " + loggerNameSpace);
443 }
444
445 this.CATEGORY_LOGGERS.put(loggerCategory, new LogWrapper(this, loggerCategory, LogFactory.getLog(loggerNameSpace)));
446 }
447
448 /**
449 * INTERNAL: Return the name space for the given category from the map.
450 *
451 * @param category category
452 *
453 * @return name space for the given category
454 */
455 private final String getNameSpaceString(final String category) {
456 if (getSession() == null) {
457 return DEFAULT_ECLIPSELINK_NAMESPACE;
458 } else if ((category == null) || (category.length() == 0)) {
459 return this.sessionNameSpace;
460 } else {
461 return this.NAMESPACE_MAP.get(category);
462 }
463 }
464
465 /**
466 * INTERNAL: Return the LogWrapper instance for the given category Note : this method is very very used so
467 * optimized for performance.
468 *
469 * @param category category
470 *
471 * @return LogWrapper instance or null if not found
472 */
473 private final LogWrapper getLogWrapper(final String category) {
474 LogWrapper lw = null;
475
476 if (getSession() == null) {
477 if (FORCE_INTERNAL_DEBUG) {
478 debug("CommonsLoggingSessionLog.getLogWrapper : " + category + " : SESSION NULL");
479 }
480
481 lw = this.CATEGORY_LOGGERS.get(DEFAULT_ECLIPSELINK_NAMESPACE);
482 } else {
483 lw = this.CATEGORY_LOGGERS.get(category);
484
485 if (lw == null) {
486 if (FORCE_INTERNAL_DEBUG) {
487 debug("CommonsLoggingSessionLog.getLogWrapper : " + category + " : CATEGORY IS NULL ?");
488 }
489
490 // really few cases :
491 if ((category == null) || (category.length() == 0)) {
492 lw = this.CATEGORY_LOGGERS.get(this.sessionNameSpace);
493 }
494 }
495 }
496
497 if (FORCE_INTERNAL_DEBUG) {
498 debug("CommonsLoggingSessionLog.getLogWrapper : " + category + " = " + lw);
499 }
500
501 return lw;
502 }
503
504 /**
505 * INTERNAL: Return the apache commons logging Log instance for the given category
506 *
507 * @param category category
508 *
509 * @return value Log instance or null if not found
510 */
511 private final Log getLog(final String category) {
512 if (FORCE_INTERNAL_DEBUG) {
513 debug("CommonsLoggingSessionLog.getLogger : IN : category : " + category);
514 }
515
516 final LogWrapper lw = getLogWrapper(category);
517
518 Log log = null;
519
520 if (lw != null) {
521 log = lw.getLog();
522 }
523
524 if (FORCE_INTERNAL_DEBUG) {
525 debug("CommonsLoggingSessionLog.getLogger : OUT : log : " + log);
526 }
527
528 return log;
529 }
530
531 /**
532 * INTERNAL: Adds default loggers for the given name space
533 *
534 * @param namespace name space
535 */
536 private final void addDefaultLoggers(final String namespace) {
537 if (FORCE_INTERNAL_DEBUG) {
538 debug("CommonsLoggingSessionLog.addDefaultLoggers : nameSpace : " + namespace);
539 }
540
541 String loggerCategory;
542 String loggerNameSpace;
543
544 final String[] categories = SessionLog.loggerCatagories;
545
546 final int size = categories.length;
547
548 for (int i = 0; i < size; i++) {
549 loggerCategory = categories[i];
550 loggerNameSpace = namespace + "." + loggerCategory;
551
552 this.NAMESPACE_MAP.put(loggerCategory, loggerNameSpace);
553 addLogger(loggerCategory, loggerNameSpace);
554 }
555
556 if (FORCE_INTERNAL_DEBUG) {
557 debug("CommonsLoggingSessionLog.addDefaultLoggers : NAMESPACE_MAP : " + this.NAMESPACE_MAP);
558 debug("CommonsLoggingSessionLog.addDefaultLoggers : CATEGORY_LOGGERS : " + this.CATEGORY_LOGGERS);
559 }
560 }
561
562 /**
563 * INTERNAL: Build a LogRecord
564 *
565 * @param entry SessionLogEntry that holds all the information for a EclipseLink logging event
566 * @param level according to eclipselink level
567 * @param log commons-logging 1.1 wrapper
568 */
569 private final void internalLog(final SessionLogEntry entry, final Level level, final Log log) {
570 if (FORCE_INTERNAL_DEBUG) {
571 debug("CommonsLoggingSessionLog.internalLog : " + computeMessage(entry, level));
572 }
573
574 final int entryLevel = entry.getLevel();
575
576 switch (entryLevel) {
577 case SEVERE:
578 log.error(computeMessage(entry, level));
579
580 break;
581
582 case WARNING:
583 log.warn(computeMessage(entry, level));
584
585 break;
586
587 case INFO:
588 case CONFIG:
589 log.info(computeMessage(entry, level));
590
591 break;
592
593 case FINE:
594 case FINER:
595 case FINEST:
596 log.debug(computeMessage(entry, level));
597
598 break;
599
600 case ALL:
601 log.trace(computeMessage(entry, level));
602
603 break;
604
605 case OFF:
606 break;
607
608 default:
609 error("CommonsLoggingSessionLog.internalLog : unknown level : " + entryLevel);
610
611 break;
612 }
613 }
614
615 /**
616 * INTERNAL: Computes the log message
617 *
618 * @param entry SessionLogEntry that holds all the information for a EclipseLink logging event
619 * @param level according to eclipselink level
620 *
621 * @return value TODO : Value Description
622 */
623 private final String computeMessage(final SessionLogEntry entry, final Level level) {
624 // Format message so that we do not depend on the bundle
625 final EclipseLinkLogRecord lr = new EclipseLinkLogRecord(level, formatMessage(entry));
626
627 lr.setSourceClassName(null);
628 lr.setSourceMethodName(null);
629 lr.setLoggerName(getNameSpaceString(entry.getNameSpace()));
630
631 if (shouldPrintSession()) {
632 lr.setSessionString(getSessionString(entry.getSession()));
633 }
634
635 if (shouldPrintConnection()) {
636 lr.setConnection(entry.getConnection());
637 }
638
639 lr.setThrown(entry.getException());
640 lr.setShouldLogExceptionStackTrace(shouldLogExceptionStackTrace());
641
642 lr.setShouldPrintDate(shouldPrintDate());
643 lr.setShouldPrintThread(shouldPrintThread());
644
645 return this.LOG_FORMATTER.format(lr);
646 }
647
648 /**
649 * INTERNAL: Return the corresponding java.util.logging.Level for a given eclipselink level.
650 *
651 * @param level according to eclipselink level
652 *
653 * @return value according to the java.util.logging.Levels
654 */
655 private static final Level getJavaLevel(final int level) {
656 return JAVA_LEVELS[level];
657 }
658
659 /**
660 * INTERNAL: Returns Log4JLogger instance
661 *
662 * @param log commons-logging 1.1 wrapper
663 *
664 * @return Log4JLogger instance
665 */
666 private static final Logger getLog4JLogger(final Log log) {
667 Logger l = null;
668
669 if (log instanceof Log4JLogger) {
670 l = ((Log4JLogger) log).getLogger();
671 }
672
673 if (FORCE_INTERNAL_DEBUG) {
674 debug("CommonsLoggingSessionLog.getLog4JLogger : " + l);
675 }
676
677 return l;
678 }
679
680 /**
681 * PUBLIC: SHOULD BE in AbstractSessionLog
682 *
683 * @see AbstractSessionLog#getLevelString() Return the log level as a string value.
684 */
685 private static final String getLevelString(final int level) {
686 switch (level) {
687 case OFF:
688 return "OFF";
689
690 case SEVERE:
691 return "SEVERE";
692
693 case WARNING:
694 return "WARNING";
695
696 case INFO:
697 return "INFO";
698
699 case CONFIG:
700 return "CONFIG";
701
702 case FINE:
703 return "FINE";
704
705 case FINER:
706 return "FINER";
707
708 case FINEST:
709 return "FINEST";
710
711 case ALL:
712 return "ALL";
713
714 default:
715 return "INFO";
716 }
717 }
718
719 /**
720 * Returns the real Log4J Level
721 * @param level eclipselink level
722 * @return org.apache.log4j.Level
723 */
724 private static final org.apache.log4j.Level getLevelFor(final int level) {
725 org.apache.log4j.Level realLevel = null;
726 switch (level) {
727 case SEVERE:
728 realLevel = org.apache.log4j.Level.ERROR;
729
730 break;
731
732 case WARNING:
733 realLevel = org.apache.log4j.Level.WARN;
734
735 break;
736
737 case INFO:
738 case CONFIG:
739 realLevel = org.apache.log4j.Level.INFO;
740
741 break;
742
743 case FINE:
744 case FINER:
745 case FINEST:
746 realLevel = org.apache.log4j.Level.DEBUG;
747
748 break;
749
750 case ALL:
751 realLevel = org.apache.log4j.Level.ALL;
752
753 break;
754
755 case OFF:
756 realLevel = org.apache.log4j.Level.OFF;
757
758 break;
759
760 default:
761 realLevel = org.apache.log4j.Level.OFF;
762 error("CommonsLoggingSessionLog.getLevelFor : unknown level : " + getLevelString(level) + " = OFF");
763
764 break;
765 }
766 if (FORCE_INTERNAL_DEBUG) {
767 debug("CommonsLoggingSessionLog.getLevelFor : level : " + getLevelString(level) + " = " + realLevel);
768 }
769 return realLevel;
770
771 }
772
773 /**
774 * Prints the message in Std out
775 * @param message message to print
776 */
777 protected final static void debug(final String message) {
778 debug(message, false);
779 }
780
781 /**
782 * Prints the message in Std out
783 * @param message message to print
784 * @param printStack adds a stack trace to find caller class
785 */
786 protected final static void debug(final String message, final boolean printStack) {
787 System.out.println(message);
788 // if (printStack && FORCE_INTERNAL_DEBUG_STACK) {
789 // // to inspect the calling stack :
790 // new Throwable().printStackTrace(System.out);
791 // }
792 }
793
794 /**
795 * Prints the message in Std err
796 * @param message message to print
797 */
798 protected final static void error(final String message) {
799 System.err.println(message);
800 }
801
802 /**
803 * Prints the message in Std err
804 * @param message message to print
805 */
806 protected final static void error(final Throwable th) {
807 th.printStackTrace(System.err);
808 }
809
810 //~ Inner Classes ----------------------------------------------------------------------------------------------------
811 /**
812 * INTERNAL: LogWrapper class wraps the real apache commons logging Log instance
813 */
814 private static final class LogWrapper {
815 //~ Members --------------------------------------------------------------------------------------------------------
816
817 /** parent CommonsLoggingSessionLog instance */
818 private final CommonsLoggingSessionLog sessionLog;
819 /** category for debug mode */
820 private final String category;
821 /** apache commons logging Log instance */
822 private final Log log;
823 /** parent LogWrapper */
824 private final LogWrapper parent;
825 /** child LogWrapper instances */
826 private List<LogWrapper> children = null;
827 /** level as defined by java.util.logging.Levels. Can be changed at runtime */
828 private int level = UNDEFINED_LEVEL;
829 /** cached level as defined by java.util.logging.Levels. Extracted from parent LogWrapper instances */
830 private int cachedLevel = UNDEFINED_LEVEL;
831
832 //~ Constructors ---------------------------------------------------------------------------------------------------
833 /**
834 * INTERNAL:
835 *
836 * Constructor
837 *
838 * @param log apache commons logging Log instance
839 */
840 protected LogWrapper(final CommonsLoggingSessionLog sessionLog, final String category, final Log log) {
841 this.sessionLog = sessionLog;
842 this.category = category;
843 this.log = log;
844
845 final Logger logger = CommonsLoggingSessionLog.getLog4JLogger(log);
846
847 String parentName = null;
848
849 if (logger != null) {
850 parentName = logger.getParent().getName();
851 }
852
853 if ((parentName != null) && !"null".equals(parentName)) {
854 if (FORCE_INTERNAL_DEBUG) {
855 debug("CommonsLoggingSessionLog.LogWrapper.new : parent : " + parentName);
856 }
857
858 this.parent = this.sessionLog.getLogWrapper(parentName);
859
860 if (this.parent != null) {
861 this.parent.addChild(this);
862 }
863 } else {
864 this.parent = null;
865 }
866 }
867
868 //~ Methods --------------------------------------------------------------------------------------------------------
869 /**
870 * INTERNAL: Returns the category
871 *
872 * @return category
873 */
874 protected final String getCategory() {
875 return this.category;
876 }
877
878 /**
879 * INTERNAL: Reset the cachedLevel
880 */
881 protected final void resetCachedLevel() {
882 this.cachedLevel = UNDEFINED_LEVEL;
883
884 if (FORCE_INTERNAL_DEBUG) {
885 debug("CommonsLoggingSessionLog.LogWrapper.setLevel : reset cachedLevel for : " + getCategory());
886 }
887 }
888
889 /**
890 * INTERNAL: Returns the apache commons logging Log instance
891 *
892 * @return apache commons logging Log instance
893 */
894 protected final Log getLog() {
895 return this.log;
896 }
897
898 /**
899 * INTERNAL: Returns the level according to the java.util.logging.Levels
900 *
901 * @return level computed from cachedLevel and internal level
902 */
903 protected final int getLevel() {
904 int res = this.cachedLevel;
905
906 // if the cachedLevel is undefined : compute it from parents :
907 if (res == UNDEFINED_LEVEL) {
908 // first gives the internal level :
909 res = this.level;
910
911 if (FORCE_INTERNAL_DEBUG) {
912 System.out.println(
913 "CommonsLoggingSessionLog.LogWrapper.getLevel : this : " + getCategory() + " : UNCACHED level : " + getLevelString(res));
914 }
915
916 if (res == UNDEFINED_LEVEL) {
917 res = computeLevel(UNDEFINED_LEVEL);
918
919 if (USE_INTERNAL_CACHE) {
920 this.cachedLevel = res;
921 }
922 }
923 }
924 return res;
925 }
926
927 /**
928 * INTERNAL: Defines the level according to the java.util.logging.Levels
929 *
930 * @param level value according to the java.util.logging.Levels
931 */
932 protected final void setLevel(final int level) {
933 this.level = level;
934
935 if (USE_INTERNAL_CACHE) {
936 this.resetCachedLevel();
937 if (this.children != null) {
938 // reset cachedLevel for all children :
939 for (final LogWrapper cw : this.children) {
940 cw.resetCachedLevel();
941 }
942 }
943 }
944 }
945
946 /**
947 * INTERNAL: Adds a child LogWrapper
948 *
949 * @param lw
950 */
951 protected final void addChild(final LogWrapper lw) {
952 if (this.children == null) {
953 this.children = new ArrayList<LogWrapper>();
954 }
955
956 this.children.add(lw);
957
958 if (FORCE_INTERNAL_DEBUG) {
959 System.out.println(
960 "CommonsLoggingSessionLog.LogWrapper.addChild : this : " + getCategory() + " : child : " + lw.getCategory());
961 }
962 }
963
964 /**
965 * INTERNAL: Computes the cached Level
966 *
967 * @param localLevel this.level copy
968 *
969 * @return level
970 */
971 private final int computeLevel(final int localLevel) {
972 LogWrapper pw;
973 LogWrapper lw = this;
974
975 if (FORCE_INTERNAL_DEBUG) {
976 System.out.println(
977 "CommonsLoggingSessionLog.LogWrapper.computeLevel : IN : " + getCategory() + " : level : " +
978 CommonsLoggingSessionLog.getLevelString(localLevel));
979 }
980
981 while ((lw != null) &&
982 (((lw == this) && (localLevel == UNDEFINED_LEVEL)) ||
983 ((lw != this) && (lw.getLevel() == UNDEFINED_LEVEL)))) {
984 pw = lw.parent;
985
986 if (pw != lw) {
987 lw = pw;
988 } else {
989 // exit from loop :
990 lw = null;
991 }
992 }
993
994 int computedLevel = OFF;
995
996 if (lw != null) {
997 computedLevel = lw.getLevel();
998
999 if (FORCE_INTERNAL_DEBUG) {
1000 System.out.println(
1001 "CommonsLoggingSessionLog.LogWrapper.computeLevel : category : " + lw.getCategory() + " - computedLevel : " + getLevelString(computedLevel) + " : " + lw.getLog());
1002 }
1003 final Logger logger = getLog4JLogger(lw.getLog());
1004
1005 if (logger == null) {
1006 error("CommonsLoggingSessionLog.computeLevel : Logger not found : " + lw.getCategory() + " : " + lw.getLog());
1007 } else {
1008
1009 final org.apache.log4j.Level realLevel = getLevelFor(computedLevel);
1010
1011 final boolean enabled = logger.isEnabledFor(realLevel);
1012
1013 if (!enabled) {
1014 computedLevel = OFF;
1015 }
1016 if (FORCE_INTERNAL_DEBUG) {
1017 System.out.println(
1018 "CommonsLoggingSessionLog.LogWrapper.computeLevel : category : " + lw.getCategory() + " : realLevel : " + realLevel + " - enabled = " + enabled);
1019 }
1020 }
1021
1022
1023 }
1024
1025 if (FORCE_INTERNAL_DEBUG) {
1026 System.out.println(
1027 "CommonsLoggingSessionLog.LogWrapper.computeLevel : OUT : " + getCategory() + " : level : " +
1028 CommonsLoggingSessionLog.getLevelString(computedLevel));
1029 }
1030
1031 return computedLevel;
1032 }
1033 }
1034 }
1035 //~ End of file --------------------------------------------------------------------------------------------------------