io.rxmicro.logger.jul.PatternFormatter Maven / Gradle / Ivy
Show all versions of rxmicro-logger Show documentation
/*
* Copyright (c) 2020. https://rxmicro.io
*
* 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 io.rxmicro.logger.jul;
import io.rxmicro.common.util.Formats;
import io.rxmicro.logger.internal.jul.config.adapter.pattern.PatternFormatterBiConsumerParser;
import io.rxmicro.logger.internal.message.IgnoreLineSeparatorMessageBuilder;
import io.rxmicro.logger.internal.message.MessageBuilder;
import io.rxmicro.logger.internal.message.ReplaceLineSeparatorMessageBuilder;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import static io.rxmicro.logger.internal.jul.InternalLoggerHelper.logInternal;
import static java.util.logging.LogManager.getLogManager;
/**
* This class supports conversion specifiers that can be used as format control expressions.
*
*
* Each conversion specifier starts with a percent sign {@code '%'} and is followed by optional format modifiers,
* a conversion word and optional parameters between braces.
* The conversion word controls the data field to convert, e.g. logger name or date format.
*
*
* Configuration:
* By default each {@link PatternFormatter} is initialized using the following {@link LogManager} configuration properties.
* If properties are not defined (or have invalid values) then the specified default values are used.
*
* -
* {@code io.rxmicro.logger.jul.PatternFormatter.pattern} specifies the pattern for the logged messages
* (defaults to {@value #DEFAULT_PATTERN}).
*
* -
* {@code io.rxmicro.logger.jul.PatternFormatter.singleLine} indicates that all logged messages must be formatted as single line,
* i.e. the {@code '\r\n'} or {@code '\n'} characters must be replaced. (defaults to {@code false}).
*
* -
* {@code io.rxmicro.logger.jul.PatternFormatter.replacement} specifies the string that must be used as replacement for
* the {@code '\r\n'} or {@code '\n'} characters.
* This parameter is ignored if {@code io.rxmicro.logger.jul.PatternFormatter.singleLine} is not {@code true}.
* If this parameter is {@value #IGNORE_REPLACEMENT}, than the {@code '\r\n'} or {@code '\n'} characters will be ignored.
* (defaults to platform specific strings:
* {@code "\\r\\n"} for Windows, {@code "\\n"} for Linux and Osx)
*
*
*
*
* The {@link PatternFormatter} supports the following conversion specifiers:
*
*
* Conversion specifiers
* Description
*
*
*
* c{length}
* lo{length}
* logger{length}
*
*
* Outputs the name of the logger at the origin of the logging event.
*
* This conversion specifier takes a string as its first and only option.
* Currently supported only one of the following options: {short}, {0}, {full}.
* {short} is synonym for {0} option.
* If no option defined this conversion specifier uses {full} option.
*
* The following table describes option usage results:
*
*
* Conversion specifier
* Logger name
* Result
*
*
* %logger
* mainPackage.sub.sample.Bar
* mainPackage.sub.sample.Bar
*
*
* %logger{full}
* mainPackage.sub.sample.Bar
* mainPackage.sub.sample.Bar
*
*
* %logger{short}
* mainPackage.sub.sample.Bar
* Bar
*
*
* %logger{0}
* mainPackage.sub.sample.Bar
* Bar
*
*
*
*
*
*
* C{length}
* class{length}
*
*
* Outputs the fully-qualified class name of the caller issuing the logging request.
*
* This conversion specifier takes a string as its first and only option.
* Currently supported only one of the following options: {short}, {0}, {full}.
* {short} is synonym for {0} option.
* If no option defined this conversion specifier uses {full} option.
*
* The following table describes option usage results.
*
*
* Conversion specifier
* Class name
* Result
*
*
* %logger
* mainPackage.sub.sample.Bar
* mainPackage.sub.sample.Bar
*
*
* %logger{full}
* mainPackage.sub.sample.Bar
* mainPackage.sub.sample.Bar
*
*
* %logger{short}
* mainPackage.sub.sample.Bar
* Bar
*
*
* %logger{0}
* mainPackage.sub.sample.Bar
* Bar
*
*
*
* Generating the caller class information is not particularly fast.
* Thus, its use should be avoided unless execution speed is not an issue.
*
*
*
*
* d{pattern}
* date{pattern}
* d{pattern, timezone}
* date{pattern, timezone}
*
*
* Used to output the date of the logging event.
*
* The date conversion word admits a pattern string as a parameter.
* The pattern syntax is compatible with the format accepted by {@link java.time.format.DateTimeFormatter}.
* If {@code timezone} is specified, this conversion specifier uses {@link java.time.ZoneId#of(String)} method to parse it,
* so timezone syntax must be compatible with zone id format.
*
* Here are some sample parameter values:
*
*
* Conversion pattern
* Result
*
*
* %date
* {@code 2020-01-02 03:04:05.123}
*
*
* %date{yyyy-MM-dd}
* {@code 2020-01-02}
*
*
* %date{HH:mm:ss.SSS}
* {@code 03:04:05.123}
*
*
* %date{, UTC}
* {@code 2020-01-02 03:04:05.123}
*
*
*
* If pattern is missing (For example: {@code %d}, {@code %date}, {@code %date{, UTC}}, the default pattern will be used:
* {@value io.rxmicro.logger.internal.jul.config.adapter.pattern.consumers.DateTimeOfLoggingEventBiConsumer#DEFAULT_PATTERN}
*
*
*
*
* F
* file
*
*
* Outputs the file name of the Java source file where the logging request was issued.
*
* Generating the file information is not particularly fast.
* Thus, its use should be avoided unless execution speed is not an issue.
*
*
*
*
* L
* line
*
*
* Outputs the line number from where the logging request was issued.
*
* Generating the line number information is not particularly fast.
* Thus, its use should be avoided unless execution speed is not an issue.
*
*
*
*
* m
* mes
* message
*
*
* Outputs the application-supplied message associated with the logging event.
*
*
*
*
* M
* method
*
*
* Outputs the method name where the logging request was issued.
*
* Generating the method name is not particularly fast.
* Thus, its use should be avoided unless execution speed is not an issue.
*
*
*
*
* n
*
*
* Outputs the platform dependent line separator character or characters.
*
* This conversion word offers practically the same performance as using non-portable line separator strings
* such as "\n", or "\r\n". Thus, it is the preferred way of specifying a line separator.
*
*
*
*
* p
* le
* level
*
*
* Outputs the level of the logging event.
*
*
*
*
* r
* relative
*
*
* Outputs the number of milliseconds elapsed since the start of the application until the creation of the logging event.
*
*
*
*
* t
* thread
*
*
* Outputs the name of the thread that generated the logging event.
*
*
*
*
* id
* rid
* request-id
* request_id
* requestId
*
*
* Outputs the request id if specified.
*
*
*
*
*
* @author nedis
* @link https://logback.qos.ch/manual/layouts.html#conversionWord
* @since 0.7
*/
public final class PatternFormatter extends Formatter {
/**
* Default pattern.
*/
public static final String DEFAULT_PATTERN = "%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %c: %m%n";
/**
* Ignore replacement constant.
*/
public static final String IGNORE_REPLACEMENT = "ignore";
private static final String FULL_CLASS_NAME = PatternFormatter.class.getName();
private final List> biConsumers;
private final Supplier messageBuilderSupplier;
private static String resolvePattern() {
return Optional.ofNullable(getLogManager().getProperty(Formats.format("?.pattern", FULL_CLASS_NAME)))
.filter(value -> !value.isEmpty())
.orElse(DEFAULT_PATTERN);
}
private static boolean isSingleLineEnabled() {
return Boolean.parseBoolean(getLogManager().getProperty(Formats.format("?.singleLine", FULL_CLASS_NAME)));
}
private static String resolveReplacement() {
return getLogManager().getProperty(Formats.format("?.replacement", FULL_CLASS_NAME));
}
/**
* Creates an instance of {@link PatternFormatter} class with the specified pattern.
*
* @param pattern the specified pattern.
* @param singleLineEnabled flag that indicates that current {@link PatternFormatter} must format message as single line.
* @param replacement the specified replacement or {@code null} if must be default value
*/
public PatternFormatter(final String pattern,
final boolean singleLineEnabled,
final String replacement) {
List> biConsumers;
try {
biConsumers = new PatternFormatterBiConsumerParser().parse(pattern, singleLineEnabled);
} catch (final PatternFormatterParseException ex) {
logInternal(
Level.SEVERE,
"The '?' pattern is invalid: ?. Set '?' as pattern for all log messages!",
pattern, ex.getMessage(), DEFAULT_PATTERN
);
biConsumers = new PatternFormatterBiConsumerParser().parse(DEFAULT_PATTERN, singleLineEnabled);
}
this.biConsumers = biConsumers;
if (singleLineEnabled) {
if (replacement == null) {
// Default replacement
this.messageBuilderSupplier = ReplaceLineSeparatorMessageBuilder::new;
} else if (IGNORE_REPLACEMENT.equals(replacement)) {
this.messageBuilderSupplier = IgnoreLineSeparatorMessageBuilder::new;
} else {
this.messageBuilderSupplier = () -> new ReplaceLineSeparatorMessageBuilder(replacement);
}
} else {
this.messageBuilderSupplier = MessageBuilder::new;
}
}
/**
* Creates an instance of {@link PatternFormatter} class with the {@code pattern} and {@code singleLineEnabled} parameters
* resolved from properties file.
*
*
* If configuration file contains the following property:
*
* io.rxmicro.logger.jul.PatternFormatter.pattern=${CUSTOM_PATTERN}
*
* then {@code ${CUSTOM_PATTERN}} will be used as pattern instead of default one: '{@value #DEFAULT_PATTERN}'
*
*
* If configuration file contains the following property:
* {@code io.rxmicro.logger.jul.PatternFormatter.singleLine=true}
* then the current instance formats all messages as single line, i.e. replaces the {@code '\r\n'} or {@code '\n'}
* characters by {@code "\\n"} for Linux and Osx or {@code "\\r\\n"} for Windows string.
*
*
* If configuration file contains the following property:
* {@code io.rxmicro.logger.jul.PatternFormatter.replacement=ANY_PROVIDED_STRING}
* then the provided string is used as replacement for the {@code '\r\n'} or {@code '\n'} characters.
*/
public PatternFormatter() {
this(resolvePattern(), isSingleLineEnabled(), resolveReplacement());
}
@Override
public String format(final LogRecord logRecord) {
final MessageBuilder messageBuilder = messageBuilderSupplier.get();
for (final BiConsumer biConsumer : biConsumers) {
biConsumer.accept(messageBuilder, logRecord);
}
return messageBuilder.build();
}
@Override
public String toString() {
return "PatternFormatter{" +
"biConsumers=" + biConsumers +
'}';
}
}