org.ops4j.pax.logging.internal.JdkHandler Maven / Gradle / Ivy
Go to download
Pax Logging API Library is a collection of logging APIs from different libraries/facades.
It supports SLF4J, Commons Logging, JULI Logging, Log4J1 API, Log4J2 API, JBoss Logging and Avalon APIs.
Additionally, Pax Logging specific library is available as backend implementation with its specific configuration mechanisms,
but it's not required.
The newest version!
/*
* Copyright 2006 Niclas Hedhman.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ops4j.pax.logging.internal;
import java.util.Objects;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.ops4j.pax.logging.PaxLogger;
import org.ops4j.pax.logging.PaxLoggingConstants;
import org.ops4j.pax.logging.PaxLoggingManager;
import org.ops4j.pax.logging.spi.support.OsgiUtil;
import org.osgi.framework.BundleContext;
/**
* JUL {@link Handler} that bridges {@link LogRecord log records} to Pax Logging loggers.
*
* Even if {@link SimpleFormatter} is used, we only call its
* {@link java.util.logging.Formatter#formatMessage(LogRecord)} which only uses
* {@link java.text.MessageFormat#format(String, Object...)} method on log record's message.
* It doesn't do anything with remaining fields of {@link LogRecord}.
*
* Since the backport of TLSv1.3 in java (JDK-8248721) the logic of TLS debug traces has been changed.
* The new TLS debug logger uses a configured JUL logger when you define the system property "javax.net.debug"
* as empty. The TLS log records contain additional string parameters. They are without a format parameter in
* the log message. The {@link JdkHandler} appends them to the end of the TLS log message.
*
* The {@link JdkHandler} has the following logging modes for the TLS log records:
*
* - no_logging - TLS messages are not logged
* - no_hex_dumps_logging - all messages except hex dumps are logged
* - debug_logging - all messages are logged
*
* The logging mode is determined by the value of the {@value PaxLoggingConstants#LOGGING_CFG_TLS_LOGGING_MODE}
* system property.
*/
public class JdkHandler extends Handler {
private static final String FQCN = java.util.logging.Logger.class.getName();
private static final String TLS_DEBUG_LOGGER = "javax.net.ssl";
private static final String NO_LOGGING_MODE = "no_logging";
private static final String DEBUG_LOGGING_MODE = "debug_logging";
private static final String HEX_DUMP_OFFSET = "0000:";
private final PaxLoggingManager m_loggingManager;
private BundleContext bundleContext;
private boolean synchronizedFormatter = false;
public JdkHandler(PaxLoggingManager loggingManager) {
m_loggingManager = loggingManager;
setFormatter(new SimpleFormatter());
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
String sync = OsgiUtil.systemOrContextProperty(bundleContext, PaxLoggingConstants.LOGGING_CFG_SKIP_JUL_SYNCHRONIZED_FORMATTER);
synchronizedFormatter = sync == null || "true".equalsIgnoreCase(sync.trim());
}
@Override
public void close() throws SecurityException {
}
@Override
public void flush() {
}
@Override
public Formatter getFormatter() {
if (synchronizedFormatter) {
return super.getFormatter();
} else {
return new SimpleFormatter();
}
}
/**
* Using information from {@link LogRecord} logging method on related {@link PaxLogger} is called.
* @param record
*/
@Override
public void publish(LogRecord record) {
Level level = record.getLevel();
String loggerName = record.getLoggerName();
PaxLogger logger = m_loggingManager.getLogger(loggerName, FQCN);
if (TLS_DEBUG_LOGGER.equals(loggerName) && !isTLSDebugLoggingEnabled()) {
return;
}
String message;
try {
if (TLS_DEBUG_LOGGER.equals(loggerName) && isTLSDebugLoggingEnabled()) {
// The TLS debug log records contain an additional string parameter with attachments
// (hex dumps, handshake messages etc). They are without a format parameter in the log message,
// so they are not parsed by the JUL formatter.
message = getTLSLogMessage(record);
} else {
// LogRecord may have parameters associated, so let's format the message
// using JUL formatter
message = getFormatter().formatMessage(record);
}
} catch (Exception ex) {
message = record.getMessage();
}
Throwable throwable = record.getThrown();
int levelInt = level.intValue();
if (throwable != null) {
if (levelInt <= Level.FINER.intValue()) {
logger.trace(message, throwable);
} else if (levelInt <= Level.FINE.intValue()) {
logger.debug(message, throwable);
} else if (levelInt <= Level.INFO.intValue()) {
logger.info(message, throwable);
} else if (levelInt <= Level.WARNING.intValue()) {
logger.warn(message, throwable);
} else {
logger.error(message, throwable);
}
} else {
if (levelInt <= Level.FINER.intValue()) {
logger.trace(message);
} else if (levelInt <= Level.FINE.intValue()) {
logger.debug(message);
} else if (levelInt <= Level.INFO.intValue()) {
logger.info(message);
} else if (levelInt <= Level.WARNING.intValue()) {
logger.warn(message);
} else {
logger.error(message);
}
}
}
/**
* Appends the TLS log record parameters to the end of the TLS log message.
*
* @param logRecord The TLS log record.
*/
private String getTLSLogMessage(LogRecord logRecord) {
String message;
try {
message = logRecord.getMessage();
if (logRecord.getParameters() != null) {
String tlsLoggingModeProperty = System.getProperty(PaxLoggingConstants.LOGGING_CFG_TLS_LOGGING_MODE);
boolean isDebugLoggingEnabled = DEBUG_LOGGING_MODE.equals(tlsLoggingModeProperty);
String logParameters = Stream.of(logRecord.getParameters())
.filter(Objects::nonNull)
.filter(String.class::isInstance)
.map(String::valueOf)
// hex dump parameters are not filtered only if the TLS logging mode is "debug logging"
.filter(x -> !isHexDumpMessage(x) || isDebugLoggingEnabled)
.collect(Collectors.joining(System.lineSeparator()));
logParameters = !logParameters.trim().isEmpty() ? System.lineSeparator() + logParameters : "";
message += logParameters;
}
} catch (Exception ex) {
message = logRecord.getMessage();
}
return message;
}
/**
* Checks if TLS debug logging is enabled.
*
* @return Returns true if the value of the system property {@value PaxLoggingConstants#LOGGING_CFG_TLS_LOGGING_MODE}
* is not blank and the TLS logging mode is not NO_LOGGING_MODE.
*/
private boolean isTLSDebugLoggingEnabled() {
String property = System.getProperty(PaxLoggingConstants.LOGGING_CFG_TLS_LOGGING_MODE);
return property != null && !property.trim().isEmpty() && !NO_LOGGING_MODE.equals(property);
}
/**
* Checks if the TLS log parameter is a hex dump message.
*
* @param message The TLS log parameter.
* @return If the parameter starts with 0000: returns true. Otherwise, returns false.
*/
private boolean isHexDumpMessage(String message) {
return message.startsWith(HEX_DUMP_OFFSET, 2);
}
}