Skip to content

[LOG4J2-3404] Creates default layouts using the available Configuration #756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ public String getName() {

public Layout<? extends Serializable> getOrCreateLayout() {
if (layout == null) {
return PatternLayout.createDefaultLayout();
return PatternLayout.createDefaultLayout(configuration);
}
return layout;
}

public Layout<? extends Serializable> getOrCreateLayout(final Charset charset) {
if (layout == null) {
return PatternLayout.newBuilder().withCharset(charset).build();
return PatternLayout.newBuilder().withCharset(charset).withConfiguration(configuration).build();
}
return layout;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,13 @@ protected static StatusLogger logger() {
return StatusLogger.getLogger();
}

/**
* For testing purposes.
*/
static int getManagerCount() {
return MAP.size();
}

/**
* May be overridden by managers to perform processing while the manager is being released and the
* lock is held. A timeout is passed for implementors to use as they see fit.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,8 @@ public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAp

@Override
public OutputStreamAppender build() {
final Layout<? extends Serializable> layout = getLayout();
final Layout<? extends Serializable> actualLayout = layout == null ? PatternLayout.createDefaultLayout()
: layout;
return new OutputStreamAppender(getName(), actualLayout, getFilter(), getManager(target, follow, actualLayout),
final Layout<? extends Serializable> layout = getOrCreateLayout();
return new OutputStreamAppender(getName(), layout, getFilter(), getManager(target, follow, layout),
ignoreExceptions, getPropertyArray());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
*/
package org.apache.logging.log4j.core.appender;

import java.io.Serializable;
import java.io.Writer;

import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.StringLayout;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
Expand All @@ -47,9 +49,13 @@ public static class Builder<B extends Builder<B>> extends AbstractAppender.Build

@Override
public WriterAppender build() {
final StringLayout layout = (StringLayout) getLayout();
final StringLayout actualLayout = layout != null ? layout : PatternLayout.createDefaultLayout();
return new WriterAppender(getName(), actualLayout, getFilter(), getManager(target, follow, actualLayout),
final Layout<? extends Serializable> layout = getOrCreateLayout();
if (!(layout instanceof StringLayout)) {
LOGGER.error("Layout must be a StringLayout to log to ServletContext");
return null;
}
final StringLayout stringLayout = (StringLayout) layout;
return new WriterAppender(getName(), stringLayout, getFilter(), getManager(target, follow, stringLayout),
isIgnoreExceptions(), getPropertyArray());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,12 @@ protected void doConfigure() {
setParents();
}

public static Level getDefaultLevel() {
final String levelName = PropertiesUtil.getProperties().getStringProperty(DefaultConfiguration.DEFAULT_LEVEL,
Level.ERROR.name());
return Level.valueOf(levelName);
}

protected void setToDefault() {
// LOG4J2-1176 facilitate memory leak investigation
setName(DefaultConfiguration.DEFAULT_NAME + "@" + Integer.toHexString(hashCode()));
Expand All @@ -727,11 +733,7 @@ protected void setToDefault() {
final LoggerConfig rootLoggerConfig = getRootLogger();
rootLoggerConfig.addAppender(appender, null, null);

final Level defaultLevel = Level.ERROR;
final String levelName = PropertiesUtil.getProperties().getStringProperty(DefaultConfiguration.DEFAULT_LEVEL,
defaultLevel.name());
final Level level = Level.valueOf(levelName);
rootLoggerConfig.setLevel(level != null ? level : defaultLevel);
rootLoggerConfig.setLevel(getDefaultLevel());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public AbstractLayout(final byte[] header, final byte[] footer) {
* Constructs a layout with an optional header and footer.
*
* @param configuration
* The configuration
* The configuration. May be null.
* @param header
* The header to include when the stream is opened. May be null.
* @param footer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.StringLayout;
import org.apache.logging.log4j.core.config.AbstractConfiguration;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
Expand Down Expand Up @@ -176,7 +179,7 @@ protected AbstractStringLayout(final Charset aCharset, final byte[] header, fina

/**
* Builds a new layout.
* @param config the configuration
* @param config the configuration. May be null.
* @param aCharset the charset used to encode the header bytes, footer bytes and anything else that needs to be
* converted from strings to bytes.
* @param headerSerializer the header bytes serializer
Expand Down Expand Up @@ -264,10 +267,19 @@ protected String serializeToString(final Serializer serializer) {
if (serializer == null) {
return null;
}
final LoggerConfig rootLogger = getConfiguration().getRootLogger();
final String loggerName;
final Level level;
if (configuration != null) {
final LoggerConfig rootLogger = configuration.getRootLogger();
loggerName = rootLogger.getName();
level = rootLogger.getLevel();
} else {
loggerName = LogManager.ROOT_LOGGER_NAME;
level = AbstractConfiguration.getDefaultLevel();
}
// Using "" for the FQCN, does it matter?
final LogEvent logEvent = getLogEventFactory().createEvent(rootLogger.getName(), null, Strings.EMPTY,
rootLogger.getLevel(), null, null, null);
final LogEvent logEvent = getLogEventFactory().createEvent(loggerName, null, Strings.EMPTY,
level, null, null, null);
return serializer.toSerializable(logEvent);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.DefaultConfiguration;
import org.apache.logging.log4j.core.config.Node;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Expand Down Expand Up @@ -243,7 +242,7 @@ private StringBuilder toText(final Serializer2 serializer, final LogEvent event,

/**
* Creates a PatternParser.
* @param config The Configuration.
* @param config The Configuration or {@code null}.
* @return The PatternParser.
*/
public static PatternParser createPatternParser(final Configuration config) {
Expand Down Expand Up @@ -763,10 +762,7 @@ public Builder withFooter(final String footer) {

@Override
public PatternLayout build() {
// fall back to DefaultConfiguration
if (configuration == null) {
configuration = new DefaultConfiguration();
}
// should work with a null configuration
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires more explanations: the PatternParser does not use a configuration directly (or has null checks), but allows one to be injected into the PatternConverters.

Looking at the available PatternConverters, the color converters, "enc", "equals*", "highlight", "maxLength", "replace", "style", "ex*" require a configuration to create a PatternParser.

I didn't find any converters except LiteralPatternConverter that actually use Configuration. LiteralPatternConverter has appropriate null checks.

return new PatternLayout(configuration, regexReplacement, pattern, patternSelector, charset,
alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi, header, footer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ public PatternParser(final String converterKey) {
* Constructor.
*
* @param config
* The current Configuration.
* The current Configuration or {@code null}.
* @param converterKey
* The key to lookup the converters.
* @param expected
* The expected base Class of each Converter.
* The expected base Class of each Converter or {@code null}.
*/
public PatternParser(final Configuration config, final String converterKey, final Class<?> expected) {
this(config, converterKey, expected, null);
Expand All @@ -117,13 +117,13 @@ public PatternParser(final Configuration config, final String converterKey, fina
* Constructor.
*
* @param config
* The current Configuration.
* The current Configuration or {@code null}.
* @param converterKey
* The key to lookup the converters.
* @param expectedClass
* The expected base Class of each Converter.
* The expected base Class of each Converter or {@code null}.
* @param filterClass
* Filter the returned plugins after calling the plugin manager.
* Filter the returned plugins after calling the plugin manager, can be {@code null}.
*/
public PatternParser(final Configuration config, final String converterKey, final Class<?> expectedClass,
final Class<?> filterClass) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*/
package org.apache.logging.log4j.core.appender;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.DefaultConfiguration;
import org.junit.jupiter.api.Test;

public class AbstractAppenderBuilderTest {

@Test
public void testDefaultLayoutLeak() {
int expected = AbstractManager.getManagerCount();
final Configuration configuration = new DefaultConfiguration();
ConsoleAppender appender = ConsoleAppender.newBuilder().setConfiguration(configuration).setName("test").build();
configuration.addAppender(appender);
configuration.start();
configuration.stop();
assertEquals(expected, AbstractManager.getManagerCount(), "No managers should be left after shutdown.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,33 @@
* limitations under the license.
*/

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import java.nio.charset.Charset;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.DefaultConfiguration;
import org.apache.logging.log4j.core.layout.AbstractStringLayout.Serializer;
import org.apache.logging.log4j.core.layout.PatternLayout.SerializerBuilder;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

/**
* Tests AbstractStringLayout.
*/
public class AbstractStringLayoutTest {

// Configuration explicitly null
private static final Serializer serializer = new SerializerBuilder().setPattern(DefaultConfiguration.DEFAULT_PATTERN).build();

static class ConcreteStringLayout extends AbstractStringLayout {
public static int DEFAULT_STRING_BUILDER_SIZE = AbstractStringLayout.DEFAULT_STRING_BUILDER_SIZE;
public static int MAX_STRING_BUILDER_SIZE = AbstractStringLayout.MAX_STRING_BUILDER_SIZE;

public ConcreteStringLayout() {
super(Charset.defaultCharset());
// Configuration explicitly null
super(null, Charset.defaultCharset(), serializer, serializer);
}

public static StringBuilder getStringBuilder() {
Expand Down Expand Up @@ -76,4 +86,14 @@ public void testGetStringBuilderCapacityRestrictedToMax() throws Exception {
"capacity, trimmed to MAX_STRING_BUILDER_SIZE");
assertEquals(0, sb3.length(), "empty, ready for use");
}

@Test
public void testNullConfigurationIsAllowed() {
try {
final ConcreteStringLayout layout = new ConcreteStringLayout();
layout.serializeToString(serializer);
} catch (NullPointerException e) {
fail(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.StringLayout;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.layout.AbstractStringLayout;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.web.WebLoggerContextUtils;

/**
Expand All @@ -55,10 +55,8 @@ public ServletAppender build() {
LOGGER.error("No servlet context is available");
return null;
}
Layout<? extends Serializable> layout = getLayout();
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
} else if (!(layout instanceof AbstractStringLayout)) {
Layout<? extends Serializable> layout = getOrCreateLayout();
if (!(layout instanceof StringLayout)) {
LOGGER.error("Layout must be a StringLayout to log to ServletContext");
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.StringLayout;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.layout.AbstractStringLayout;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.web.WebLoggerContextUtils;

/**
Expand All @@ -55,10 +55,8 @@ public ServletAppender build() {
LOGGER.error("No servlet context is available");
return null;
}
Layout<? extends Serializable> layout = getLayout();
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
} else if (!(layout instanceof AbstractStringLayout)) {
final Layout<? extends Serializable> layout = getOrCreateLayout();
if (!(layout instanceof StringLayout)) {
LOGGER.error("Layout must be a StringLayout to log to ServletContext");
return null;
}
Expand Down