use thread name formtting as simple formatter for default, remove png logging
This commit is contained in:
parent
81f3cc723d
commit
b2ec110f85
10 changed files with 138 additions and 478 deletions
|
@ -72,7 +72,7 @@ public final class LogContext implements AutoCloseable {
|
|||
}
|
||||
|
||||
static {
|
||||
final HashMap<String, Reference<Level, Void>> map = new HashMap<String, Reference<Level, Void>>();
|
||||
final HashMap<String, Reference<Level, Void>> map = new HashMap<>();
|
||||
addStrong(map, Level.OFF);
|
||||
addStrong(map, Level.ALL);
|
||||
addStrong(map, Level.SEVERE);
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
@ -22,12 +23,17 @@ public final class LogManager extends java.util.logging.LogManager {
|
|||
static {
|
||||
try {
|
||||
// Ensure the StandardOutputStreams are initialized early to capture the current System.out and System.err.
|
||||
Class.forName(StandardOutputStreams.class.getName());
|
||||
ClassLoader.getSystemClassLoader().loadClass(StandardOutputStreams.class.getName());
|
||||
} catch (ClassNotFoundException ignore) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private static final AtomicBoolean CONFIGURED = new AtomicBoolean();
|
||||
|
||||
// Configuration
|
||||
private final AtomicReference<LogContextConfigurator> configuratorRef = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Construct a new logmanager instance. Attempts to plug a known memory leak in {@link java.util.logging.Level} as
|
||||
* well.
|
||||
|
@ -35,24 +41,117 @@ public final class LogManager extends java.util.logging.LogManager {
|
|||
public LogManager() {
|
||||
}
|
||||
|
||||
// Configuration
|
||||
|
||||
private final AtomicReference<LogContextConfigurator> configuratorRef = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Configure the system log context initially.
|
||||
*/
|
||||
@Override
|
||||
public void readConfiguration() {
|
||||
doConfigure(null);
|
||||
if (CONFIGURED.compareAndSet(false, true)) {
|
||||
doConfigure(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the system log context initially withe given input stream.
|
||||
* Configure the system log context initially with a given input stream.
|
||||
*
|
||||
* @param inputStream ignored
|
||||
* @param inputStream the input stream
|
||||
*/
|
||||
@Override
|
||||
public void readConfiguration(InputStream inputStream) {
|
||||
doConfigure(inputStream);
|
||||
if (CONFIGURED.compareAndSet(false, true)) {
|
||||
doConfigure(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*
|
||||
* @param mapper not used
|
||||
*/
|
||||
@Override
|
||||
public void updateConfiguration(final Function<String, BiFunction<String, String, String>> mapper) throws IOException {
|
||||
// no operation - the configuration API should be used
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*
|
||||
* @param ins not used
|
||||
* @param mapper not used
|
||||
*/
|
||||
@Override
|
||||
public void updateConfiguration(final InputStream ins, final Function<String, BiFunction<String, String, String>> mapper)
|
||||
throws IOException {
|
||||
// no operation - the configuration API should be used
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration listeners are not currently supported.
|
||||
*
|
||||
* @param listener not used
|
||||
* @return this log manager
|
||||
*/
|
||||
@Override
|
||||
public java.util.logging.LogManager addConfigurationListener(final Runnable listener) {
|
||||
// no operation
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration listeners are not currently supported.
|
||||
*
|
||||
* @param listener not used
|
||||
*/
|
||||
@Override
|
||||
public void removeConfigurationListener(final Runnable listener) {
|
||||
// no operation
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. Properties are not supported.
|
||||
*
|
||||
* @param name ignored
|
||||
* @return {@code null}
|
||||
*/
|
||||
@Override
|
||||
public String getProperty(String name) {
|
||||
// no properties
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. This method only causes trouble.
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
// no operation!
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getLoggerNames() {
|
||||
return LogContext.getLogContext().getLoggerNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing. Loggers are only added/acquired via {@link #getLogger(String)}.
|
||||
*
|
||||
* @param logger ignored
|
||||
* @return {@code false}
|
||||
*/
|
||||
@Override
|
||||
public boolean addLogger(java.util.logging.Logger logger) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a logger with the given name.
|
||||
*
|
||||
* @param name the logger name
|
||||
* @return the corresponding logger
|
||||
*/
|
||||
@Override
|
||||
public Logger getLogger(String name) {
|
||||
return LogContext.getLogContext().getLogger(name);
|
||||
}
|
||||
|
||||
private void doConfigure(InputStream inputStream) {
|
||||
|
@ -100,87 +199,4 @@ public final class LogManager extends java.util.logging.LogManager {
|
|||
}
|
||||
configurator.configure(LogContext.getSystemLogContext(), inputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*
|
||||
* @param mapper not used
|
||||
*/
|
||||
public void updateConfiguration(final Function<String, BiFunction<String, String, String>> mapper) throws IOException {
|
||||
// no operation the configuration API should be used
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*
|
||||
* @param ins not used
|
||||
* @param mapper not used
|
||||
*/
|
||||
public void updateConfiguration(final InputStream ins, final Function<String, BiFunction<String, String, String>> mapper)
|
||||
throws IOException {
|
||||
// no operation the configuration API should be used
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration listeners are not currently supported.
|
||||
*
|
||||
* @param listener not used
|
||||
* @return this log manager
|
||||
*/
|
||||
public java.util.logging.LogManager addConfigurationListener(final Runnable listener) {
|
||||
// no operation
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration listeners are not currently supported.
|
||||
*
|
||||
* @param listener not used
|
||||
*/
|
||||
public void removeConfigurationListener(final Runnable listener) {
|
||||
// no operation
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. Properties are not supported.
|
||||
*
|
||||
* @param name ignored
|
||||
* @return {@code null}
|
||||
*/
|
||||
public String getProperty(String name) {
|
||||
// no properties
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. This method only causes trouble.
|
||||
*/
|
||||
public void reset() {
|
||||
// no operation!
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getLoggerNames() {
|
||||
return LogContext.getLogContext().getLoggerNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing. Loggers are only added/acquired via {@link #getLogger(String)}.
|
||||
*
|
||||
* @param logger ignored
|
||||
* @return {@code false}
|
||||
*/
|
||||
public boolean addLogger(java.util.logging.Logger logger) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a logger with the given name.
|
||||
*
|
||||
* @param name the logger name
|
||||
* @return the corresponding logger
|
||||
*/
|
||||
public Logger getLogger(String name) {
|
||||
return LogContext.getLogContext().getLogger(name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,8 @@ public class LoggerFinder extends System.LoggerFinder {
|
|||
final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(name);
|
||||
if (!logger.getClass().getName().equals("org.xbib.logging.Logger")) {
|
||||
if (LOGGED.compareAndSet(false, true)) {
|
||||
logger.log(Level.ERROR,
|
||||
"The LogManager accessed before the \"java.util.logging.manager\" system property was set to \"org.xbib.loggingr.LogManager\". Results may be unexpected.");
|
||||
logger.log(Level.WARN,
|
||||
"The LogManager accessed before the \"java.util.logging.manager\" system property was set to \"org.xbib.logging.LogManager\". Results may be unexpected.");
|
||||
}
|
||||
}
|
||||
return new SystemLogger(logger);
|
||||
|
|
|
@ -26,7 +26,6 @@ class ObjectBuilder<T> {
|
|||
private final Map<String, String> properties;
|
||||
private final Set<PropertyValue> definedProperties;
|
||||
private final Set<String> postConstructMethods;
|
||||
private String moduleName;
|
||||
|
||||
private ObjectBuilder(final ContextConfiguration contextConfiguration,
|
||||
final Class<? extends T> baseClass, final String className) {
|
||||
|
@ -113,7 +112,6 @@ class ObjectBuilder<T> {
|
|||
* @return this builder
|
||||
*/
|
||||
ObjectBuilder<T> setModuleName(final String moduleName) {
|
||||
this.moduleName = moduleName;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -133,7 +131,7 @@ class ObjectBuilder<T> {
|
|||
final ClassLoader classLoader = getClass().getClassLoader();
|
||||
final Class<? extends T> actualClass;
|
||||
try {
|
||||
actualClass = Class.forName(className, true, classLoader).asSubclass(baseClass);
|
||||
actualClass = classLoader.loadClass(className).asSubclass(baseClass);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(String.format("Failed to load class \"%s\"", className), e);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import java.util.logging.Logger;
|
|||
import org.xbib.logging.Level;
|
||||
import org.xbib.logging.LogContext;
|
||||
import org.xbib.logging.LogContextConfigurator;
|
||||
import org.xbib.logging.formatters.PatternFormatter;
|
||||
import org.xbib.logging.formatters.SimpleFormatter;
|
||||
import org.xbib.logging.handlers.ConsoleHandler;
|
||||
import org.xbib.logging.util.StandardOutputStreams;
|
||||
|
||||
|
@ -60,11 +60,11 @@ public class PropertyLogContextConfigurator implements LogContextConfigurator {
|
|||
final Iterator<LogContextConfigurator> serviceLoader =
|
||||
ServiceLoader.load(LogContextConfigurator.class, PropertyLogContextConfigurator.class.getClassLoader()).iterator();
|
||||
if (serviceLoader.hasNext()) {
|
||||
serviceLoader.next().configure(context, null);
|
||||
LogContextConfigurator logContextConfigurator = serviceLoader.next();
|
||||
logContextConfigurator.configure(context, null);
|
||||
} else {
|
||||
// Configure a default console handler, pattern formatter and associated with the root logger
|
||||
final ConsoleHandler handler =
|
||||
new ConsoleHandler(new PatternFormatter("%d{yyyy-MM-dd'T'HH:mm:ssXXX} %-5p [%c] (%t) %s%e%n"));
|
||||
// Configure a default console handler, thread formatter and associated with the root logger
|
||||
final ConsoleHandler handler = new ConsoleHandler(new SimpleFormatter());
|
||||
handler.setLevel(Level.INFO);
|
||||
handler.setAutoFlush(true);
|
||||
final Logger rootLogger = context.getLogger("");
|
||||
|
@ -75,7 +75,7 @@ public class PropertyLogContextConfigurator implements LogContextConfigurator {
|
|||
}
|
||||
|
||||
private static InputStream findConfiguration() {
|
||||
final String propLoc = System.getProperty("logging.configuration");
|
||||
final String propLoc = System.getProperty("org.xbib.logging.configuration");
|
||||
if (propLoc != null) {
|
||||
try {
|
||||
return URI.create(propLoc).toURL().openStream();
|
||||
|
@ -83,10 +83,9 @@ public class PropertyLogContextConfigurator implements LogContextConfigurator {
|
|||
StandardOutputStreams.printError("Unable to read the logging configuration from '%s' (%s)%n", propLoc, e);
|
||||
}
|
||||
}
|
||||
final ClassLoader cl = PropertyLogContextConfigurator.class.getClassLoader();
|
||||
try {
|
||||
return cl.getResourceAsStream("logging.properties");
|
||||
} catch (Exception ignore) {
|
||||
return PropertyLogContextConfigurator.class.getClassLoader().getResourceAsStream("logging.properties");
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,16 +10,16 @@ import java.util.logging.LogManager;
|
|||
import java.util.logging.LogRecord;
|
||||
import org.xbib.logging.util.StackTraceFormatter;
|
||||
|
||||
public class ThreadLoggingFormatter extends Formatter {
|
||||
public class SimpleFormatter extends Formatter {
|
||||
|
||||
private static final String PROPERTY_KEY = "org.xbib.interlibrary.jul.ThreadLoggingFormatter.format";
|
||||
private static final String PROPERTY_KEY = "org.xbib.logging.formatters.SimpleFormatter.format";
|
||||
|
||||
private static final String DEFAULT_FORMAT =
|
||||
"%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] [%5$s] %6$s %7$s%n";
|
||||
|
||||
private final ThreadMXBean threadMXBean;
|
||||
|
||||
public ThreadLoggingFormatter() {
|
||||
public SimpleFormatter() {
|
||||
this.threadMXBean = ManagementFactory.getThreadMXBean();
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ public class ThreadLoggingFormatter extends Formatter {
|
|||
return LogManager.getLogManager().getProperty(name);
|
||||
}
|
||||
|
||||
private static final String DEFAULT_FORMAT_STRING = getFormat(ThreadLoggingFormatter::getLoggingProperty);
|
||||
private static final String DEFAULT_FORMAT_STRING = getFormat(SimpleFormatter::getLoggingProperty);
|
||||
|
||||
private static String getFormat(Function<String, String> defaultPropertyGetter) {
|
||||
String format = System.getProperty(PROPERTY_KEY);
|
|
@ -1,14 +1,9 @@
|
|||
package org.xbib.logging.handlers;
|
||||
|
||||
import java.io.Console;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.ErrorManager;
|
||||
import java.util.logging.Formatter;
|
||||
import org.xbib.logging.errormanager.HandlerErrorManager;
|
||||
|
@ -136,83 +131,6 @@ public class ConsoleHandler extends OutputStreamHandler {
|
|||
|
||||
private static final String ESC = Character.toString(27);
|
||||
|
||||
/**
|
||||
* Write a PNG image to the console log, if it is supported.
|
||||
* The image data stream must be closed by the caller.
|
||||
*
|
||||
* @param imageData the PNG image data stream to write (must not be {@code null})
|
||||
* @param columns the number of text columns to occupy (0 for automatic)
|
||||
* @param rows the number of text rows to occupy (0 for automatic)
|
||||
* @return {@code true} if the image was written, or {@code false} if image support isn't found
|
||||
* @throws IOException if the stream failed while writing the image
|
||||
*/
|
||||
public boolean writeImagePng(InputStream imageData, int columns, int rows) throws IOException {
|
||||
Objects.requireNonNull(imageData, "imageData");
|
||||
columns = Math.max(0, columns);
|
||||
rows = Math.max(0, rows);
|
||||
if (!isGraphicsSupportPassivelyDetected()) {
|
||||
// no graphics
|
||||
return false;
|
||||
}
|
||||
lock.lock();
|
||||
try {
|
||||
// clear out any pending stuff
|
||||
final Writer writer = getWriter();
|
||||
if (writer == null)
|
||||
return false;
|
||||
// start with the header
|
||||
try (OutputStream os = Base64.getEncoder().wrap(new OutputStream() {
|
||||
final byte[] buffer = new byte[2048];
|
||||
int pos = 0;
|
||||
|
||||
public void write(final int b) throws IOException {
|
||||
if (pos == buffer.length)
|
||||
more();
|
||||
buffer[pos++] = (byte) b;
|
||||
}
|
||||
|
||||
public void write(final byte[] b, int off, int len) throws IOException {
|
||||
while (len > 0) {
|
||||
if (pos == buffer.length) {
|
||||
more();
|
||||
}
|
||||
final int cnt = Math.min(len, buffer.length - pos);
|
||||
System.arraycopy(b, off, buffer, pos, cnt);
|
||||
pos += cnt;
|
||||
off += cnt;
|
||||
len -= cnt;
|
||||
}
|
||||
}
|
||||
|
||||
void more() throws IOException {
|
||||
writer.write("m=1;");
|
||||
writer.write(new String(buffer, 0, pos, StandardCharsets.US_ASCII));
|
||||
writer.write(ESC + "\\");
|
||||
// set up next segment
|
||||
writer.write(ESC + "_G");
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
writer.write("m=0;");
|
||||
writer.write(new String(buffer, 0, pos, StandardCharsets.US_ASCII));
|
||||
writer.write(ESC + "\\\n");
|
||||
writer.flush();
|
||||
pos = 0;
|
||||
}
|
||||
})) {
|
||||
// set the header
|
||||
writer.write(String.format(ESC + "_Gf=100,a=T,c=%d,r=%d,", Integer.valueOf(columns), Integer.valueOf(rows)));
|
||||
// write the data in encoded chunks
|
||||
imageData.transferTo(os);
|
||||
}
|
||||
// OK
|
||||
return true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the local error manager. This is an error manager that will publish errors to this console handler.
|
||||
* The console handler itself should not use this error manager.
|
||||
|
@ -264,24 +182,4 @@ public class ConsoleHandler extends OutputStreamHandler {
|
|||
final String colorterm = System.getenv("COLORTERM");
|
||||
return colorterm != null && (colorterm.contains("truecolor") || colorterm.contains("24bit"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the console can be passively detected to support graphical output.
|
||||
* This call may be expensive, so the result should be captured for the lifetime of any formatter making use of
|
||||
* this information.
|
||||
*
|
||||
* @return {@code true} if the console exists and supports graphical output; {@code false} otherwise or if
|
||||
* graphical support cannot be passively detected
|
||||
*/
|
||||
public static boolean isGraphicsSupportPassivelyDetected() {
|
||||
if (!hasConsole()) {
|
||||
return false;
|
||||
}
|
||||
final String term = System.getenv("TERM");
|
||||
final String termProgram = System.getenv("TERM_PROGRAM");
|
||||
return term != null && (term.equalsIgnoreCase("kitty")
|
||||
|| term.equalsIgnoreCase("xterm-kitty")
|
||||
|| term.equalsIgnoreCase("wezterm")
|
||||
|| term.equalsIgnoreCase("konsole")) || termProgram != null && termProgram.equalsIgnoreCase("wezterm");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package org.xbib.logging.handlers;
|
||||
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
/**
|
||||
* Replacement for java.util.logging.ConsoleHandler.
|
||||
*/
|
||||
public class ThreadLoggingHandler extends ThreadLoggingStreamHandler {
|
||||
|
||||
public ThreadLoggingHandler() {
|
||||
super();
|
||||
this.setOutputStream(System.out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
super.publish(record);
|
||||
this.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.flush();
|
||||
}
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
package org.xbib.logging.handlers;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import org.xbib.logging.formatters.ThreadLoggingFormatter;
|
||||
|
||||
/**
|
||||
* Replacement for java.util.logging.StreamHandler that is wired to the ThreadLoggingFormatter
|
||||
* and does not use any fall-back formatter
|
||||
*/
|
||||
public class ThreadLoggingStreamHandler extends Handler {
|
||||
|
||||
private static final ThreadLoggingFormatter formatter = new ThreadLoggingFormatter();
|
||||
|
||||
private final ReentrantLock lock;
|
||||
|
||||
private OutputStream output;
|
||||
|
||||
private boolean doneHeader;
|
||||
|
||||
private volatile Writer writer;
|
||||
|
||||
public ThreadLoggingStreamHandler() {
|
||||
super();
|
||||
this.lock = this.initLocking();
|
||||
setLevel(Level.ALL);
|
||||
setFilter(null);
|
||||
setFormatter(formatter);
|
||||
try {
|
||||
setEncoding("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// can't happen
|
||||
}
|
||||
}
|
||||
|
||||
protected void setOutputStream(OutputStream out) {
|
||||
if (this.tryUseLock()) {
|
||||
try {
|
||||
this.setOutputStream0(out);
|
||||
} finally {
|
||||
this.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
this.setOutputStream0(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setOutputStream0(OutputStream out) {
|
||||
if (out == null) {
|
||||
throw new NullPointerException();
|
||||
} else {
|
||||
this.flushAndClose();
|
||||
this.output = out;
|
||||
this.doneHeader = false;
|
||||
String encoding = this.getEncoding();
|
||||
if (encoding == null) {
|
||||
this.writer = new OutputStreamWriter(this.output);
|
||||
} else {
|
||||
try {
|
||||
this.writer = new OutputStreamWriter(this.output, encoding);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("Unexpected exception " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(String encoding) throws UnsupportedEncodingException {
|
||||
if (this.tryUseLock()) {
|
||||
try {
|
||||
this.setEncoding0(encoding);
|
||||
} finally {
|
||||
this.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
this.setEncoding0(encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setEncoding0(String encoding) throws UnsupportedEncodingException {
|
||||
super.setEncoding(encoding);
|
||||
if (this.output != null) {
|
||||
this.flush();
|
||||
if (encoding == null) {
|
||||
this.writer = new OutputStreamWriter(this.output);
|
||||
} else {
|
||||
this.writer = new OutputStreamWriter(this.output, encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
if (this.tryUseLock()) {
|
||||
try {
|
||||
this.publish0(record);
|
||||
} finally {
|
||||
this.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
this.publish0(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void publish0(LogRecord record) {
|
||||
if (this.isLoggable(record)) {
|
||||
String msg;
|
||||
try {
|
||||
msg = this.getFormatter().format(record);
|
||||
} catch (Exception e) {
|
||||
this.reportError(null, e, 5);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!this.doneHeader) {
|
||||
this.writer.write(this.getFormatter().getHead(this));
|
||||
this.doneHeader = true;
|
||||
}
|
||||
this.writer.write(msg);
|
||||
} catch (Exception e) {
|
||||
this.reportError(null, e, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(LogRecord record) {
|
||||
return this.writer != null && record != null && super.isLoggable(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
if (this.tryUseLock()) {
|
||||
try {
|
||||
this.flush0();
|
||||
} finally {
|
||||
this.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
this.flush0();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void flush0() {
|
||||
if (this.writer != null) {
|
||||
try {
|
||||
this.writer.flush();
|
||||
} catch (Exception e) {
|
||||
this.reportError(null, e, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void flushAndClose() {
|
||||
if (this.writer != null) {
|
||||
try {
|
||||
if (!this.doneHeader) {
|
||||
this.writer.write(this.getFormatter().getHead(this));
|
||||
this.doneHeader = true;
|
||||
}
|
||||
this.writer.write(this.getFormatter().getTail(this));
|
||||
this.writer.flush();
|
||||
this.writer.close();
|
||||
} catch (Exception e) {
|
||||
this.reportError(null, e, 3);
|
||||
}
|
||||
this.writer = null;
|
||||
this.output = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.tryUseLock()) {
|
||||
try {
|
||||
this.flushAndClose();
|
||||
} finally {
|
||||
this.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
this.flushAndClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ReentrantLock initLocking() {
|
||||
Class<?> clazz = this.getClass();
|
||||
ClassLoader loader = clazz.getClassLoader();
|
||||
return loader != null && loader != ClassLoader.getPlatformClassLoader() ? null : new ReentrantLock();
|
||||
}
|
||||
|
||||
private boolean tryUseLock() {
|
||||
if (this.lock == null) {
|
||||
return false;
|
||||
} else {
|
||||
this.lock.lock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void unlock() {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogRecord;
|
||||
|
@ -29,10 +30,10 @@ public final class HandlerTests {
|
|||
|
||||
@Test
|
||||
public void testNullHandler() {
|
||||
final ExtHandler handler = new ExtHandler() {
|
||||
};
|
||||
handler.setLevel(Level.ALL);
|
||||
handler.publish(new ExtLogRecord(Level.INFO, "Test message", null));
|
||||
try (final ExtHandler handler = new ExtHandler() {}) {
|
||||
handler.setLevel(Level.ALL);
|
||||
handler.publish(new ExtLogRecord(Level.INFO, "Test message", null));
|
||||
}
|
||||
}
|
||||
|
||||
private void initHandler(ExtHandler handler) throws UnsupportedEncodingException {
|
||||
|
@ -67,7 +68,7 @@ public final class HandlerTests {
|
|||
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
handler.setOutputStream(stream);
|
||||
testPublish(handler);
|
||||
assertEquals("Test message", new String(stream.toByteArray(), "utf-8"));
|
||||
assertEquals("Test message", stream.toString(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -85,20 +86,13 @@ public final class HandlerTests {
|
|||
handler.setFile(tempFile);
|
||||
testPublish(handler);
|
||||
handler.close();
|
||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
final FileInputStream is = new FileInputStream(tempFile);
|
||||
try {
|
||||
int r;
|
||||
while ((r = is.read()) != -1)
|
||||
os.write(r);
|
||||
assertEquals("Test message", new String(os.toByteArray(), "utf-8"));
|
||||
tempFile.deleteOnExit();
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
} finally {
|
||||
os.close();
|
||||
try (ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
FileInputStream is = new FileInputStream(tempFile)) {
|
||||
int r;
|
||||
while ((r = is.read()) != -1)
|
||||
os.write(r);
|
||||
assertEquals("Test message", os.toString(StandardCharsets.UTF_8));
|
||||
tempFile.deleteOnExit();
|
||||
}
|
||||
} finally {
|
||||
tempFile.delete();
|
||||
|
@ -106,7 +100,7 @@ public final class HandlerTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEnableDisableHandler() throws Throwable {
|
||||
public void testEnableDisableHandler() {
|
||||
final StringListHandler handler = new StringListHandler();
|
||||
testPublish(handler);
|
||||
assertEquals(1, handler.size());
|
||||
|
|
Loading…
Reference in a new issue