This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,190 @@
/*
* 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.juli;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.logging.LogRecord;
/**
* A {@link FileHandler} implementation that uses a queue of log entries.
*
* <p>Configuration properties are inherited from the {@link FileHandler}
* class. This class does not add its own configuration properties for the
* logging configuration, but relies on the following system properties
* instead:</p>
*
* <ul>
* <li><code>org.apache.juli.AsyncOverflowDropType</code>
* Default value: <code>1</code></li>
* <li><code>org.apache.juli.AsyncMaxRecordCount</code>
* Default value: <code>10000</code></li>
* <li><code>org.apache.juli.AsyncLoggerPollInterval</code>
* Default value: <code>1000</code></li>
* </ul>
*
* <p>See the System Properties page in the configuration reference of Tomcat.</p>
*/
public class AsyncFileHandler extends FileHandler {
public static final int OVERFLOW_DROP_LAST = 1;
public static final int OVERFLOW_DROP_FIRST = 2;
public static final int OVERFLOW_DROP_FLUSH = 3;
public static final int OVERFLOW_DROP_CURRENT = 4;
public static final int DEFAULT_OVERFLOW_DROP_TYPE = 1;
public static final int DEFAULT_MAX_RECORDS = 10000;
public static final int DEFAULT_LOGGER_SLEEP_TIME = 1000;
public static final int OVERFLOW_DROP_TYPE = Integer.parseInt(
System.getProperty("org.apache.juli.AsyncOverflowDropType",
Integer.toString(DEFAULT_OVERFLOW_DROP_TYPE)));
public static final int MAX_RECORDS = Integer.parseInt(
System.getProperty("org.apache.juli.AsyncMaxRecordCount",
Integer.toString(DEFAULT_MAX_RECORDS)));
public static final int LOGGER_SLEEP_TIME = Integer.parseInt(
System.getProperty("org.apache.juli.AsyncLoggerPollInterval",
Integer.toString(DEFAULT_LOGGER_SLEEP_TIME)));
protected static final LinkedBlockingDeque<LogEntry> queue =
new LinkedBlockingDeque<>(MAX_RECORDS);
protected static final LoggerThread logger = new LoggerThread();
static {
logger.start();
}
protected volatile boolean closed = false;
public AsyncFileHandler() {
this(null, null, null, DEFAULT_MAX_DAYS);
}
public AsyncFileHandler(String directory, String prefix, String suffix) {
this(directory, prefix, suffix, DEFAULT_MAX_DAYS);
}
public AsyncFileHandler(String directory, String prefix, String suffix, int maxDays) {
super(directory, prefix, suffix, maxDays);
open();
}
@Override
public void close() {
if (closed) {
return;
}
closed = true;
super.close();
}
@Override
protected void open() {
if (!closed) {
return;
}
closed = false;
super.open();
}
@Override
public void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
// fill source entries, before we hand the record over to another
// thread with another class loader
record.getSourceMethodName();
LogEntry entry = new LogEntry(record, this);
boolean added = false;
try {
while (!added && !queue.offer(entry)) {
switch (OVERFLOW_DROP_TYPE) {
case OVERFLOW_DROP_LAST: {
//remove the last added element
queue.pollLast();
break;
}
case OVERFLOW_DROP_FIRST: {
//remove the first element in the queue
queue.pollFirst();
break;
}
case OVERFLOW_DROP_FLUSH: {
added = queue.offer(entry, 1000, TimeUnit.MILLISECONDS);
break;
}
case OVERFLOW_DROP_CURRENT: {
added = true;
break;
}
}//switch
}//while
} catch (InterruptedException x) {
// Allow thread to be interrupted and back out of the publish
// operation. No further action required.
}
}
protected void publishInternal(LogRecord record) {
super.publish(record);
}
protected static class LoggerThread extends Thread {
public LoggerThread() {
this.setDaemon(true);
this.setName("AsyncFileHandlerWriter-" + System.identityHashCode(this));
}
@Override
public void run() {
while (true) {
try {
LogEntry entry = queue.poll(LOGGER_SLEEP_TIME, TimeUnit.MILLISECONDS);
if (entry != null) {
entry.flush();
}
} catch (InterruptedException x) {
// Ignore the attempt to interrupt the thread.
} catch (Exception x) {
x.printStackTrace();
}
}
}
}
protected static class LogEntry {
private final LogRecord record;
private final AsyncFileHandler handler;
public LogEntry(LogRecord record, AsyncFileHandler handler) {
super();
this.record = record;
this.handler = handler;
}
public boolean flush() {
if (handler.closed) {
return false;
} else {
handler.publishInternal(record);
return true;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,192 @@
/*
* 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.juli;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>Cache structure for SimpleDateFormat formatted timestamps based on
* seconds.</p>
*
* <p>Millisecond formatting using S is not supported. You should add the
* millisecond information after getting back the second formatting.</p>
*
* <p>The cache consists of entries for a consecutive range of
* seconds. The length of the range is configurable. It is
* implemented based on a cyclic buffer. New entries shift the range.</p>
*
* <p>The cache is not threadsafe. It can be used without synchronization
* via thread local instances, or with synchronization as a global cache.</p>
*
* <p>The cache can be created with a parent cache to build a cache hierarchy.
* Access to the parent cache is threadsafe.</p>
*/
public class DateFormatCache {
private static final String msecPattern = "#";
/* Timestamp format */
private final String format;
/* Number of cached entries */
private final int cacheSize;
private final Cache cache;
/**
* Replace the millisecond formatting character 'S' by
* some dummy characters in order to make the resulting
* formatted time stamps cacheable. Our consumer might
* choose to replace the dummy chars with the actual
* milliseconds because that's relatively cheap.
*/
private String tidyFormat(String format) {
boolean escape = false;
StringBuilder result = new StringBuilder();
int len = format.length();
char x;
for (int i = 0; i < len; i++) {
x = format.charAt(i);
if (escape || x != 'S') {
result.append(x);
} else {
result.append(msecPattern);
}
if (x == '\'') {
escape = !escape;
}
}
return result.toString();
}
public DateFormatCache(int size, String format, DateFormatCache parent) {
cacheSize = size;
this.format = tidyFormat(format);
Cache parentCache = null;
if (parent != null) {
synchronized(parent) {
parentCache = parent.cache;
}
}
cache = new Cache(parentCache);
}
public String getFormat(long time) {
return cache.getFormat(time);
}
public String getTimeFormat() {
return format;
}
private class Cache {
/* Second formatted in most recent invocation */
private long previousSeconds = Long.MIN_VALUE;
/* Formatted timestamp generated in most recent invocation */
private String previousFormat = "";
/* First second contained in cache */
private long first = Long.MIN_VALUE;
/* Last second contained in cache */
private long last = Long.MIN_VALUE;
/* Index of "first" in the cyclic cache */
private int offset = 0;
/* Helper object to be able to call SimpleDateFormat.format(). */
private final Date currentDate = new Date();
private String cache[];
private SimpleDateFormat formatter;
private Cache parent = null;
private Cache(Cache parent) {
cache = new String[cacheSize];
formatter = new SimpleDateFormat(format, Locale.US);
formatter.setTimeZone(TimeZone.getDefault());
this.parent = parent;
}
private String getFormat(long time) {
long seconds = time / 1000;
/* First step: if we have seen this timestamp
during the previous call, return the previous value. */
if (seconds == previousSeconds) {
return previousFormat;
}
/* Second step: Try to locate in cache */
previousSeconds = seconds;
int index = (offset + (int)(seconds - first)) % cacheSize;
if (index < 0) {
index += cacheSize;
}
if (seconds >= first && seconds <= last) {
if (cache[index] != null) {
/* Found, so remember for next call and return.*/
previousFormat = cache[index];
return previousFormat;
}
/* Third step: not found in cache, adjust cache and add item */
} else if (seconds >= last + cacheSize || seconds <= first - cacheSize) {
first = seconds;
last = first + cacheSize - 1;
index = 0;
offset = 0;
for (int i = 1; i < cacheSize; i++) {
cache[i] = null;
}
} else if (seconds > last) {
for (int i = 1; i < seconds - last; i++) {
cache[(index + cacheSize - i) % cacheSize] = null;
}
first = seconds - (cacheSize - 1);
last = seconds;
offset = (index + 1) % cacheSize;
} else if (seconds < first) {
for (int i = 1; i < first - seconds; i++) {
cache[(index + i) % cacheSize] = null;
}
first = seconds;
last = seconds + (cacheSize - 1);
offset = index;
}
/* Last step: format new timestamp either using
* parent cache or locally. */
if (parent != null) {
synchronized(parent) {
previousFormat = parent.getFormat(time);
}
} else {
currentDate.setTime(time);
previousFormat = formatter.format(currentDate);
}
cache[index] = previousFormat;
return previousFormat;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,107 @@
/*
* 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.juli;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
/**
* A more compact formatter.
*
* Equivalent log4j config:
* <pre>
* log4j.rootCategory=WARN, A1
* log4j.appender.A1=org.apache.log4j.ConsoleAppender
* log4j.appender.A1.layout=org.apache.log4j.PatternLayout
* log4j.appender.A1.Target=System.err
* log4j.appender.A1.layout.ConversionPattern=%r %-15.15c{2} %-1.1p %m %n
* </pre>
*
* Example:
* 1130122891846 Http11BaseProtocol I Initializing Coyote HTTP/1.1 on http-8800
*
*
* @author Costin Manolache
*/
public class JdkLoggerFormatter extends Formatter {
// values from JDK Level
public static final int LOG_LEVEL_TRACE = 400;
public static final int LOG_LEVEL_DEBUG = 500;
public static final int LOG_LEVEL_INFO = 800;
public static final int LOG_LEVEL_WARN = 900;
public static final int LOG_LEVEL_ERROR = 1000;
public static final int LOG_LEVEL_FATAL = 1000;
@Override
public String format(LogRecord record) {
Throwable t=record.getThrown();
int level=record.getLevel().intValue();
String name=record.getLoggerName();
long time=record.getMillis();
String message=formatMessage(record);
if( name.indexOf('.') >= 0 )
name = name.substring(name.lastIndexOf('.') + 1);
// Use a string buffer for better performance
StringBuilder buf = new StringBuilder();
buf.append(time);
// pad to 8 to make it more readable
for( int i=0; i<8-buf.length(); i++ ) { buf.append(" "); }
// Append a readable representation of the log level.
switch(level) {
case LOG_LEVEL_TRACE: buf.append(" T "); break;
case LOG_LEVEL_DEBUG: buf.append(" D "); break;
case LOG_LEVEL_INFO: buf.append(" I "); break;
case LOG_LEVEL_WARN: buf.append(" W "); break;
case LOG_LEVEL_ERROR: buf.append(" E "); break;
//case : buf.append(" F "); break;
default: buf.append(" ");
}
// Append the name of the log instance if so configured
buf.append(name);
buf.append(" ");
// pad to 20 chars
for( int i=0; i<8-buf.length(); i++ ) { buf.append(" "); }
// Append the message
buf.append(message);
// Append stack trace if not null
if(t != null) {
buf.append(System.lineSeparator());
java.io.StringWriter sw= new java.io.StringWriter(1024);
java.io.PrintWriter pw= new java.io.PrintWriter(sw);
t.printStackTrace(pw);
pw.close();
buf.append(sw.toString());
}
buf.append(System.lineSeparator());
// Print to the appropriate destination
return buf.toString();
}
}

View File

@@ -0,0 +1,253 @@
/*
* 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.juli;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Formatter;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
/**
* Provides same information as default log format but on a single line to make
* it easier to grep the logs. The only exception is stacktraces which are
* always preceded by whitespace to make it simple to skip them.
*/
/*
* Date processing based on AccessLogValve.
*/
public class OneLineFormatter extends Formatter {
private static final String UNKNOWN_THREAD_NAME = "Unknown thread with ID ";
private static final Object threadMxBeanLock = new Object();
private static volatile ThreadMXBean threadMxBean = null;
private static final int THREAD_NAME_CACHE_SIZE = 10000;
private static ThreadLocal<ThreadNameCache> threadNameCache = new ThreadLocal<ThreadNameCache>() {
@Override
protected ThreadNameCache initialValue() {
return new ThreadNameCache(THREAD_NAME_CACHE_SIZE);
}
};
/* Timestamp format */
private static final String DEFAULT_TIME_FORMAT = "dd-MMM-yyyy HH:mm:ss";
/**
* The size of our global date format cache
*/
private static final int globalCacheSize = 30;
/**
* The size of our thread local date format cache
*/
private static final int localCacheSize = 5;
/**
* Thread local date format cache.
*/
private ThreadLocal<DateFormatCache> localDateCache;
public OneLineFormatter() {
String timeFormat = LogManager.getLogManager().getProperty(
OneLineFormatter.class.getName() + ".timeFormat");
if (timeFormat == null) {
timeFormat = DEFAULT_TIME_FORMAT;
}
setTimeFormat(timeFormat);
}
/**
* Specify the time format to use for time stamps in log messages.
*
* @param timeFormat The format to use using the
* {@link java.text.SimpleDateFormat} syntax
*/
public void setTimeFormat(final String timeFormat) {
final DateFormatCache globalDateCache =
new DateFormatCache(globalCacheSize, timeFormat, null);
localDateCache = new ThreadLocal<DateFormatCache>() {
@Override
protected DateFormatCache initialValue() {
return new DateFormatCache(localCacheSize, timeFormat, globalDateCache);
}
};
}
/**
* Obtain the format currently being used for time stamps in log messages.
*
* @return The current format in {@link java.text.SimpleDateFormat} syntax
*/
public String getTimeFormat() {
return localDateCache.get().getTimeFormat();
}
@Override
public String format(LogRecord record) {
StringBuilder sb = new StringBuilder();
// Timestamp
addTimestamp(sb, record.getMillis());
// Severity
sb.append(' ');
sb.append(record.getLevel().getLocalizedName());
// Thread
sb.append(' ');
sb.append('[');
if (Thread.currentThread() instanceof AsyncFileHandler.LoggerThread) {
// If using the async handler can't get the thread name from the
// current thread.
sb.append(getThreadName(record.getThreadID()));
} else {
sb.append(Thread.currentThread().getName());
}
sb.append(']');
// Source
sb.append(' ');
sb.append(record.getSourceClassName());
sb.append('.');
sb.append(record.getSourceMethodName());
// Message
sb.append(' ');
sb.append(formatMessage(record));
// New line for next record
sb.append(System.lineSeparator());
// Stack trace
if (record.getThrown() != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new IndentingPrintWriter(sw);
record.getThrown().printStackTrace(pw);
pw.close();
sb.append(sw.getBuffer());
}
return sb.toString();
}
protected void addTimestamp(StringBuilder buf, long timestamp) {
buf.append(localDateCache.get().getFormat(timestamp));
long frac = timestamp % 1000;
buf.append('.');
if (frac < 100) {
if (frac < 10) {
buf.append('0');
buf.append('0');
} else {
buf.append('0');
}
}
buf.append(frac);
}
/**
* LogRecord has threadID but no thread name.
* LogRecord uses an int for thread ID but thread IDs are longs.
* If the real thread ID > (Integer.MAXVALUE / 2) LogRecord uses it's own
* ID in an effort to avoid clashes due to overflow.
* <p>
* Words fail me to describe what I think of the design decision to use an
* int in LogRecord for a long value and the resulting mess that follows.
*/
private static String getThreadName(int logRecordThreadId) {
Map<Integer,String> cache = threadNameCache.get();
String result = null;
if (logRecordThreadId > (Integer.MAX_VALUE / 2)) {
result = cache.get(Integer.valueOf(logRecordThreadId));
}
if (result != null) {
return result;
}
if (logRecordThreadId > Integer.MAX_VALUE / 2) {
result = UNKNOWN_THREAD_NAME + logRecordThreadId;
} else {
// Double checked locking OK as threadMxBean is volatile
if (threadMxBean == null) {
synchronized (threadMxBeanLock) {
if (threadMxBean == null) {
threadMxBean = ManagementFactory.getThreadMXBean();
}
}
}
ThreadInfo threadInfo =
threadMxBean.getThreadInfo(logRecordThreadId);
if (threadInfo == null) {
return Long.toString(logRecordThreadId);
}
result = threadInfo.getThreadName();
}
cache.put(Integer.valueOf(logRecordThreadId), result);
return result;
}
private static class ThreadNameCache extends LinkedHashMap<Integer,String> {
private static final long serialVersionUID = 1L;
private final int cacheSize;
public ThreadNameCache(int cacheSize) {
this.cacheSize = cacheSize;
}
@Override
protected boolean removeEldestEntry(Entry<Integer, String> eldest) {
return (size() > cacheSize);
}
}
/*
* Minimal implementation to indent the printing of stack traces. This
* implementation depends on Throwable using WrappedPrintWriter.
*/
private static class IndentingPrintWriter extends PrintWriter {
public IndentingPrintWriter(Writer out) {
super(out);
}
@Override
public void println(Object x) {
super.print('\t');
super.println(x);
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.juli;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
/**
* Outputs the just the log message with no additional elements. Stack traces
* are not logged. Log messages are separated by
* <code>System.lineSeparator()</code>. This is intended for use
* by access logs and the like that need complete control over the output
* format.
*/
public class VerbatimFormatter extends Formatter {
@Override
public String format(LogRecord record) {
// Timestamp
StringBuilder sb = new StringBuilder(record.getMessage());
// New line for next record
sb.append(System.lineSeparator());
return sb.toString();
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.juli;
/**
* An interface intended for use by class loaders associated with a web
* application that enables them to provide additional information to JULI about
* the web application with which they are associated. For any web application
* the combination of {@link #getWebappName()}, {@link #getHostName()} and
* {@link #getServiceName()} must be unique.
*/
public interface WebappProperties {
/**
* Returns a name for the logging system to use for the web application, if
* any, associated with the class loader.
*
* @return The name to use for the web application or null if none is
* available.
*/
String getWebappName();
/**
* Returns a name for the logging system to use for the Host where the
* web application, if any, associated with the class loader is deployed.
*
* @return The name to use for the Host where the web application is
* deployed or null if none is available.
*/
String getHostName();
/**
* Returns a name for the logging system to use for the Service where the
* Host, if any, associated with the class loader is deployed.
*
* @return The name to use for the Service where the Host is deployed or
* null if none is available.
*/
String getServiceName();
/**
* Enables JULI to determine if the web application includes a local
* configuration without JULI having to look for the file which it may not
* have permission to do when running under a SecurityManager.
*
* @return {@code true} if the web application includes a logging
* configuration at the standard location of
* /WEB-INF/classes/logging.properties.
*/
boolean hasLoggingConfig();
}

View File

@@ -0,0 +1,185 @@
/*
* 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.juli.logging;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Hard-coded java.util.logging commons-logging implementation.
*/
class DirectJDKLog implements Log {
// no reason to hide this - but good reasons to not hide
public final Logger logger;
// Alternate config reader and console format
private static final String SIMPLE_FMT="java.util.logging.SimpleFormatter";
private static final String FORMATTER="org.apache.juli.formatter";
static {
if (System.getProperty("java.util.logging.config.class") == null &&
System.getProperty("java.util.logging.config.file") == null) {
// default configuration - it sucks. Let's override at least the
// formatter for the console
try {
Formatter fmt= (Formatter) Class.forName(System.getProperty(
FORMATTER, SIMPLE_FMT)).getConstructor().newInstance();
// it is also possible that the user modified jre/lib/logging.properties -
// but that's really stupid in most cases
Logger root=Logger.getLogger("");
for (Handler handler : root.getHandlers()) {
// I only care about console - that's what's used in default config anyway
if (handler instanceof ConsoleHandler) {
handler.setFormatter(fmt);
}
}
} catch (Throwable t) {
// maybe it wasn't included - the ugly default will be used.
}
}
}
public DirectJDKLog(String name ) {
logger=Logger.getLogger(name);
}
@Override
public final boolean isErrorEnabled() {
return logger.isLoggable(Level.SEVERE);
}
@Override
public final boolean isWarnEnabled() {
return logger.isLoggable(Level.WARNING);
}
@Override
public final boolean isInfoEnabled() {
return logger.isLoggable(Level.INFO);
}
@Override
public final boolean isDebugEnabled() {
return logger.isLoggable(Level.FINE);
}
@Override
public final boolean isFatalEnabled() {
return logger.isLoggable(Level.SEVERE);
}
@Override
public final boolean isTraceEnabled() {
return logger.isLoggable(Level.FINER);
}
@Override
public final void debug(Object message) {
log(Level.FINE, String.valueOf(message), null);
}
@Override
public final void debug(Object message, Throwable t) {
log(Level.FINE, String.valueOf(message), t);
}
@Override
public final void trace(Object message) {
log(Level.FINER, String.valueOf(message), null);
}
@Override
public final void trace(Object message, Throwable t) {
log(Level.FINER, String.valueOf(message), t);
}
@Override
public final void info(Object message) {
log(Level.INFO, String.valueOf(message), null);
}
@Override
public final void info(Object message, Throwable t) {
log(Level.INFO, String.valueOf(message), t);
}
@Override
public final void warn(Object message) {
log(Level.WARNING, String.valueOf(message), null);
}
@Override
public final void warn(Object message, Throwable t) {
log(Level.WARNING, String.valueOf(message), t);
}
@Override
public final void error(Object message) {
log(Level.SEVERE, String.valueOf(message), null);
}
@Override
public final void error(Object message, Throwable t) {
log(Level.SEVERE, String.valueOf(message), t);
}
@Override
public final void fatal(Object message) {
log(Level.SEVERE, String.valueOf(message), null);
}
@Override
public final void fatal(Object message, Throwable t) {
log(Level.SEVERE, String.valueOf(message), t);
}
// from commons logging. This would be my number one reason why java.util.logging
// is bad - design by committee can be really bad ! The impact on performance of
// using java.util.logging - and the ugliness if you need to wrap it - is far
// worse than the unfriendly and uncommon default format for logs.
private void log(Level level, String msg, Throwable ex) {
if (logger.isLoggable(level)) {
// Hack (?) to get the stack trace.
Throwable dummyException=new Throwable();
StackTraceElement locations[]=dummyException.getStackTrace();
// Caller will be the third element
String cname = "unknown";
String method = "unknown";
if (locations != null && locations.length >2) {
StackTraceElement caller = locations[2];
cname = caller.getClassName();
method = caller.getMethodName();
}
if (ex==null) {
logger.logp(level, cname, method, msg);
} else {
logger.logp(level, cname, method, msg, ex);
}
}
}
static Log getInstance(String name) {
return new DirectJDKLog( name );
}
}

View File

@@ -0,0 +1,251 @@
/*
* 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.juli.logging;
/**
* <p>A simple logging interface abstracting logging APIs. In order to be
* instantiated successfully by {@link LogFactory}, classes that implement
* this interface must have a constructor that takes a single String
* parameter representing the "name" of this Log.</p>
*
* <p> The six logging levels used by <code>Log</code> are (in order):</p>
* <ol>
* <li>trace (the least serious)</li>
* <li>debug</li>
* <li>info</li>
* <li>warn</li>
* <li>error</li>
* <li>fatal (the most serious)</li>
* </ol>
* <p>The mapping of these log levels to the concepts used by the underlying
* logging system is implementation dependent.
* The implementation should ensure, though, that this ordering behaves
* as expected.</p>
*
* <p>Performance is often a logging concern.
* By examining the appropriate property,
* a component can avoid expensive operations (producing information
* to be logged).</p>
*
* <p> For example,
* <code>
* if (log.isDebugEnabled()) {
* ... do something expensive ...
* log.debug(theResult);
* }
* </code>
* </p>
*
* <p>Configuration of the underlying logging system will generally be done
* external to the Logging APIs, through whatever mechanism is supported by
* that system.</p>
*
* @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
* @author Rod Waldhoff
*/
public interface Log {
// ----------------------------------------------------- Logging Properties
/**
* <p> Is debug logging currently enabled? </p>
*
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than debug. </p>
*
* @return <code>true</code> if debug level logging is enabled, otherwise
* <code>false</code>
*/
public boolean isDebugEnabled();
/**
* <p> Is error logging currently enabled? </p>
*
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than error. </p>
*
* @return <code>true</code> if error level logging is enabled, otherwise
* <code>false</code>
*/
public boolean isErrorEnabled();
/**
* <p> Is fatal logging currently enabled? </p>
*
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than fatal. </p>
*
* @return <code>true</code> if fatal level logging is enabled, otherwise
* <code>false</code>
*/
public boolean isFatalEnabled();
/**
* <p> Is info logging currently enabled? </p>
*
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than info. </p>
*
* @return <code>true</code> if info level logging is enabled, otherwise
* <code>false</code>
*/
public boolean isInfoEnabled();
/**
* <p> Is trace logging currently enabled? </p>
*
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than trace. </p>
*
* @return <code>true</code> if trace level logging is enabled, otherwise
* <code>false</code>
*/
public boolean isTraceEnabled();
/**
* <p> Is warn logging currently enabled? </p>
*
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than warn. </p>
*
* @return <code>true</code> if warn level logging is enabled, otherwise
* <code>false</code>
*/
public boolean isWarnEnabled();
// -------------------------------------------------------- Logging Methods
/**
* <p> Log a message with trace log level. </p>
*
* @param message log this message
*/
public void trace(Object message);
/**
* <p> Log an error with trace log level. </p>
*
* @param message log this message
* @param t log this cause
*/
public void trace(Object message, Throwable t);
/**
* <p> Log a message with debug log level. </p>
*
* @param message log this message
*/
public void debug(Object message);
/**
* <p> Log an error with debug log level. </p>
*
* @param message log this message
* @param t log this cause
*/
public void debug(Object message, Throwable t);
/**
* <p> Log a message with info log level. </p>
*
* @param message log this message
*/
public void info(Object message);
/**
* <p> Log an error with info log level. </p>
*
* @param message log this message
* @param t log this cause
*/
public void info(Object message, Throwable t);
/**
* <p> Log a message with warn log level. </p>
*
* @param message log this message
*/
public void warn(Object message);
/**
* <p> Log an error with warn log level. </p>
*
* @param message log this message
* @param t log this cause
*/
public void warn(Object message, Throwable t);
/**
* <p> Log a message with error log level. </p>
*
* @param message log this message
*/
public void error(Object message);
/**
* <p> Log an error with error log level. </p>
*
* @param message log this message
* @param t log this cause
*/
public void error(Object message, Throwable t);
/**
* <p> Log a message with fatal log level. </p>
*
* @param message log this message
*/
public void fatal(Object message);
/**
* <p> Log an error with fatal log level. </p>
*
* @param message log this message
* @param t log this cause
*/
public void fatal(Object message, Throwable t);
}

View File

@@ -0,0 +1,72 @@
/*
* 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.juli.logging;
/**
* <p>An exception that is thrown only if a suitable <code>LogFactory</code>
* or <code>Log</code> instance cannot be created by the corresponding
* factory methods.</p>
*
* @author Craig R. McClanahan
*/
public class LogConfigurationException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* Construct a new exception with <code>null</code> as its detail message.
*/
public LogConfigurationException() {
super();
}
/**
* Construct a new exception with the specified detail message.
*
* @param message The detail message
*/
public LogConfigurationException(String message) {
super(message);
}
/**
* Construct a new exception with the specified cause and a derived
* detail message.
*
* @param cause The underlying cause
*/
public LogConfigurationException(Throwable cause) {
super(cause);
}
/**
* Construct a new exception with the specified detail message and cause.
*
* @param message The detail message
* @param cause The underlying cause
*/
public LogConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,248 @@
/*
* 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.juli.logging;
import java.lang.reflect.Constructor;
import java.nio.file.FileSystems;
import java.util.ServiceLoader;
import java.util.logging.LogManager;
/**
* This is a modified LogFactory that uses a simple {@link ServiceLoader} based
* discovery mechanism with a default of using JDK based logging. An
* implementation that uses the full Commons Logging discovery mechanism is
* available as part of the Tomcat extras download.
*
* Why? It is an attempt to strike a balance between simpler code (no discovery)
* and providing flexibility - particularly for those projects that embed Tomcat
* or some of Tomcat's components - is an alternative logging
* implementation is desired.
*
* Note that this implementation is not just a wrapper around JDK logging (like
* the original commons-logging impl). It adds 2 features - a simpler
* configuration (which is in fact a subset of log4j.properties) and a
* formatter that is less ugly.
*
* The removal of 'abstract' preserves binary backward compatibility. It is
* possible to preserve the abstract - and introduce another (hardcoded) factory
* - but I see no benefit.
*
* Since this class is not intended to be extended - all protected methods are
* removed. This can be changed - but again, there is little value in keeping
* dead code. Just take a quick look at the removed code ( and it's complexity).
*
* --------------
*
* Original comment:
* <p>Factory for creating {@link Log} instances, with discovery and
* configuration features similar to that employed by standard Java APIs
* such as JAXP.</p>
*
* <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
* based on the SAXParserFactory and DocumentBuilderFactory implementations
* (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
*
*
* @author Craig R. McClanahan
* @author Costin Manolache
* @author Richard A. Sitze
*/
public class LogFactory {
private static final LogFactory singleton = new LogFactory();
private final Constructor<? extends Log> discoveredLogConstructor;
/**
* Private constructor that is not available for public use.
*/
private LogFactory() {
/*
* Work-around known a JRE bug.
* https://bugs.openjdk.java.net/browse/JDK-8194653
*
* Pre-load the default file system. No performance impact as we need to
* load the default file system anyway. Just do it earlier to avoid the
* potential deadlock.
*
* This can be removed once the oldest JRE supported by Tomcat includes
* a fix.
*/
FileSystems.getDefault();
// Look via a ServiceLoader for a Log implementation that has a
// constructor taking the String name.
ServiceLoader<Log> logLoader = ServiceLoader.load(Log.class);
Constructor<? extends Log> m=null;
for (Log log: logLoader) {
Class<? extends Log> c=log.getClass();
try {
m=c.getConstructor(String.class);
break;
}
catch (NoSuchMethodException | SecurityException e) {
throw new Error(e);
}
}
discoveredLogConstructor=m;
}
// --------------------------------------------------------- Public Methods
// only those 2 methods need to change to use a different direct logger.
/**
* <p>Construct (if necessary) and return a <code>Log</code> instance,
* using the factory's current set of configuration attributes.</p>
*
* <p><strong>NOTE</strong> - Depending upon the implementation of
* the <code>LogFactory</code> you are using, the <code>Log</code>
* instance you are returned may or may not be local to the current
* application, and may or may not be returned again on a subsequent
* call with the same name argument.</p>
*
* @param name Logical name of the <code>Log</code> instance to be
* returned (the meaning of this name is only known to the underlying
* logging implementation that is being wrapped)
*
* @return A log instance with the requested name
*
* @exception LogConfigurationException if a suitable <code>Log</code>
* instance cannot be returned
*/
public Log getInstance(String name) throws LogConfigurationException {
if (discoveredLogConstructor == null) {
return DirectJDKLog.getInstance(name);
}
try {
return discoveredLogConstructor.newInstance(name);
} catch (ReflectiveOperationException | IllegalArgumentException e) {
throw new LogConfigurationException(e);
}
}
/**
* Convenience method to derive a name from the specified class and
* call <code>getInstance(String)</code> with it.
*
* @param clazz Class for which a suitable Log name will be derived
*
* @return A log instance with a name of clazz.getName()
*
* @exception LogConfigurationException if a suitable <code>Log</code>
* instance cannot be returned
*/
public Log getInstance(Class<?> clazz) throws LogConfigurationException {
return getInstance( clazz.getName());
}
// ------------------------------------------------------- Static Variables
// --------------------------------------------------------- Static Methods
/**
* <p>Construct (if necessary) and return a <code>LogFactory</code>
* instance, using the following ordered lookup procedure to determine
* the name of the implementation class to be loaded.</p>
* <ul>
* <li>The <code>org.apache.commons.logging.LogFactory</code> system
* property.</li>
* <li>The JDK 1.3 Service Discovery mechanism</li>
* <li>Use the properties file <code>commons-logging.properties</code>
* file, if found in the class path of this class. The configuration
* file is in standard <code>java.util.Properties</code> format and
* contains the fully qualified name of the implementation class
* with the key being the system property defined above.</li>
* <li>Fall back to a default implementation class
* (<code>org.apache.commons.logging.impl.LogFactoryImpl</code>).</li>
* </ul>
*
* <p><em>NOTE</em> - If the properties file method of identifying the
* <code>LogFactory</code> implementation class is utilized, all of the
* properties defined in this file will be set as configuration attributes
* on the corresponding <code>LogFactory</code> instance.</p>
*
* @return The singleton LogFactory instance
*
* @exception LogConfigurationException if the implementation class is not
* available or cannot be instantiated.
*/
public static LogFactory getFactory() throws LogConfigurationException {
return singleton;
}
/**
* Convenience method to return a named logger, without the application
* having to care about factories.
*
* @param clazz Class from which a log name will be derived
*
* @return A log instance with a name of clazz.getName()
*
* @exception LogConfigurationException if a suitable <code>Log</code>
* instance cannot be returned
*/
public static Log getLog(Class<?> clazz)
throws LogConfigurationException {
return getFactory().getInstance(clazz);
}
/**
* Convenience method to return a named logger, without the application
* having to care about factories.
*
* @param name Logical name of the <code>Log</code> instance to be
* returned (the meaning of this name is only known to the underlying
* logging implementation that is being wrapped)
*
* @return A log instance with the requested name
*
* @exception LogConfigurationException if a suitable <code>Log</code>
* instance cannot be returned
*/
public static Log getLog(String name)
throws LogConfigurationException {
return getFactory().getInstance(name);
}
/**
* Release any internal references to previously created {@link LogFactory}
* instances that have been associated with the specified class loader
* (if any), after calling the instance method <code>release()</code> on
* each of them.
*
* @param classLoader ClassLoader for which to release the LogFactory
*/
public static void release(ClassLoader classLoader) {
// JULI's log manager looks at the current classLoader so there is no
// need to use the passed in classLoader, the default implementation
// does not so calling reset in that case will break things
if (!LogManager.getLogManager().getClass().getName().equals(
"java.util.logging.LogManager")) {
LogManager.getLogManager().reset();
}
}
}

View File

@@ -0,0 +1,37 @@
<!--
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.
-->
<body>
<h2>Overview</h2>
<p>This implementation of commons-logging uses a commons-logging.jar
specific to a particular logging framework, instead of discovery. This takes
out the guessing, is simpler, faster and more robust. Just like you chose a
logging implementation, you should also use a matching commons-logging - for
example you download log4j.jar and commons-logging-log4j.jar, or use jdk
logging and use commons-logging-jdk.jar.</p>
<p>A similar packaging is used by Eclipse SWT - they provide a common widget API,
but each platform uses a different implementation jar - instead of using a complex
discovery/plugin mechanism.
</p>
<p>This package generates commons-logging-jdk14.jar - i.e. the java.util implementation
of commons-logging api.</p>
</body>