You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by ps...@apache.org on 2007/06/25 08:59:20 UTC
svn commit: r550391 - in /logging/sandbox/jul-to-log4j-bridge/src:
main/java/org/apache/log4j/jul/ test/java/org/apache/log4j/jul/
Author: psmith
Date: Sun Jun 24 23:59:19 2007
New Revision: 550391
URL: http://svn.apache.org/viewvc?view=rev&rev=550391
Log:
Bug 42664 Addition of JULAppender provided by Sagin Mann.
* package structure changed slightly
* Mapping of jul<->log4j levels handled by the JULLevelConverter interface structure
NOTE: one test is currently failing under maven, but all tests appear to pass in Eclipse
(even when run as a group).
Added:
logging/sandbox/jul-to-log4j-bridge/src/main/java/org/apache/log4j/jul/JULAppender.java
logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/AssertionHandler.java
logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderCustomLevelTest.java
logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderTest.java
Added: logging/sandbox/jul-to-log4j-bridge/src/main/java/org/apache/log4j/jul/JULAppender.java
URL: http://svn.apache.org/viewvc/logging/sandbox/jul-to-log4j-bridge/src/main/java/org/apache/log4j/jul/JULAppender.java?view=auto&rev=550391
==============================================================================
--- logging/sandbox/jul-to-log4j-bridge/src/main/java/org/apache/log4j/jul/JULAppender.java (added)
+++ logging/sandbox/jul-to-log4j-bridge/src/main/java/org/apache/log4j/jul/JULAppender.java Sun Jun 24 23:59:19 2007
@@ -0,0 +1,225 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.jul;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.logging.julbridge.JULLevelConverter;
+import org.apache.logging.julbridge.JULLog4jEventConverter;
+
+/**
+ * A Log4j appender for Java Logging API (aka JUL). This appender allows
+ * existing log4j-enabled applications running inside a JUL-enabled environment
+ * (like an application server) to correctly log events in the proper log level
+ * and JUL format.<br>
+ * <br>
+ * The need for this appender comes from several facts:<br>
+ * <br>
+ *
+ * 1. JUL alone does not provide features like layouts, patterns, NDC, etc; so
+ * using JUL takes away important features from developers.<br>
+ * <br>
+ *
+ * 2. Log4j alone does not correctly map Log4j log levels to JUL levels. The
+ * best one can do at the moment is use the all-purpose ConsoleAppender, which
+ * causes all log4j events to appear as either INFO or WARNING, depending on the
+ * appender's target (out/err) and NOT depending on the logging method
+ * (debug/trace/error/etc). Proper mapping to JUL levels (INFO, FINE, FINEST,
+ * etc) is expected to depend on the logging method being called.<br>
+ * <br>
+ *
+ * 3. The fact that there is no appropriate log4j-JUL level mapping also means
+ * that the JUL environment mistreats events generated by the application; for
+ * example, the administrator's level-dependent rules may mistreat log4j events,
+ * or a simple query for all FATAL-level events using standard JUL interfaces
+ * would not show log4j events, making maintenance and troubleshooting
+ * difficult.<br>
+ * <br>
+ *
+ * This appender effectively satisfies those needs:<br>
+ * <br>
+ *
+ * 1. It fully supports layouts like ConsoleAppender<br>
+ * <br>
+ *
+ * 2. It appropriately maps Log4j-levels to JUL levels, depending on the logging
+ * method being called. The mapping is as follows (log4j --> JUL):<br>
+ * all --> ALL<br>
+ * trace --> FINER<br>
+ * debug --> FINE<br>
+ * info --> INFO<br>
+ * warn --> WARNING<br>
+ * error --> SEVERE<br>
+ * fatal --> SEVERE<br>
+ * <br>
+ * Note: there is no *standard* JUL level between WARNING and SEVERE, so both
+ * error and fatal log4j events are mapped to SEVERE.<br>
+ * <br>
+ *
+ * Usage and configuration is identical to using the {@link ConsoleAppener}.
+ * Any application currently configured to use the ConsoleAppender, and wishes
+ * to switch to JulAppender simply has to replace "ConsoleAppender" with
+ * "JulAppender" in its log4j configuration; for example:<br>
+ * <br>
+ * <tt>
+ * log4j.appender.julAppender=org.apache.log4j.JulAppender
+ * log4j.appender.julAppender.layout=org.apache.log4j.PatternLayout
+ * log4j.appender.julAppender.layout.ConversionPattern=%d %-5p %c - %m%n
+ * </tt><br>
+ * <br>
+ * It may even be sensible to reduce the number of elements in the pattern since
+ * JUL usually provides some context information for each logged event (JUL
+ * implementation dependent).<br>
+ * <br>
+ *
+ * Other features:<br>
+ * <br>
+ *
+ * 1. Automatic mapping between log4j logger hierarchy and JUL logger hierarchy;
+ * For example, a log4j logger for class com.mycompany.service.MyWebService will
+ * automatically log JUL events as if it is a JUL logger by the same name, using
+ * the respective JUL configuration and rules for that logger. If such a JUL
+ * logger is not available by the JUL environment, it will attempt to log as if
+ * it is the JUL logger com.mycompany.service, and so on.<br>
+ * <br>
+ *
+ * 2. If a log4j event is being dropped by the JUL environment because its level
+ * is too fine, a warning will be sent to LogLog, indicating a mismatch between
+ * log4j configuration and JUL configuration.<br>
+ * <br>
+ *
+ * 3. If, for some reason, the appropriate JUL logger cannot be obtained
+ * (usually because of a JUL-environment issue), a warning will be sent to
+ * LogLog.<br>
+ * <br>
+ *
+ * @author Sagi Mann (sagimann@gmail.com)
+ * @author psmith
+ */
+public class JULAppender extends AppenderSkeleton {
+
+ private JULLevelConverter levelConverter = JULLog4jEventConverter.DEFAULT_LEVEL_CONVERTER;
+ private String customLevelConverterClass = null;
+
+ /** Creates a new appender with no special layout */
+ public JULAppender() {
+ }
+
+ /** Creates a new appender with the specified layout */
+ public JULAppender(Layout layout) {
+ setLayout(layout);
+ }
+
+ public void activateOptions() {
+
+ if (customLevelConverterClass != null) {
+ try {
+ Class clazz = Class.forName(customLevelConverterClass);
+ levelConverter = (JULLevelConverter) clazz.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Failed to create custom Level Converter class '" +
+ customLevelConverterClass + "'", e);
+ }
+ }
+
+ super.activateOptions();
+ }
+
+ /**
+ * This appender requires a layout. However, if the appender is created
+ * programatically without a layout, it will simply log the event as-is.
+ */
+ public boolean requiresLayout() {
+ return true;
+ }
+
+ public void close() {
+ }
+
+ /**
+ * Append a log event at the appropriate JUL level, depending on the log4j
+ * level.
+ */
+ protected void append(LoggingEvent loggingEvent) {
+ java.util.logging.Logger logger = java.util.logging.Logger
+ .getLogger(loggingEvent.getLoggerName());
+ if (logger == null) {
+ LogLog
+ .warn("Cannot obtain JUL " +
+ loggingEvent.getLoggerName() +
+ ". " +
+ "Verify that this appender is used while an appropriate LogManager " +
+ "is active.");
+ return;
+ }
+
+ // Layout is optional. No layout --> the message is written without
+ // formatting.
+ // In practice, since the requiresLayout method of this appender returns
+ // true,
+ // layout should never be null at this point, however, it is possible
+ // that
+ // the logging service will support optional layouts in the future...
+
+ String msg;
+ if (layout != null) {
+ msg = layout.format(loggingEvent);
+ } else {
+ msg = loggingEvent.getRenderedMessage();
+ }
+
+ // Split the level mapping logic into 2 sections for performance reasons
+ // -
+ // All low-level messages (debug, trace, etc) are separated from the
+ // more common-level messages (a complete binary tree is an
+ // overkill...):
+
+ Level level = loggingEvent.getLevel();
+
+ java.util.logging.Level jullevel = levelConverter
+ .convertLog4jLevel(level);
+ logger.log(jullevel, msg);
+ }
+
+ /**
+ * Returns the customized {@link JULLevelConverter} implementation class
+ * that will be used in place of the default.
+ *
+ * @return
+ */
+ public final String getCustomLevelConverterClass() {
+ return customLevelConverterClass;
+ }
+
+ /**
+ * Sets the name of the customized {@link JULLevelConverter} class
+ * implementation that will be used to map log4j and JUL Logging Levels.
+ *
+ * A non-null value will indicate a custom implementation, otherwise a
+ * default is chosen (see {@link JULLog4jEventConverter}.
+ *
+ * @param customLevelConverterClass
+ */
+ public final void setCustomLevelConverterClass(
+ String customLevelConverterClass) {
+ this.customLevelConverterClass = customLevelConverterClass;
+ }
+}
Added: logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/AssertionHandler.java
URL: http://svn.apache.org/viewvc/logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/AssertionHandler.java?view=auto&rev=550391
==============================================================================
--- logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/AssertionHandler.java (added)
+++ logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/AssertionHandler.java Sun Jun 24 23:59:19 2007
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.jul;
+
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+/**
+ * Helper class - a JUL handler that will verify the logging activity. It is
+ * provided with an expected message to verify. Once the logger logs an event,
+ * it may test the handler using the activated() and passed() methods:<br>
+ * activated() : did the logger attempt to log anything?<br>
+ * passed(): did the logger log the correct message?<br>
+ */
+public class AssertionHandler extends Handler {
+
+ private String expectedMessage;
+ private Level expectedLevel = null;
+
+ private boolean passed = false;
+ private boolean activated = false;
+ private LogRecord lastObservedLogRecord;
+
+ public AssertionHandler() {
+ this.activated = false;
+ this.passed = false;
+ }
+
+ public final Level getExpectedLevel() {
+ return expectedLevel;
+ }
+
+ public final void setExpectedLevel(Level expectedLevel) {
+ this.expectedLevel = expectedLevel;
+ }
+
+ public void close() throws SecurityException {
+ }
+
+ public void flush() {
+ }
+
+ public void publish(LogRecord record) {
+ lastObservedLogRecord = record;
+ activated = true;
+ passed = expectedMessage.equals(record.getMessage());
+ if(expectedLevel != null) {
+ passed = passed && (expectedLevel.intValue() == record.getLevel().intValue());
+ }
+ }
+
+ public void setExpectedMessage(String expectedMessage) {
+ this.expectedMessage = expectedMessage;
+ }
+
+ public boolean passed() {
+ return passed;
+ }
+
+ public boolean activated() {
+ return activated;
+ }
+
+ public final LogRecord getLastObservedLogRecord() {
+ return lastObservedLogRecord;
+ }
+
+ public String getLastObservedLogRecordAsString() {
+
+ LogRecord record = getLastObservedLogRecord();
+ if(record == null ) {
+ return "{no LogRecord seen}";
+ }
+ StringBuffer buf = new StringBuffer();
+ buf.append(record.getLoggerName()).append(",").append(record.getMessage());
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
Added: logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderCustomLevelTest.java
URL: http://svn.apache.org/viewvc/logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderCustomLevelTest.java?view=auto&rev=550391
==============================================================================
--- logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderCustomLevelTest.java (added)
+++ logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderCustomLevelTest.java Sun Jun 24 23:59:19 2007
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.jul;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.logging.julbridge.JULLevelConverter;
+import org.apache.logging.julbridge.JULLog4jBridge;
+
+import junit.framework.TestCase;
+
+public class JULAppenderCustomLevelTest extends TestCase{
+
+ java.util.logging.Logger julLogger;
+ Logger log;
+ AssertionHandler testHandler;
+
+ protected void setUp() throws Exception {
+ // Prepare the JUL logger to simulate a JUL environment
+ // The logger is set to log only INFO-level events or higher.
+ // During testing, a JUL handler will verify the activity of the logger.
+ String julLoggerName = JULAppenderTest.class.getName();
+ julLogger = java.util.logging.Logger.getLogger(julLoggerName);
+ julLogger.setLevel(java.util.logging.Level.INFO);
+ //julLogger.setUseParentHandlers(false);
+ testHandler = new AssertionHandler();
+ julLogger.addHandler(testHandler);
+
+ // Prepare the Log4j logger that will be tested using the JulAppender.
+ // It is set to log ALL events on purpose, in order to test JUL-Log4j
+ // level inconsistencies.
+ log = Logger.getLogger(JULAppenderTest.class);
+ log.setLevel(Level.ALL);
+ JULAppender appender = new JULAppender();
+ appender.setCustomLevelConverterClass(SimpleCustomLevelConverter.class.getName());
+ appender.activateOptions();
+ log.addAppender(appender);
+
+ /*
+ * We expect ALL levels to be INFO in this case.
+ */
+ testHandler.setExpectedLevel(java.util.logging.Level.INFO);
+ }
+
+
+
+ protected void tearDown() throws Exception {
+ JULLog4jBridge.repatriate();
+ }
+
+ public void testCustomLevelTests() {
+ String message;
+
+ message = "this is a trace message";
+ testHandler.setExpectedMessage(message);
+ log.trace(message);
+
+ assertTrue(testHandler.passed());
+
+ message = "this is a debug message";
+ testHandler.setExpectedMessage(message);
+ log.debug(message);
+
+ assertTrue(testHandler.passed());
+ }
+
+ public static class SimpleCustomLevelConverter implements JULLevelConverter{
+
+ public Level convertJuliLevel(java.util.logging.Level juliLevel) {
+ return Level.INFO;
+ }
+
+ public java.util.logging.Level convertLog4jLevel(Level log4jLevel) {
+ return java.util.logging.Level.INFO;
+ }
+
+ }
+
+}
Added: logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderTest.java?view=auto&rev=550391
==============================================================================
--- logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderTest.java (added)
+++ logging/sandbox/jul-to-log4j-bridge/src/test/java/org/apache/log4j/jul/JULAppenderTest.java Sun Jun 24 23:59:19 2007
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.log4j.jul;
+
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.logging.julbridge.JULLog4jBridge;
+
+import junit.framework.*;
+
+/** JulAppender test case
+ *
+ * @author Sagi Mann (sagimann@gmail.com)
+ */
+public class JULAppenderTest extends TestCase {
+
+
+ java.util.logging.Logger julLogger;
+ Logger log;
+ AssertionHandler testHandler;
+
+
+ public JULAppenderTest(String testName) {
+ super(testName);
+ }
+
+
+
+ protected void setUp() throws Exception {
+ // Prepare the JUL logger to simulate a JUL environment
+ // The logger is set to log only INFO-level events or higher.
+ // During testing, a JUL handler will verify the activity of the logger.
+ String julLoggerName = JULAppenderTest.class.getName();
+ julLogger = java.util.logging.Logger.getLogger(julLoggerName);
+ julLogger.setLevel(java.util.logging.Level.INFO);
+ //julLogger.setUseParentHandlers(false);
+ testHandler = new AssertionHandler();
+ julLogger.addHandler(testHandler);
+
+ // Prepare the Log4j logger that will be tested using the JulAppender.
+ // It is set to log ALL events on purpose, in order to test JUL-Log4j
+ // level inconsistencies.
+ log = Logger.getLogger(JULAppenderTest.class);
+ log.setLevel(Level.ALL);
+ JULAppender appender = new JULAppender();
+ appender.activateOptions();
+ log.addAppender(appender);
+ }
+
+
+
+ protected void tearDown() throws Exception {
+ JULLog4jBridge.repatriate();
+ }
+
+
+
+ public void testAllLevels() {
+ String message;
+
+ message = "this is a trace message";
+ log.trace(message);
+ assertFalse("log4j should not have received an event yet, handler should still be inactive: lastObservedEvent:" + testHandler.getLastObservedLogRecordAsString(), testHandler.activated());
+
+ message = "this is a debug message";
+ testHandler.setExpectedMessage(message);
+ log.debug(message);
+ assertFalse(testHandler.activated());
+
+ message = "this is an info message";
+ testHandler.setExpectedMessage(message);
+ log.info(message);
+ assertTrue(testHandler.activated() && testHandler.passed());
+
+ message = "this is a warn message";
+ testHandler.setExpectedMessage(message);
+ log.warn(message);
+ assertTrue(testHandler.activated() && testHandler.passed());
+
+ message = "this is an error message";
+ testHandler.setExpectedMessage(message);
+ log.error(message);
+ assertTrue(testHandler.activated() && testHandler.passed());
+
+ message = "this is a fatal message";
+ testHandler.setExpectedMessage(message);
+ log.fatal(message);
+ assertTrue(testHandler.activated() && testHandler.passed());
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org