http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/Log4JULogger.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/Log4JULogger.java b/src/main/java/org/apache/log4j/spi/Log4JULogger.java new file mode 100644 index 0000000..2476810 --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/Log4JULogger.java @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.spi; + +import org.apache.log4j.Logger; +import org.apache.log4j.ULogger; +import org.apache.log4j.helpers.MessageFormatter; +import org.apache.log4j.Level; + + +/** + * An implementation of ULogger on org.apache.log4j.Logger. + */ +public final class Log4JULogger implements ULogger { + + /** + * Wrapped log4j logger. + */ + private final Logger logger; + + /** + * Create a new instance. + * + * @param l logger, may not be null. + */ + public Log4JULogger(final Logger l) { + super(); + if (l == null) { + throw new NullPointerException("l"); + } + logger = l; + } + + /** + * {@inheritDoc} + */ + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + /** + * {@inheritDoc} + */ + public void debug(final Object msg) { + logger.debug(msg); + } + + /** + * {@inheritDoc} + */ + public void debug(final Object parameterizedMsg, + final Object param1) { + if (logger.isDebugEnabled()) { + logger.debug(MessageFormatter.format( + parameterizedMsg.toString(), param1)); + } + } + + /** + * {@inheritDoc} + */ + public void debug(final String parameterizedMsg, + final Object param1, + final Object param2) { + if (logger.isDebugEnabled()) { + logger.debug(MessageFormatter.format( + parameterizedMsg.toString(), param1, param2)); + } + } + + /** + * {@inheritDoc} + */ + public void debug(final Object msg, + final Throwable t) { + logger.debug(msg, t); + } + + + /** + * {@inheritDoc} + */ + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + /** + * {@inheritDoc} + */ + public void info(final Object msg) { + logger.info(msg); + } + + + /** + * {@inheritDoc} + */ + public void info(final Object parameterizedMsg, + final Object param1) { + if (logger.isInfoEnabled()) { + logger.info(MessageFormatter.format( + parameterizedMsg.toString(), param1)); + } + } + + /** + * {@inheritDoc} + */ + public void info(final String parameterizedMsg, + final Object param1, + final Object param2) { + if (logger.isInfoEnabled()) { + logger.info(MessageFormatter.format( + parameterizedMsg.toString(), + param1, + param2)); + } + } + + /** + * {@inheritDoc} + */ + public void info(final Object msg, final Throwable t) { + logger.info(msg, t); + } + + /** + * {@inheritDoc} + */ + public boolean isWarnEnabled() { + return logger.isEnabledFor(Level.WARN); + } + + /** + * {@inheritDoc} + */ + public void warn(final Object msg) { + logger.warn(msg); + } + + /** + * {@inheritDoc} + */ + public void warn(final Object parameterizedMsg, + final Object param1) { + if (logger.isEnabledFor(Level.WARN)) { + logger.warn(MessageFormatter.format( + parameterizedMsg.toString(), param1)); + } + } + + /** + * {@inheritDoc} + */ + public void warn(final String parameterizedMsg, + final Object param1, + final Object param2) { + if (logger.isEnabledFor(Level.WARN)) { + logger.warn(MessageFormatter.format( + parameterizedMsg.toString(), param1, param2)); + } + } + + /** + * {@inheritDoc} + */ + public void warn(final Object msg, final Throwable t) { + logger.warn(msg, t); + } + + /** + * {@inheritDoc} + */ + public boolean isErrorEnabled() { + return logger.isEnabledFor(Level.ERROR); + } + + /** + * {@inheritDoc} + */ + public void error(final Object msg) { + logger.error(msg); + } + + + /** + * {@inheritDoc} + */ + public void error(final Object parameterizedMsg, final Object param1) { + if (logger.isEnabledFor(Level.ERROR)) { + logger.error(MessageFormatter.format( + parameterizedMsg.toString(), param1)); + } + } + + /** + * {@inheritDoc} + */ + public void error(final String parameterizedMsg, + final Object param1, + final Object param2) { + if (logger.isEnabledFor(Level.ERROR)) { + logger.error(MessageFormatter.format( + parameterizedMsg.toString(), param1, param2)); + } + } + + /** + * {@inheritDoc} + */ + public void error(final Object msg, final Throwable t) { + logger.error(msg, t); + } + +}
http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/LoggerEventListener.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/LoggerEventListener.java b/src/main/java/org/apache/log4j/spi/LoggerEventListener.java new file mode 100644 index 0000000..b39f728 --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/LoggerEventListener.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.spi; + +import org.apache.log4j.Appender; +import org.apache.log4j.Logger; + + +/** + Interface used to listen for Logger related events such as + add/remove appender or changing levels. Clients register an instance of + the interface and the instance is called back when the various events occur. + + LoggerRepository provides methods for adding and removing + LoggerEventListener instances. + + When implementing the methods of this interface, it is useful to remember + that the Logger can access the repository using its getRepository() + method. + + @author Ceki Gülcü + @author Mark Womack +*/ +public interface LoggerEventListener { + /** + Called when an appender is added to the logger. + + @param logger The logger to which the appender was added. + @param appender The appender added to the logger. */ + void appenderAddedEvent(Logger logger, Appender appender); + + /** + Called when an appender is removed from the logger. + + @param logger The logger from which the appender was removed. + @param appender The appender removed from the logger. */ + void appenderRemovedEvent(Logger logger, Appender appender); + + /** + Called when level changed on the logger. + + @param logger The logger that changed levels. */ + void levelChangedEvent(Logger logger); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/LoggerRepositoryEventListener.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/LoggerRepositoryEventListener.java b/src/main/java/org/apache/log4j/spi/LoggerRepositoryEventListener.java new file mode 100644 index 0000000..773a497 --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/LoggerRepositoryEventListener.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.spi; + + +/** + Interface used to listen for LoggerRepository related + events such as startup, reset, and shutdown. Clients register + an instance of the interface and the instance is called back + when the various events occur. + + LoggerRepository provides methods for adding and removing + LoggerRepositoryEventListener instances. + + @author Ceki Gülcü + @author Mark Womack +*/ +public interface LoggerRepositoryEventListener { + /** + Called when the repository configuration is reset. + @param repository repository + */ + void configurationResetEvent(LoggerRepository repository); + + /** + Called when the repository configuration is changed. + @param repository repository + */ + void configurationChangedEvent(LoggerRepository repository); + + /** + Called when the repository is shutdown. When this method is + invoked, the repository is still valid (ie it has not been + shutdown, but will be after this method returns). + @param repository repository. + */ + void shutdownEvent(LoggerRepository repository); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/LoggerRepositoryEx.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/LoggerRepositoryEx.java b/src/main/java/org/apache/log4j/spi/LoggerRepositoryEx.java new file mode 100644 index 0000000..c079a2c --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/LoggerRepositoryEx.java @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.spi; + +import org.apache.log4j.Appender; +import org.apache.log4j.Category; +import org.apache.log4j.Logger; +import org.apache.log4j.plugins.PluginRegistry; +import org.apache.log4j.scheduler.Scheduler; + +import java.util.List; +import java.util.Map; + + +/** + A <code>LoggerRepository</code> is used to create and retrieve + <code>Loggers</code>. The relation between loggers in a repository + depends on the repository but typically loggers are arranged in a + named hierarchy. + + <p>In addition to the creational methods, a + <code>LoggerRepository</code> can be queried for existing loggers, + can act as a point of registry for events related to loggers. + + @author Ceki Gülcü + @author Mark Womack + @author Curt Arnold + */ +public interface LoggerRepositoryEx extends LoggerRepository { + /** + Add a {@link LoggerRepositoryEventListener} to the repository. The + listener will be called when repository events occur. + @param listener event listener, may not be null. + */ + void addLoggerRepositoryEventListener( + LoggerRepositoryEventListener listener); + + /** + Remove a {@link LoggerRepositoryEventListener} from the repository. + @param listener listener. + */ + void removeLoggerRepositoryEventListener( + LoggerRepositoryEventListener listener); + + /** + Add a {@link LoggerEventListener} to the repository. The listener + will be called when repository events occur. + @param listener listener, may not be null. + */ + void addLoggerEventListener(LoggerEventListener listener); + + /** + Remove a {@link LoggerEventListener} from the repository. + @param listener listener, may not be null. + */ + void removeLoggerEventListener(LoggerEventListener listener); + + /** + * Get the name of this logger repository. + * @return name, may not be null. + */ + String getName(); + + /** + * A logger repository is a named entity. + * @param repoName new name, may not be null. + */ + void setName(String repoName); + + /** + * Is the current configuration of the repository in its original (pristine) + * state? + * @return true if repository is in original state. + * + */ + boolean isPristine(); + + /** + * Set the pristine flag. + * @param state state + * @see #isPristine + */ + void setPristine(boolean state); + + /** + Requests that a appender removed event be sent to any registered + {@link LoggerEventListener}. + @param logger The logger from which the appender was removed. + @param appender The appender removed from the logger. + */ + void fireRemoveAppenderEvent(Category logger, Appender appender); + + /** + Requests that a level changed event be sent to any registered + {@link LoggerEventListener}. + @param logger The logger which changed levels. + */ + void fireLevelChangedEvent(Logger logger); + + /** + Requests that a configuration changed event be sent to any registered + {@link LoggerRepositoryEventListener}. + */ + void fireConfigurationChangedEvent(); + + /** + * Return the PluginRegisty for this LoggerRepository. + * @return plug in registry. + */ + PluginRegistry getPluginRegistry(); + + /** + * Return the {@link Scheduler} for this LoggerRepository. + * @return scheduler. + */ + Scheduler getScheduler(); + + /** + * Get the properties specific for this repository. + * @return property map. + */ + Map getProperties(); + + /** + * Get the property of this repository. + * @param key property key. + * @return key value or null if not set. + */ + String getProperty(String key); + + /** + * Set a property of this repository. + * @param key key, may not be null. + * @param value new value, if null, property will be removed. + */ + void setProperty(String key, String value); + + /** + * Errors which cannot be logged, go to the error list. + * + * @return List + */ + List getErrorList(); + + /** + * Errors which cannot be logged, go to the error list. + * + * @param errorItem an ErrorItem to add to the error list + */ + void addErrorItem(ErrorItem errorItem); + + /** + * A LoggerRepository can also act as a store for various objects used + * by log4j components. + * + * @param key key, may not be null. + * @return The object stored under 'key'. + */ + Object getObject(String key); + + /** + * Store an object under 'key'. If no object can be found, null is returned. + * + * @param key key, may not be null. + * @param value value, may be null. + */ + void putObject(String key, Object value); + + /** + * Sets the logger factory used by LoggerRepository.getLogger(String). + * @param loggerFactory factory to use, may not be null + */ + void setLoggerFactory(LoggerFactory loggerFactory); + + /** + * Returns the logger factory used by + * LoggerRepository.getLogger(String). + * + * @return non-null factory + */ + LoggerFactory getLoggerFactory(); + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/NOPULogger.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/NOPULogger.java b/src/main/java/org/apache/log4j/spi/NOPULogger.java new file mode 100644 index 0000000..98fe537 --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/NOPULogger.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.spi; + +import org.apache.log4j.ULogger; + + +/** + * A no operation (NOP) implementation of {@link ULogger}. + * + * @author Ceki Gülcü + */ +public final class NOPULogger implements ULogger { + + /** + * The unique instance of NOPLogger. + */ + public static final NOPULogger NOP_LOGGER = new NOPULogger(); + + /** + * There is no point in people creating multiple instances of NullLogger. + * Hence, the private access modifier. + */ + private NOPULogger() { + super(); + } + + /** + * Get instance. + * @param name logger name. + * @return logger. + */ + public static NOPULogger getLogger(final String name) { + return NOP_LOGGER; + } + + /** + * {@inheritDoc} + */ + public boolean isDebugEnabled() { + return false; + } + + /** + * {@inheritDoc} + */ + public void debug(final Object msg) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void debug(final Object parameterizedMsg, final Object param1) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void debug(final String parameterizedMsg, + final Object param1, + final Object param2) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void debug(final Object msg, final Throwable t) { + // NOP + } + + /** + * {@inheritDoc} + */ + public boolean isInfoEnabled() { + // NOP + return false; + } + + /** + * {@inheritDoc} + */ + public void info(final Object msg) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void info(final Object parameterizedMsg, final Object param1) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void info(final String parameterizedMsg, + final Object param1, final Object param2) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void info(final Object msg, final Throwable t) { + // NOP + } + + /** + * {@inheritDoc} + */ + public boolean isWarnEnabled() { + return false; + } + + /** + * {@inheritDoc} + */ + public void warn(final Object msg) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void warn(final Object parameterizedMsg, + final Object param1) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void warn(final String parameterizedMsg, + final Object param1, + final Object param2) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void warn(final Object msg, final Throwable t) { + // NOP + } + + /** + * {@inheritDoc} + */ + public boolean isErrorEnabled() { + return false; + } + + /** + * {@inheritDoc} + */ + public void error(final Object msg) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void error(final Object parameterizedMsg, final Object param1) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void error(final String parameterizedMsg, + final Object param1, + final Object param2) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void error(final Object msg, final Throwable t) { + // NOP + } + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/SimpleULogger.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/SimpleULogger.java b/src/main/java/org/apache/log4j/spi/SimpleULogger.java new file mode 100644 index 0000000..714a57a --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/SimpleULogger.java @@ -0,0 +1,305 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.spi; + +import org.apache.log4j.ULogger; +import org.apache.log4j.helpers.MessageFormatter; + + +/** + * A simple implementation that logs messages of level INFO or higher on + * the console (<code>System.out</code>). + * <p> + * The output includes the relative time in milliseconds, thread name, level, + * logger name, and the message followed by the line separator for the host. + * In log4j terms it amounts to the "%r [%t] %level %logger - %m%n" pattern. + * <pre> +176 [main] INFO examples.Sort - Populating an array of 2 elements in reverse. +225 [main] INFO examples.SortAlgo - Entered the sort method. +304 [main] INFO SortAlgo.DUMP - Dump of interger array: +317 [main] INFO SortAlgo.DUMP - Element [0] = 0 +331 [main] INFO SortAlgo.DUMP - Element [1] = 1 +343 [main] INFO examples.Sort - The next log statement should be an error msg. +346 [main] ERROR SortAlgo.DUMP - Tried to dump an uninitialized array. + at org.log4j.examples.SortAlgo.dump(SortAlgo.java:58) + at org.log4j.examples.Sort.main(Sort.java:64) +467 [main] INFO examples.Sort - Exiting main method. +</pre> + * + * @author Ceki Gülcü + */ +public final class SimpleULogger implements ULogger { + + /** + * Logger name. + */ + private final String loggerName; + + + /** + * Mark the time when this class gets loaded into memory. + */ + private static long startTime = System.currentTimeMillis(); + + /** + * Line separator. + */ + public static final String LINE_SEPARATOR + = System.getProperty("line.separator"); + + /** + * INFO string literal. + */ + private static final String INFO_STR = "INFO"; + /** + * WARN string literal. + */ + private static final String WARN_STR = "WARN"; + /** + * ERROR string literal. + */ + private static final String ERROR_STR = "ERROR"; + + /** + * Constructor is private to force construction through getLogger. + * @param name logger name + */ + private SimpleULogger(final String name) { + super(); + this.loggerName = name; + } + + /** + * Creates a new instance. + * + * @param name logger name + * @return logger. + */ + public static SimpleULogger getLogger(final String name) { + return new SimpleULogger(name); + } + + /** + * {@inheritDoc} + */ + public boolean isDebugEnabled() { + return false; + } + + /** + * {@inheritDoc} + */ + public void debug(final Object msg) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void debug(final Object parameterizedMsg, final Object param1) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void debug(final String parameterizedMsg, + final Object param1, + final Object param2) { + // NOP + } + + /** + * {@inheritDoc} + */ + public void debug(final Object msg, final Throwable t) { + // NOP + } + + /** + * This is our internal implementation for logging regular (non-parameterized) + * log messages. + * + * @param level level + * @param message message + * @param t throwable + */ + private void log(final String level, + final String message, + final Throwable t) { + StringBuffer buf = new StringBuffer(); + + long millis = System.currentTimeMillis(); + buf.append(millis - startTime); + + buf.append(" ["); + buf.append(Thread.currentThread().getName()); + buf.append("] "); + + buf.append(level); + buf.append(" "); + + buf.append(loggerName); + buf.append(" - "); + + buf.append(message); + + buf.append(LINE_SEPARATOR); + + System.out.print(buf.toString()); + if (t != null) { + t.printStackTrace(System.out); + } + System.out.flush(); + } + /** + * For parameterized messages, first substitute parameters and then log. + * + * @param level level + * @param parameterizedMsg message pattern + * @param param1 param1 + * @param param2 param2 + */ + private void parameterizedLog(final String level, + final Object parameterizedMsg, + final Object param1, + final Object param2) { + if (parameterizedMsg instanceof String) { + String msgStr = (String) parameterizedMsg; + msgStr = MessageFormatter.format(msgStr, param1, param2); + log(level, msgStr, null); + } else { + // To be failsafe, we handle the case where 'messagePattern' is not + // a String. Unless the user makes a mistake, this should not happen. + log(level, parameterizedMsg.toString(), null); + } + } + + /** + * {@inheritDoc} + */ + public boolean isInfoEnabled() { + return true; + } + + /** + * {@inheritDoc} + */ + public void info(final Object msg) { + log(INFO_STR, msg.toString(), null); + } + + + /** + * {@inheritDoc} + */ + public void info(final Object parameterizedMsg, final Object param1) { + parameterizedLog(INFO_STR, parameterizedMsg, param1, null); + } + + /** + * {@inheritDoc} + */ + public void info(final String parameterizedMsg, + final Object param1, + final Object param2) { + parameterizedLog(INFO_STR, parameterizedMsg, param1, param2); + } + + /** + * {@inheritDoc} + */ + public void info(final Object msg, final Throwable t) { + log(INFO_STR, msg.toString(), t); + } + + /** + * {@inheritDoc} + */ + public boolean isWarnEnabled() { + return true; + } + + /** + * {@inheritDoc} + */ + public void warn(final Object msg) { + log(WARN_STR, msg.toString(), null); + } + + /** + * {@inheritDoc} + */ + public void warn(final Object parameterizedMsg, final Object param1) { + parameterizedLog(WARN_STR, parameterizedMsg, param1, null); + } + + /** + * {@inheritDoc} + */ + public void warn(final String parameterizedMsg, + final Object param1, + final Object param2) { + parameterizedLog(WARN_STR, parameterizedMsg, param1, param2); + } + + /** + * {@inheritDoc} + */ + public void warn(final Object msg, final Throwable t) { + log(WARN_STR, msg.toString(), t); + } + + /** + * {@inheritDoc} + */ + public boolean isErrorEnabled() { + return true; + } + + /** + * {@inheritDoc} + */ + public void error(final Object msg) { + log(ERROR_STR, msg.toString(), null); + } + + + /** + * {@inheritDoc} + */ + public void error(final Object parameterizedMsg, final Object param1) { + parameterizedLog(ERROR_STR, parameterizedMsg, param1, null); + } + + /** + * {@inheritDoc} + */ + public void error(final String parameterizedMsg, + final Object param1, + final Object param2) { + parameterizedLog(ERROR_STR, parameterizedMsg, param1, param2); + } + + /** + * {@inheritDoc} + */ + public void error(final Object msg, final Throwable t) { + log(ERROR_STR, msg.toString(), t); + } + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/Thresholdable.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/spi/Thresholdable.java b/src/main/java/org/apache/log4j/spi/Thresholdable.java new file mode 100644 index 0000000..222345f --- /dev/null +++ b/src/main/java/org/apache/log4j/spi/Thresholdable.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.spi; + +import org.apache.log4j.Level; + +/** + * An interface that defines the required methods for supporting the + * setting and getting of a level threshold. Components should implement + * this interface if logging events they process should meet a certain + * threshold before being processed further. Examples of this are + * Appenders and Receivers which will not process logging events unless + * the event level is at or greater than a set threshold level. + * + * @author Paul Smith ([email protected]) + * @author Mark Womack + */ +public interface Thresholdable { + /** + * Sets the component theshold to the given level. + * + * @param level The threshold level events must equal or be greater + * than before further processing can be done. + */ + void setThreshold(Level level); + + /** + * Gets the current threshold setting of the component. + * + * @return Level The current threshold level of the component. + */ + Level getThreshold(); + + /** + * Returns true if the given level is equals or greater than the current + * threshold value of the component. + * + * @param level The level to test against the component threshold. + * @return boolean True if level is equal or greater than the + * component threshold. + */ + boolean isAsSevereAsThreshold(Level level); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/varia/ListModelAppender.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/varia/ListModelAppender.java b/src/main/java/org/apache/log4j/varia/ListModelAppender.java new file mode 100644 index 0000000..ccbc9be --- /dev/null +++ b/src/main/java/org/apache/log4j/varia/ListModelAppender.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.varia; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.spi.LoggingEvent; + +import javax.swing.DefaultListModel; +import javax.swing.ListModel; + + +/** + * A very basic appender that takes the events and stores them in to a + * ListModel for late retrieval. + * + * + * @author Paul Smith ([email protected]) + * + */ +public final class ListModelAppender extends AppenderSkeleton { + /** + * Default list model. + */ + private final DefaultListModel model = new DefaultListModel(); + + /** + * Constructs a ListModelAppender. + */ + public ListModelAppender() { + super(true); + } + /** + * Returns a reference to the ListModel that contains all the LoggingEvents + * that have been appended to this class. + * + * @return the list model + */ + public ListModel getModel() { + return model; + } + + /** {@inheritDoc} */ + protected void append(final LoggingEvent event) { + model.addElement(event); + } + + /** {@inheritDoc} */ + public void close() { + clearModel(); + } + + /** + * Removes all the Events from the model. + */ + public void clearModel() { + model.clear(); + } + + /** {@inheritDoc} */ + public boolean requiresLayout() { + return false; + } + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java b/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java new file mode 100644 index 0000000..4430205 --- /dev/null +++ b/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java @@ -0,0 +1,1043 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.varia; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.helpers.Constants; +import org.apache.log4j.plugins.Receiver; +import org.apache.log4j.rule.ExpressionRule; +import org.apache.log4j.rule.Rule; +import org.apache.log4j.spi.LocationInfo; +import org.apache.log4j.spi.LoggingEvent; +import org.apache.log4j.spi.ThrowableInformation; + +/** + * LogFilePatternReceiver can parse and tail log files, converting entries into + * LoggingEvents. If the file doesn't exist when the receiver is initialized, the + * receiver will look for the file once every 10 seconds. + * <p> + * This receiver relies on java.util.regex features to perform the parsing of text in the + * log file, however the only regular expression field explicitly supported is + * a glob-style wildcard used to ignore fields in the log file if needed. All other + * fields are parsed by using the supplied keywords. + * <p> + * <b>Features:</b><br> + * - specify the URL of the log file to be processed<br> + * - specify the timestamp format in the file (if one exists, using patterns from {@link java.text.SimpleDateFormat})<br> + * - specify the pattern (logFormat) used in the log file using keywords, a wildcard character (*) and fixed text<br> + * - 'tail' the file (allows the contents of the file to be continually read and new events processed)<br> + * - supports the parsing of multi-line messages and exceptions + * - 'hostname' property set to URL host (or 'file' if not available) + * - 'application' property set to URL path (or value of fileURL if not available) + *<p> + * <b>Keywords:</b><br> + * TIMESTAMP<br> + * LOGGER<br> + * LEVEL<br> + * THREAD<br> + * CLASS<br> + * FILE<br> + * LINE<br> + * METHOD<br> + * RELATIVETIME<br> + * MESSAGE<br> + * NDC<br> + * PROP(key)<br> + * <p> + * Use a * to ignore portions of the log format that should be ignored + * <p> + * Example:<br> + * If your file's patternlayout is this:<br> + * <b>%d %-5p [%t] %C{2} (%F:%L) - %m%n</b> + *<p> + * specify this as the log format:<br> + * <b>TIMESTAMP LEVEL [THREAD] CLASS (FILE:LINE) - MESSAGE</b> + *<p> + * To define a PROPERTY field, use PROP(key) + * <p> + * Example:<br> + * If you used the RELATIVETIME pattern layout character in the file, + * you can use PROP(RELATIVETIME) in the logFormat definition to assign + * the RELATIVETIME field as a property on the event. + * <p> + * If your file's patternlayout is this:<br> + * <b>%r [%t] %-5p %c %x - %m%n</b> + *<p> + * specify this as the log format:<br> + * <b>PROP(RELATIVETIME) [THREAD] LEVEL LOGGER * - MESSAGE</b> + * <p> + * Note the * - it can be used to ignore a single word or sequence of words in the log file + * (in order for the wildcard to ignore a sequence of words, the text being ignored must be + * followed by some delimiter, like '-' or '[') - ndc is being ignored in the following example. + * <p> + * Assign a filterExpression in order to only process events which match a filter. + * If a filterExpression is not assigned, all events are processed. + *<p> + * <b>Limitations:</b><br> + * - no support for the single-line version of throwable supported by patternlayout<br> + * (this version of throwable will be included as the last line of the message)<br> + * - the relativetime patternLayout character must be set as a property: PROP(RELATIVETIME)<br> + * - messages should appear as the last field of the logFormat because the variability in message content<br> + * - exceptions are converted if the exception stack trace (other than the first line of the exception)<br> + * is stored in the log file with a tab followed by the word 'at' as the first characters in the line<br> + * - tailing may fail if the file rolls over. + *<p> + * <b>Example receiver configuration settings</b> (add these as params, specifying a LogFilePatternReceiver 'plugin'):<br> + * param: "timestampFormat" value="yyyy-MM-d HH:mm:ss,SSS"<br> + * param: "logFormat" value="PROP(RELATIVETIME) [THREAD] LEVEL LOGGER * - MESSAGE"<br> + * param: "fileURL" value="file:///c:/events.log"<br> + * param: "tailing" value="true" + *<p> + * This configuration will be able to process these sample events:<br> + * 710 [ Thread-0] DEBUG first.logger first - <test> <test2>something here</test2> <test3 blah=something/> <test4> <test5>something else</test5> </test4></test><br> + * 880 [ Thread-2] DEBUG first.logger third - <test> <test2>something here</test2> <test3 blah=something/> <test4> <test5>something else</test5> </test4></test><br> + * 880 [ Thread-0] INFO first.logger first - infomsg-0<br> + * java.lang.Exception: someexception-first<br> + * at Generator2.run(Generator2.java:102)<br> + * + *@author Scott Deboy + */ +public class LogFilePatternReceiver extends Receiver { + private final List keywords = new ArrayList(); + + private static final String PROP_START = "PROP("; + private static final String PROP_END = ")"; + + private static final String LOGGER = "LOGGER"; + private static final String MESSAGE = "MESSAGE"; + private static final String TIMESTAMP = "TIMESTAMP"; + private static final String NDC = "NDC"; + private static final String LEVEL = "LEVEL"; + private static final String THREAD = "THREAD"; + private static final String CLASS = "CLASS"; + private static final String FILE = "FILE"; + private static final String LINE = "LINE"; + private static final String METHOD = "METHOD"; + + private static final String DEFAULT_HOST = "file"; + + //all lines other than first line of exception begin with tab followed by 'at' followed by text + private static final String EXCEPTION_PATTERN = "^\\s+at.*"; + private static final String REGEXP_DEFAULT_WILDCARD = ".*?"; + private static final String REGEXP_GREEDY_WILDCARD = ".*"; + private static final String PATTERN_WILDCARD = "*"; + private static final String NOSPACE_GROUP = "(\\S*\\s*?)"; + private static final String DEFAULT_GROUP = "(" + REGEXP_DEFAULT_WILDCARD + ")"; + private static final String GREEDY_GROUP = "(" + REGEXP_GREEDY_WILDCARD + ")"; + private static final String MULTIPLE_SPACES_REGEXP = "[ ]+"; + private final String newLine = System.getProperty("line.separator"); + + private final String[] emptyException = new String[] { "" }; + + private SimpleDateFormat dateFormat; + private String timestampFormat = "yyyy-MM-d HH:mm:ss,SSS"; + private String logFormat; + private String customLevelDefinitions; + private String fileURL; + private String host; + private String path; + private boolean tailing; + private String filterExpression; + private long waitMillis = 2000; //default 2 seconds + + private static final String VALID_DATEFORMAT_CHARS = "GyMwWDdFEaHkKhmsSzZ"; + private static final String VALID_DATEFORMAT_CHAR_PATTERN = "[" + VALID_DATEFORMAT_CHARS + "]"; + + private Rule expressionRule; + + private Map currentMap; + private List additionalLines; + private List matchingKeywords; + + private String regexp; + private Reader reader; + private Pattern regexpPattern; + private Pattern exceptionPattern; + private String timestampPatternText; + + private boolean useCurrentThread; + public static final int MISSING_FILE_RETRY_MILLIS = 10000; + private boolean appendNonMatches; + private final Map customLevelDefinitionMap = new HashMap(); + + public LogFilePatternReceiver() { + keywords.add(TIMESTAMP); + keywords.add(LOGGER); + keywords.add(LEVEL); + keywords.add(THREAD); + keywords.add(CLASS); + keywords.add(FILE); + keywords.add(LINE); + keywords.add(METHOD); + keywords.add(MESSAGE); + keywords.add(NDC); + try { + exceptionPattern = Pattern.compile(EXCEPTION_PATTERN); + } catch (PatternSyntaxException pse) { + //shouldn't happen + } + } + + /** + * Accessor + * + * @return file URL + */ + public String getFileURL() { + return fileURL; + } + + /** + * Mutator + * + * @param fileURL + */ + public void setFileURL(String fileURL) { + this.fileURL = fileURL; + } + + /** + * If the log file contains non-log4j level strings, they can be mapped to log4j levels using the format (android example): + * V=TRACE,D=DEBUG,I=INFO,W=WARN,E=ERROR,F=FATAL,S=OFF + * + * @param customLevelDefinitions the level definition string + */ + public void setCustomLevelDefinitions(String customLevelDefinitions) { + this.customLevelDefinitions = customLevelDefinitions; + } + + public String getCustomLevelDefinitions() { + return customLevelDefinitions; + } + + /** + * Accessor + * @return append non matches + */ + public boolean isAppendNonMatches() { + return appendNonMatches; + } + + /** + * Mutator + * @param appendNonMatches + */ + public void setAppendNonMatches(boolean appendNonMatches) { + this.appendNonMatches = appendNonMatches; + } + + /** + * Accessor + * + * @return filter expression + */ + public String getFilterExpression() { + return filterExpression; + } + + /** + * Mutator + * + * @param filterExpression + */ + public void setFilterExpression(String filterExpression) { + this.filterExpression = filterExpression; + } + + /** + * Accessor + * + * @return tailing + */ + public boolean isTailing() { + return tailing; + } + + /** + * Mutator + * + * @param tailing + */ + public void setTailing(boolean tailing) { + this.tailing = tailing; + } + + /** + * When true, this property uses the current Thread to perform the import, + * otherwise when false (the default), a new Thread is created and started to manage + * the import. + * @return + */ + public final boolean isUseCurrentThread() { + return useCurrentThread; + } + + /** + * Sets whether the current Thread or a new Thread is created to perform the import, + * the default being false (new Thread created). + * + * @param useCurrentThread + */ + public final void setUseCurrentThread(boolean useCurrentThread) { + this.useCurrentThread = useCurrentThread; + } + + /** + * Accessor + * + * @return log format + */ + public String getLogFormat() { + return logFormat; + } + + /** + * Mutator + * + * @param logFormat + * the format + */ + public void setLogFormat(String logFormat) { + this.logFormat = logFormat; + } + + /** + * Mutator. Specify a pattern from {@link java.text.SimpleDateFormat} + * + * @param timestampFormat + */ + public void setTimestampFormat(String timestampFormat) { + this.timestampFormat = timestampFormat; + } + + /** + * Accessor + * + * @return timestamp format + */ + public String getTimestampFormat() { + return timestampFormat; + } + + /** + * Accessor + * @return millis between retrieves of content + */ + public long getWaitMillis() { + return waitMillis; + } + + /** + * Mutator + * @param waitMillis + */ + public void setWaitMillis(long waitMillis) { + this.waitMillis = waitMillis; + } + + /** + * Walk the additionalLines list, looking for the EXCEPTION_PATTERN. + * <p> + * Return the index of the first matched line + * (the match may be the 1st line of an exception) + * <p> + * Assumptions: <br> + * - the additionalLines list may contain both message and exception lines<br> + * - message lines are added to the additionalLines list and then + * exception lines (all message lines occur in the list prior to all + * exception lines) + * + * @return -1 if no exception line exists, line number otherwise + */ + private int getExceptionLine() { + for (int i = 0; i < additionalLines.size(); i++) { + Matcher exceptionMatcher = exceptionPattern.matcher((String)additionalLines.get(i)); + if (exceptionMatcher.matches()) { + return i; + } + } + return -1; + } + + /** + * Combine all message lines occuring in the additionalLines list, adding + * a newline character between each line + * <p> + * the event will already have a message - combine this message + * with the message lines in the additionalLines list + * (all entries prior to the exceptionLine index) + * + * @param firstMessageLine primary message line + * @param exceptionLine index of first exception line + * @return message + */ + private String buildMessage(String firstMessageLine, int exceptionLine) { + if (additionalLines.size() == 0) { + return firstMessageLine; + } + StringBuffer message = new StringBuffer(); + if (firstMessageLine != null) { + message.append(firstMessageLine); + } + + int linesToProcess = (exceptionLine == -1?additionalLines.size(): exceptionLine); + + for (int i = 0; i < linesToProcess; i++) { + message.append(newLine); + message.append(additionalLines.get(i)); + } + return message.toString(); + } + + /** + * Combine all exception lines occuring in the additionalLines list into a + * String array + * <p> + * (all entries equal to or greater than the exceptionLine index) + * + * @param exceptionLine index of first exception line + * @return exception + */ + private String[] buildException(int exceptionLine) { + if (exceptionLine == -1) { + return emptyException; + } + String[] exception = new String[additionalLines.size() - exceptionLine - 1]; + for (int i = 0; i < exception.length; i++) { + exception[i] = (String) additionalLines.get(i + exceptionLine); + } + return exception; + } + + /** + * Construct a logging event from currentMap and additionalLines + * (additionalLines contains multiple message lines and any exception lines) + * <p> + * CurrentMap and additionalLines are cleared in the process + * + * @return event + */ + private LoggingEvent buildEvent() { + if (currentMap.size() == 0) { + if (additionalLines.size() > 0) { + for (Iterator iter = additionalLines.iterator();iter.hasNext();) { + getLogger().info("found non-matching line: " + iter.next()); + } + } + additionalLines.clear(); + return null; + } + //the current map contains fields - build an event + int exceptionLine = getExceptionLine(); + String[] exception = buildException(exceptionLine); + + //messages are listed before exceptions in additionallines + if (additionalLines.size() > 0 && exception.length > 0) { + currentMap.put(MESSAGE, buildMessage((String) currentMap.get(MESSAGE), + exceptionLine)); + } + LoggingEvent event = convertToEvent(currentMap, exception); + currentMap.clear(); + additionalLines.clear(); + return event; + } + + /** + * Read, parse and optionally tail the log file, converting entries into logging events. + * + * A runtimeException is thrown if the logFormat pattern is malformed. + * + * @param bufferedReader + * @throws IOException + */ + protected void process(BufferedReader bufferedReader) throws IOException { + Matcher eventMatcher; + Matcher exceptionMatcher; + String line; + while ((line = bufferedReader.readLine()) != null) { + //skip empty line entries + eventMatcher = regexpPattern.matcher(line); + if (line.trim().equals("")) {continue;} + exceptionMatcher = exceptionPattern.matcher(line); + if (eventMatcher.matches()) { + //build an event from the previous match (held in current map) + LoggingEvent event = buildEvent(); + if (event != null) { + if (passesExpression(event)) { + doPost(event); + } + } + currentMap.putAll(processEvent(eventMatcher.toMatchResult())); + } else if (exceptionMatcher.matches()) { + //an exception line + additionalLines.add(line); + } else { + //neither...either post an event with the line or append as additional lines + //if this was a logging event with multiple lines, each line will show up as its own event instead of being + //appended as multiple lines on the same event.. + //choice is to have each non-matching line show up as its own line, or append them all to a previous event + if (appendNonMatches) { + //hold on to the previous time, so we can do our best to preserve time-based ordering if the event is a non-match + String lastTime = (String)currentMap.get(TIMESTAMP); + //build an event from the previous match (held in current map) + if (currentMap.size() > 0) { + LoggingEvent event = buildEvent(); + if (event != null) { + if (passesExpression(event)) { + doPost(event); + } + } + } + if (lastTime != null) { + currentMap.put(TIMESTAMP, lastTime); + } + currentMap.put(MESSAGE, line); + } else { + additionalLines.add(line); + } + } + } + + //process last event if one exists + LoggingEvent event = buildEvent(); + if (event != null) { + if (passesExpression(event)) { + doPost(event); + } + } + } + + protected void createPattern() { + regexpPattern = Pattern.compile(regexp); + } + + /** + * Helper method that supports the evaluation of the expression + * + * @param event + * @return true if expression isn't set, or the result of the evaluation otherwise + */ + private boolean passesExpression(LoggingEvent event) { + if (event != null) { + if (expressionRule != null) { + return (expressionRule.evaluate(event, null)); + } + } + return true; + } + + /** + * Convert the match into a map. + * <p> + * Relies on the fact that the matchingKeywords list is in the same + * order as the groups in the regular expression + * + * @param result + * @return map + */ + private Map processEvent(MatchResult result) { + Map map = new HashMap(); + //group zero is the entire match - process all other groups + for (int i = 1; i < result.groupCount() + 1; i++) { + Object key = matchingKeywords.get(i - 1); + Object value = result.group(i); + map.put(key, value); + + } + return map; + } + + /** + * Helper method that will convert timestamp format to a pattern + * + * + * @return string + */ + private String convertTimestamp() { + //some locales (for example, French) generate timestamp text with characters not included in \w - + // now using \S (all non-whitespace characters) instead of /w + String result = timestampFormat.replaceAll(VALID_DATEFORMAT_CHAR_PATTERN + "+", "\\\\S+"); + //make sure dots in timestamp are escaped + result = result.replaceAll(Pattern.quote("."), "\\\\."); + return result; + } + + protected void setHost(String host) { + this.host = host; + } + + protected void setPath(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + + /** + * Build the regular expression needed to parse log entries + * + */ + protected void initialize() { + if (host == null && path == null) { + try { + URL url = new URL(fileURL); + host = url.getHost(); + path = url.getPath(); + } catch (MalformedURLException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + if (host == null || host.trim().equals("")) { + host = DEFAULT_HOST; + } + if (path == null || path.trim().equals("")) { + path = fileURL; + } + + currentMap = new HashMap(); + additionalLines = new ArrayList(); + matchingKeywords = new ArrayList(); + + if (timestampFormat != null) { + dateFormat = new SimpleDateFormat(quoteTimeStampChars(timestampFormat)); + timestampPatternText = convertTimestamp(); + } + //if custom level definitions exist, parse them + updateCustomLevelDefinitionMap(); + try { + if (filterExpression != null) { + expressionRule = ExpressionRule.getRule(filterExpression); + } + } catch (Exception e) { + getLogger().warn("Invalid filter expression: " + filterExpression, e); + } + + List buildingKeywords = new ArrayList(); + + String newPattern = logFormat; + + int index = 0; + String current = newPattern; + //build a list of property names and temporarily replace the property with an empty string, + //we'll rebuild the pattern later + List propertyNames = new ArrayList(); + while (index > -1) { + if (current.indexOf(PROP_START) > -1 && current.indexOf(PROP_END) > -1) { + index = current.indexOf(PROP_START); + String longPropertyName = current.substring(current.indexOf(PROP_START), current.indexOf(PROP_END) + 1); + String shortProp = getShortPropertyName(longPropertyName); + buildingKeywords.add(shortProp); + propertyNames.add(longPropertyName); + current = current.substring(longPropertyName.length() + 1 + index); + newPattern = singleReplace(newPattern, longPropertyName, new Integer(buildingKeywords.size() -1).toString()); + } else { + //no properties + index = -1; + } + } + + /* + * we're using a treemap, so the index will be used as the key to ensure + * keywords are ordered correctly + * + * examine pattern, adding keywords to an index-based map patterns can + * contain only one of these per entry...properties are the only 'keyword' + * that can occur multiple times in an entry + */ + Iterator iter = keywords.iterator(); + while (iter.hasNext()) { + String keyword = (String) iter.next(); + int index2 = newPattern.indexOf(keyword); + if (index2 > -1) { + buildingKeywords.add(keyword); + newPattern = singleReplace(newPattern, keyword, new Integer(buildingKeywords.size() -1).toString()); + } + } + + String buildingInt = ""; + + for (int i=0;i<newPattern.length();i++) { + String thisValue = String.valueOf(newPattern.substring(i, i+1)); + if (isInteger(thisValue)) { + buildingInt = buildingInt + thisValue; + } else { + if (isInteger(buildingInt)) { + matchingKeywords.add(buildingKeywords.get(Integer.parseInt(buildingInt))); + } + //reset + buildingInt = ""; + } + } + + //if the very last value is an int, make sure to add it + if (isInteger(buildingInt)) { + matchingKeywords.add(buildingKeywords.get(Integer.parseInt(buildingInt))); + } + + newPattern = replaceMetaChars(newPattern); + + //compress one or more spaces in the pattern into the [ ]+ regexp + //(supports padding of level in log files) + newPattern = newPattern.replaceAll(MULTIPLE_SPACES_REGEXP, MULTIPLE_SPACES_REGEXP); + newPattern = newPattern.replaceAll(Pattern.quote(PATTERN_WILDCARD), REGEXP_DEFAULT_WILDCARD); + //use buildingKeywords here to ensure correct order + for (int i = 0;i<buildingKeywords.size();i++) { + String keyword = (String) buildingKeywords.get(i); + //make the final keyword greedy (we're assuming it's the message) + if (i == (buildingKeywords.size() - 1)) { + newPattern = singleReplace(newPattern, String.valueOf(i), GREEDY_GROUP); + } else if (TIMESTAMP.equals(keyword)) { + newPattern = singleReplace(newPattern, String.valueOf(i), "(" + timestampPatternText + ")"); + } else if (LOGGER.equals(keyword) || LEVEL.equals(keyword)) { + newPattern = singleReplace(newPattern, String.valueOf(i), NOSPACE_GROUP); + } else { + newPattern = singleReplace(newPattern, String.valueOf(i), DEFAULT_GROUP); + } + } + + regexp = newPattern; + getLogger().debug("regexp is " + regexp); + } + + private void updateCustomLevelDefinitionMap() { + if (customLevelDefinitions != null) { + StringTokenizer entryTokenizer = new StringTokenizer(customLevelDefinitions, ","); + + customLevelDefinitionMap.clear(); + while (entryTokenizer.hasMoreTokens()) { + StringTokenizer innerTokenizer = new StringTokenizer(entryTokenizer.nextToken(), "="); + customLevelDefinitionMap.put(innerTokenizer.nextToken(), Level.toLevel(innerTokenizer.nextToken())); + } + } + } + + private boolean isInteger(String value) { + try { + Integer.parseInt(value); + return true; + } catch (NumberFormatException nfe) { + return false; + } + } + + private String quoteTimeStampChars(String input) { + //put single quotes around text that isn't a supported dateformat char + StringBuffer result = new StringBuffer(); + //ok to default to false because we also check for index zero below + boolean lastCharIsDateFormat = false; + for (int i = 0;i<input.length();i++) { + String thisVal = input.substring(i, i + 1); + boolean thisCharIsDateFormat = VALID_DATEFORMAT_CHARS.contains(thisVal); + //we have encountered a non-dateformat char + if (!thisCharIsDateFormat && (i == 0 || lastCharIsDateFormat)) { + result.append("'"); + } + //we have encountered a dateformat char after previously encountering a non-dateformat char + if (thisCharIsDateFormat && i > 0 && !lastCharIsDateFormat) { + result.append("'"); + } + lastCharIsDateFormat = thisCharIsDateFormat; + result.append(thisVal); + } + //append an end single-quote if we ended with non-dateformat char + if (!lastCharIsDateFormat) { + result.append("'"); + } + return result.toString(); + } + + private String singleReplace(String inputString, String oldString, String newString) + { + int propLength = oldString.length(); + int startPos = inputString.indexOf(oldString); + if (startPos == -1) + { + getLogger().info("string: " + oldString + " not found in input: " + inputString + " - returning input"); + return inputString; + } + if (startPos == 0) + { + inputString = inputString.substring(propLength); + inputString = newString + inputString; + } else { + inputString = inputString.substring(0, startPos) + newString + inputString.substring(startPos + propLength); + } + return inputString; + } + + private String getShortPropertyName(String longPropertyName) + { + String currentProp = longPropertyName.substring(longPropertyName.indexOf(PROP_START)); + String prop = currentProp.substring(0, currentProp.indexOf(PROP_END) + 1); + String shortProp = prop.substring(PROP_START.length(), prop.length() - 1); + return shortProp; + } + + /** + * Some perl5 characters may occur in the log file format. + * Escape these characters to prevent parsing errors. + * + * @param input + * @return string + */ + private String replaceMetaChars(String input) { + //escape backslash first since that character is used to escape the remaining meta chars + input = input.replaceAll("\\\\", "\\\\\\"); + + //don't escape star - it's used as the wildcard + input = input.replaceAll(Pattern.quote("]"), "\\\\]"); + input = input.replaceAll(Pattern.quote("["), "\\\\["); + input = input.replaceAll(Pattern.quote("^"), "\\\\^"); + input = input.replaceAll(Pattern.quote("$"), "\\\\$"); + input = input.replaceAll(Pattern.quote("."), "\\\\."); + input = input.replaceAll(Pattern.quote("|"), "\\\\|"); + input = input.replaceAll(Pattern.quote("?"), "\\\\?"); + input = input.replaceAll(Pattern.quote("+"), "\\\\+"); + input = input.replaceAll(Pattern.quote("("), "\\\\("); + input = input.replaceAll(Pattern.quote(")"), "\\\\)"); + input = input.replaceAll(Pattern.quote("-"), "\\\\-"); + input = input.replaceAll(Pattern.quote("{"), "\\\\{"); + input = input.replaceAll(Pattern.quote("}"), "\\\\}"); + input = input.replaceAll(Pattern.quote("#"), "\\\\#"); + return input; + } + + /** + * Convert a keyword-to-values map to a LoggingEvent + * + * @param fieldMap + * @param exception + * + * @return logging event + */ + private LoggingEvent convertToEvent(Map fieldMap, String[] exception) { + if (fieldMap == null) { + return null; + } + + //a logger must exist at a minimum for the event to be processed + if (!fieldMap.containsKey(LOGGER)) { + fieldMap.put(LOGGER, "Unknown"); + } + if (exception == null) { + exception = emptyException; + } + + Logger logger = null; + long timeStamp = 0L; + String level = null; + String threadName = null; + Object message = null; + String ndc = null; + String className = null; + String methodName = null; + String eventFileName = null; + String lineNumber = null; + Hashtable properties = new Hashtable(); + + logger = Logger.getLogger((String) fieldMap.remove(LOGGER)); + + if ((dateFormat != null) && fieldMap.containsKey(TIMESTAMP)) { + try { + timeStamp = dateFormat.parse((String) fieldMap.remove(TIMESTAMP)) + .getTime(); + } catch (Exception e) { + e.printStackTrace(); + } + } + //use current time if timestamp not parseable + if (timeStamp == 0L) { + timeStamp = System.currentTimeMillis(); + } + + message = fieldMap.remove(MESSAGE); + if (message == null) { + message = ""; + } + + level = (String) fieldMap.remove(LEVEL); + Level levelImpl; + if (level == null) { + levelImpl = Level.DEBUG; + } else { + //first try to resolve against custom level definition map, then fall back to regular levels + levelImpl = (Level) customLevelDefinitionMap.get(level); + if (levelImpl == null) { + levelImpl = Level.toLevel(level.trim()); + if (!level.equals(levelImpl.toString())) { + //check custom level map + if (levelImpl == null) { + levelImpl = Level.DEBUG; + getLogger().debug("found unexpected level: " + level + ", logger: " + logger.getName() + ", msg: " + message); + //make sure the text that couldn't match a level is added to the message + message = level + " " + message; + } + } + } + } + + threadName = (String) fieldMap.remove(THREAD); + + ndc = (String) fieldMap.remove(NDC); + + className = (String) fieldMap.remove(CLASS); + + methodName = (String) fieldMap.remove(METHOD); + + eventFileName = (String) fieldMap.remove(FILE); + + lineNumber = (String) fieldMap.remove(LINE); + + properties.put(Constants.HOSTNAME_KEY, host); + properties.put(Constants.APPLICATION_KEY, path); + properties.put(Constants.RECEIVER_NAME_KEY, getName()); + + //all remaining entries in fieldmap are properties + properties.putAll(fieldMap); + + LocationInfo info = null; + + if ((eventFileName != null) || (className != null) || (methodName != null) + || (lineNumber != null)) { + info = new LocationInfo(eventFileName, className, methodName, lineNumber); + } else { + info = LocationInfo.NA_LOCATION_INFO; + } + + LoggingEvent event = new LoggingEvent(null, + logger, timeStamp, levelImpl, message, + threadName, + new ThrowableInformation(exception), + ndc, + info, + properties); + + return event; + } + +// public static void main(String[] args) { +// org.apache.log4j.Logger rootLogger = org.apache.log4j.Logger.getRootLogger(); +// org.apache.log4j.ConsoleAppender appender = new org.apache.log4j.ConsoleAppender(new org.apache.log4j.SimpleLayout()); +// appender.setName("console"); +// rootLogger.addAppender(appender); +// LogFilePatternReceiver test = new LogFilePatternReceiver(); +// org.apache.log4j.spi.LoggerRepository repo = new org.apache.log4j.LoggerRepositoryExImpl(org.apache.log4j.LogManager.getLoggerRepository()); +// test.setLoggerRepository(repo); +// test.setLogFormat("PROP(RELATIVETIME) [THREAD] LEVEL LOGGER * - MESSAGE"); +// test.setTailing(false); +// test.setAppendNonMatches(true); +// test.setTimestampFormat("yyyy-MM-d HH:mm:ss,SSS"); +// test.setFileURL("file:///C:/log/test.log"); +// test.initialize(); +// test.activateOptions(); +// } + + /** + * Close the reader. + */ + public void shutdown() { + getLogger().info(getPath() + " shutdown"); + active = false; + try { + if (reader != null) { + reader.close(); + reader = null; + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + /** + * Read and process the log file. + */ + public void activateOptions() { + getLogger().info("activateOptions"); + active = true; + Runnable runnable = new Runnable() { + public void run() { + initialize(); + while (reader == null) { + getLogger().info("attempting to load file: " + getFileURL()); + try { + reader = new InputStreamReader(new URL(getFileURL()).openStream()); + } catch (FileNotFoundException fnfe) { + getLogger().info("file not available - will try again"); + synchronized (this) { + try { + wait(MISSING_FILE_RETRY_MILLIS); + } catch (InterruptedException ie) {} + } + } catch (IOException ioe) { + getLogger().warn("unable to load file", ioe); + return; + } + } + try { + BufferedReader bufferedReader = new BufferedReader(reader); + createPattern(); + do { + process(bufferedReader); + try { + synchronized (this) { + wait(waitMillis); + } + } catch (InterruptedException ie) {} + if (tailing) { + getLogger().debug("tailing file"); + } + } while (tailing); + + } catch (IOException ioe) { + //io exception - probably shut down + getLogger().info("stream closed"); + } + getLogger().debug("processing " + path + " complete"); + shutdown(); + } + }; + if(useCurrentThread) { + runnable.run(); + }else { + new Thread(runnable, "LogFilePatternReceiver-"+getName()).start(); + } + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java b/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java new file mode 100644 index 0000000..f99f289 --- /dev/null +++ b/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.varia; + +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; + + +/** + * BeanInfo class for the meta-data of the LogFilePatternReceiver. + * + */ +public class LogFilePatternReceiverBeanInfo extends SimpleBeanInfo { + /* (non-Javadoc) + * @see java.beans.BeanInfo#getPropertyDescriptors() + */ + public PropertyDescriptor[] getPropertyDescriptors() { + try { + return new PropertyDescriptor[] { + new PropertyDescriptor("fileURL", LogFilePatternReceiver.class), + new PropertyDescriptor( + "timestampFormat", LogFilePatternReceiver.class), + new PropertyDescriptor("logFormat", LogFilePatternReceiver.class), + new PropertyDescriptor("name", LogFilePatternReceiver.class), + new PropertyDescriptor("tailing", LogFilePatternReceiver.class), + new PropertyDescriptor( + "filterExpression", LogFilePatternReceiver.class), + new PropertyDescriptor("waitMillis", LogFilePatternReceiver.class), + new PropertyDescriptor("appendNonMatches", LogFilePatternReceiver.class), + new PropertyDescriptor("customLevelDefinitions", LogFilePatternReceiver.class), + new PropertyDescriptor("useCurrentThread", LogFilePatternReceiver.class), + }; + } catch (Exception e) { + } + + return null; + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/xml/LogFileXMLReceiver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/xml/LogFileXMLReceiver.java b/src/main/java/org/apache/log4j/xml/LogFileXMLReceiver.java new file mode 100644 index 0000000..945d4f0 --- /dev/null +++ b/src/main/java/org/apache/log4j/xml/LogFileXMLReceiver.java @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.xml; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collection; +import java.util.Iterator; + +import org.apache.log4j.helpers.Constants; +import org.apache.log4j.plugins.Receiver; +import org.apache.log4j.rule.ExpressionRule; +import org.apache.log4j.rule.Rule; +import org.apache.log4j.spi.Decoder; +import org.apache.log4j.spi.LoggingEvent; + +/** + * LogFileXMLReceiver will read an xml-formated log file and make the events in the log file + * available to the log4j framework. + * <p> + * This receiver supports log files created using log4j's XMLLayout, as well as java.util.logging + * XMLFormatter (via the org.apache.log4j.spi.Decoder interface). + * <p> + * By default, log4j's XMLLayout is supported (no need to specify a decoder in that case). + * <p> + * To configure this receiver to support java.util.logging's XMLFormatter, specify a 'decoder' param + * of org.apache.log4j.xml.UtilLoggingXMLDecoder. + * <p> + * Tailing -may- work, but not in all cases (try using a file:// URL). If a process has a log file + * open, the receiver may be able to read and tail the file. If the process closes the file and + * reopens the file, the receiver may not be able to continue tailing the file. + * <p> + * An expressionFilter may be specified. Only events passing the expression will be forwarded to the + * log4j framework. + * <p> + * Once the event has been "posted", it will be handled by the appenders currently configured in the + * LoggerRespository. + * + * @author Scott Deboy <[email protected]> + * @since 1.3 + */ + +public class LogFileXMLReceiver extends Receiver { + private String fileURL; + private Rule expressionRule; + private String filterExpression; + private String decoder = "org.apache.log4j.xml.XMLDecoder"; + private boolean tailing = false; + + private Decoder decoderInstance; + private Reader reader; + private static final String FILE_KEY = "file"; + private String host; + private String path; + private boolean useCurrentThread; + + /** + * Accessor + * + * @return file URL + */ + public String getFileURL() { + return fileURL; + } + + /** + * Specify the URL of the XML-formatted file to process. + * + * @param fileURL + */ + public void setFileURL(String fileURL) { + this.fileURL = fileURL; + } + + /** + * Accessor + * + * @return + */ + public String getDecoder() { + return decoder; + } + + /** + * Specify the class name implementing org.apache.log4j.spi.Decoder that can process the file. + * + * @param _decoder + */ + public void setDecoder(String _decoder) { + decoder = _decoder; + } + + /** + * Accessor + * + * @return filter expression + */ + public String getFilterExpression() { + return filterExpression; + } + + /** + * Accessor + * + * @return tailing flag + */ + public boolean isTailing() { + return tailing; + } + + /** + * Set the 'tailing' flag - may only work on file:// URLs and may stop tailing if the writing + * process closes the file and reopens. + * + * @param tailing + */ + public void setTailing(boolean tailing) { + this.tailing = tailing; + } + + /** + * Set the filter expression that will cause only events which pass the filter to be forwarded + * to the log4j framework. + * + * @param filterExpression + */ + public void setFilterExpression(String filterExpression) { + this.filterExpression = filterExpression; + } + + private boolean passesExpression(LoggingEvent event) { + if (event != null) { + if (expressionRule != null) { + return (expressionRule.evaluate(event, null)); + } + } + return true; + } + + public static void main(String[] args) { + /* + * LogFileXMLReceiver test = new LogFileXMLReceiver(); + * test.setFileURL("file:///c:/samplelog.xml"); test.setFilterExpression("level >= TRACE"); + * test.activateOptions(); + */ + } + + /** + * Close the receiver, release any resources that are accessing the file. + */ + public void shutdown() { + try { + if (reader != null) { + reader.close(); + reader = null; + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + /** + * Process the file + */ + public void activateOptions() { + Runnable runnable = new Runnable() { + public void run() { + try { + URL url = new URL(fileURL); + host = url.getHost(); + if (host != null && host.equals("")) { + host = FILE_KEY; + } + path = url.getPath(); + } catch (MalformedURLException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + try { + if (filterExpression != null) { + expressionRule = ExpressionRule.getRule(filterExpression); + } + } catch (Exception e) { + getLogger().warn("Invalid filter expression: " + filterExpression, e); + } + + Class c; + try { + c = Class.forName(decoder); + Object o = c.newInstance(); + if (o instanceof Decoder) { + decoderInstance = (Decoder) o; + } + } catch (ClassNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InstantiationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + reader = new InputStreamReader(new URL(getFileURL()).openStream()); + process(reader); + } catch (FileNotFoundException fnfe) { + getLogger().info("file not available"); + } catch (IOException ioe) { + getLogger().warn("unable to load file", ioe); + return; + } + } + }; + if (useCurrentThread) { + runnable.run(); + } else { + Thread thread = new Thread(runnable, "LogFileXMLReceiver-" + getName()); + + thread.start(); + + } + } + + private void process(Reader unbufferedReader) throws IOException { + BufferedReader bufferedReader = new BufferedReader(unbufferedReader); + char[] content = new char[10000]; + getLogger().debug("processing starting: " + fileURL); + int length = 0; + do { + System.out.println("in do loop-about to process"); + while ((length = bufferedReader.read(content)) > -1) { + processEvents(decoderInstance.decodeEvents(String.valueOf(content, 0, length))); + } + if (tailing) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } while (tailing); + getLogger().debug("processing complete: " + fileURL); + + shutdown(); + } + + private void processEvents(Collection c) { + if (c == null) { + return; + } + + for (Iterator iter = c.iterator(); iter.hasNext();) { + LoggingEvent evt = (LoggingEvent) iter.next(); + if (passesExpression(evt)) { + if (evt.getProperty(Constants.HOSTNAME_KEY) != null) { + evt.setProperty(Constants.HOSTNAME_KEY, host); + } + if (evt.getProperty(Constants.APPLICATION_KEY) != null) { + evt.setProperty(Constants.APPLICATION_KEY, path); + } + doPost(evt); + } + } + } + + /** + * When true, this property uses the current Thread to perform the import, otherwise when false + * (the default), a new Thread is created and started to manage the import. + * + * @return + */ + public final boolean isUseCurrentThread() { + return useCurrentThread; + } + + /** + * Sets whether the current Thread or a new Thread is created to perform the import, the default + * being false (new Thread created). + * + * @param useCurrentThread + */ + public final void setUseCurrentThread(boolean useCurrentThread) { + this.useCurrentThread = useCurrentThread; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/xml/UtilLoggingEntityResolver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/xml/UtilLoggingEntityResolver.java b/src/main/java/org/apache/log4j/xml/UtilLoggingEntityResolver.java new file mode 100644 index 0000000..71a349c --- /dev/null +++ b/src/main/java/org/apache/log4j/xml/UtilLoggingEntityResolver.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.xml; + +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; + +import java.io.ByteArrayInputStream; + + +/** + * An {@link EntityResolver} specifically designed to return + * an empty InputSource for logger.dtd. + * + */ +public final class UtilLoggingEntityResolver implements EntityResolver { + + /** + * Create new instance. + */ + public UtilLoggingEntityResolver() { + super(); + } + + + /** {@inheritDoc} */ + public InputSource resolveEntity(final String publicId, + final String systemId) { + if (systemId.endsWith("logger.dtd")) { + return new InputSource(new ByteArrayInputStream(new byte[0])); + } else { + return null; + } + } +}
