Skip to content

Commit 30e44c9

Browse files
Merge pull request #71 from SAP/issue/68
Dynamic Log Level configuration for specific Packages
2 parents a2cdde2 + 488a00a commit 30e44c9

File tree

10 files changed

+445
-99
lines changed

10 files changed

+445
-99
lines changed

cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/helper/DynamicLogLevelHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ public class DynamicLogLevelHelper {
77
* from mdc
88
*/
99
public static final String MDC_DYNAMIC_LOG_LEVEL_KEY = "dynamic_log_level";
10+
public static final String MDC_DYNAMIC_LOG_LEVEL_PREFIXES = "dynamic_log_level_prefixes";
1011

1112
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.sap.hcp.cf.log4j2.filter;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
import org.apache.logging.log4j.Level;
5+
import org.apache.logging.log4j.Marker;
6+
import org.apache.logging.log4j.core.LogEvent;
7+
import org.apache.logging.log4j.core.Logger;
8+
import org.apache.logging.log4j.core.config.plugins.Plugin;
9+
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
10+
import org.apache.logging.log4j.core.filter.AbstractFilter;
11+
import org.apache.logging.log4j.message.Message;
12+
import org.apache.logging.log4j.util.ReadOnlyStringMap;
13+
import org.slf4j.MDC;
14+
15+
import com.sap.hcp.cf.logging.common.helper.DynamicLogLevelHelper;
16+
17+
@Plugin(name = "DynamicLevelPrefixLoggerFilter", category = "Core", elementType = "filter", printObject = true)
18+
public class DynamicLevelPrefixLoggerFilter extends AbstractFilter {
19+
20+
@Override
21+
public Result filter(LogEvent event) {
22+
Level level = event.getLevel();
23+
Level dynamicLevel = getDynamicLevel(event);
24+
String loggerFqcn = event.getLoggerFqcn();
25+
String logLevelPackages = getDynamicPackages(event);
26+
return filter(level, dynamicLevel, loggerFqcn, logLevelPackages);
27+
}
28+
29+
private Level getDynamicLevel(LogEvent event) {
30+
String logLevel = getContextValue(event, DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_KEY);
31+
return StringUtils.isNotBlank(logLevel) ? Level.getLevel(logLevel) : null;
32+
}
33+
34+
private String getContextValue(LogEvent event, String key) {
35+
ReadOnlyStringMap contextData = event.getContextData();
36+
return contextData != null ? contextData.getValue(key) : null;
37+
}
38+
39+
private String getDynamicPackages(final LogEvent event) {
40+
String logLevelPackages = getContextValue(event, DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES);
41+
return logLevelPackages;
42+
}
43+
44+
private Result filter(Level level, Level dynamicLevel, String loggerFqcn, String logLevelPackages) {
45+
if (dynamicLevel != null && level.isMoreSpecificThan(dynamicLevel)
46+
&& checkPackages(loggerFqcn, logLevelPackages)) {
47+
return Result.ACCEPT;
48+
}
49+
return Result.NEUTRAL;
50+
}
51+
52+
private boolean checkPackages(String loggerFqcn, String logLevelPackages) {
53+
if (StringUtils.isNotBlank(logLevelPackages)) {
54+
for (String current : logLevelPackages.split(",")) {
55+
if (loggerFqcn.startsWith(current)) {
56+
return true;
57+
}
58+
}
59+
}
60+
return false;
61+
}
62+
63+
64+
@Override
65+
public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
66+
final Throwable t) {
67+
return filter(level, getMdcLevel(), logger.getName(), getMdcPackages());
68+
}
69+
70+
@Override
71+
public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
72+
final Throwable t) {
73+
return filter(level, getMdcLevel(), logger.getName(), getMdcPackages());
74+
}
75+
76+
@Override
77+
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
78+
final Object... params) {
79+
return filter(level, getMdcLevel(), logger.getName(), getMdcPackages());
80+
}
81+
82+
private Level getMdcLevel() {
83+
String mdcLevel = MDC.get(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_KEY);
84+
return StringUtils.isNotBlank(mdcLevel) ? Level.getLevel(mdcLevel) : null;
85+
}
86+
87+
private String getMdcPackages() {
88+
return MDC.get(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES);
89+
}
90+
91+
@PluginFactory
92+
public static DynamicLevelPrefixLoggerFilter createFilter() {
93+
return new DynamicLevelPrefixLoggerFilter();
94+
}
95+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.sap.hcp.cf.log4j2.filter;
2+
3+
import static org.hamcrest.Matchers.is;
4+
import static org.junit.Assert.assertThat;
5+
6+
import java.net.URL;
7+
import java.util.HashMap;
8+
9+
import org.apache.logging.log4j.Level;
10+
import org.apache.logging.log4j.core.Filter.Result;
11+
import org.apache.logging.log4j.core.LoggerContext;
12+
import org.apache.logging.log4j.core.appender.CountingNoOpAppender;
13+
import org.apache.logging.log4j.core.config.ConfigurationSource;
14+
import org.apache.logging.log4j.core.config.xml.XmlConfiguration;
15+
import org.apache.logging.log4j.core.impl.JdkMapAdapterStringMap;
16+
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
17+
import org.junit.Test;
18+
import org.slf4j.MDC;
19+
20+
import com.sap.hcp.cf.logging.common.helper.DynamicLogLevelHelper;
21+
22+
public class DynamicLevelPrefixLoggerFilterTest {
23+
24+
private static final String KNOWN_PREFIX = "known.prefix";
25+
private static final String UNKNOWN_PREFIX = "unknown.prefix";
26+
27+
private final JdkMapAdapterStringMap contextData;
28+
private final DynamicLevelPrefixLoggerFilter filter = new DynamicLevelPrefixLoggerFilter();
29+
30+
public DynamicLevelPrefixLoggerFilterTest() {
31+
HashMap<String, String> customMDC = new HashMap<>();
32+
customMDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_KEY, "DEBUG");
33+
customMDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES, KNOWN_PREFIX);
34+
this.contextData = new JdkMapAdapterStringMap(customMDC);
35+
}
36+
37+
@Test
38+
public void acceptsOnKnownPackage() throws Exception {
39+
Log4jLogEvent event = Log4jLogEvent.newBuilder().setLoggerFqcn(KNOWN_PREFIX + "acceptsOnKnownPackage")
40+
.setContextData(contextData).setLevel(Level.INFO).build();
41+
assertThat(filter.filter(event), is(Result.ACCEPT));
42+
}
43+
44+
@Test
45+
public void neutralOnUnknownPackage() throws Exception {
46+
Log4jLogEvent event = Log4jLogEvent.newBuilder().setLoggerFqcn(UNKNOWN_PREFIX + "neutralOnUnknownPackage")
47+
.setContextData(contextData).setLevel(Level.INFO).build();
48+
assertThat(filter.filter(event), is(Result.NEUTRAL));
49+
}
50+
51+
@Test
52+
public void neutralOnLowerLevel() throws Exception {
53+
Log4jLogEvent event = Log4jLogEvent.newBuilder().setLoggerFqcn(KNOWN_PREFIX + "neutralOnUnknownPackage")
54+
.setContextData(contextData).setLevel(Level.TRACE).build();
55+
assertThat(filter.filter(event), is(Result.NEUTRAL));
56+
}
57+
58+
@Test
59+
public void neutralOnUnconfiguredLevelKey() throws Exception {
60+
HashMap<String, String> customMDC = new HashMap<>();
61+
customMDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES, KNOWN_PREFIX);
62+
JdkMapAdapterStringMap missingLevelKey = new JdkMapAdapterStringMap(customMDC);
63+
Log4jLogEvent event = Log4jLogEvent.newBuilder().setLoggerFqcn(KNOWN_PREFIX + "neutralOnUnknownPackage")
64+
.setContextData(missingLevelKey).setLevel(Level.INFO).build();
65+
assertThat(filter.filter(event), is(Result.NEUTRAL));
66+
}
67+
68+
@Test
69+
public void neutralOnUnconfiguredPrefix() throws Exception {
70+
HashMap<String, String> customMDC = new HashMap<>();
71+
customMDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES, KNOWN_PREFIX);
72+
JdkMapAdapterStringMap missingPrefixes = new JdkMapAdapterStringMap(customMDC);
73+
Log4jLogEvent event = Log4jLogEvent.newBuilder().setLoggerFqcn(KNOWN_PREFIX + "neutralOnUnknownPackage")
74+
.setContextData(missingPrefixes).setLevel(Level.INFO).build();
75+
assertThat(filter.filter(event), is(Result.NEUTRAL));
76+
}
77+
78+
@Test
79+
public void integratesIntoConfiguration() throws Exception {
80+
81+
LoggerContext loggerContext = new LoggerContext("integratesIntoConfiguration");
82+
URL configLocation = getClass().getResource("log4j2-test.xml");
83+
ConfigurationSource configurationSource = ConfigurationSource.fromUri(configLocation.toURI());
84+
XmlConfiguration configuration = new XmlConfiguration(loggerContext, configurationSource);
85+
loggerContext.start(configuration);
86+
org.apache.logging.log4j.core.Logger logger = loggerContext
87+
.getLogger(KNOWN_PREFIX + "integratesIntoConfiguration");
88+
CountingNoOpAppender appender = new CountingNoOpAppender("integratesIntoConfiguration", null);
89+
logger.addAppender(appender);
90+
logger.debug("test-integration-message-at-debug");
91+
assertThat(appender.getCount(), is(0L));
92+
logger.info("test-integration-message-at-info");
93+
assertThat(appender.getCount(), is(1L));
94+
MDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_KEY, "DEBUG");
95+
MDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES, KNOWN_PREFIX);
96+
logger.debug("test-integration-message-at-debug");
97+
assertThat(appender.getCount(), is(2L));
98+
loggerContext.close();
99+
}
100+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration status="warn" strict="true"
3+
packages="com.sap.hcp.cf.log4j2.converter,com.sap.hcp.cf.log4j2">
4+
<DynamicLevelPrefixLoggerFilter />
5+
<Appenders>
6+
<Console name="Console" target="SYSTEM_OUT">
7+
<PatternLayout
8+
pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
9+
</Console>
10+
</Appenders>
11+
<Loggers>
12+
<Root level="info">
13+
<AppenderRef ref="Console" />
14+
</Root>
15+
</Loggers>
16+
</Configuration>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.sap.hcp.cf.logback.filter;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
import org.slf4j.MDC;
5+
import org.slf4j.Marker;
6+
7+
import com.sap.hcp.cf.logging.common.helper.DynamicLogLevelHelper;
8+
9+
import ch.qos.logback.classic.Level;
10+
import ch.qos.logback.classic.Logger;
11+
import ch.qos.logback.classic.turbo.TurboFilter;
12+
import ch.qos.logback.core.spi.FilterReply;
13+
14+
public class DynamicLevelPrefixLoggerTurboFilter extends TurboFilter {
15+
16+
@Override
17+
public FilterReply decide(final Marker marker, final Logger logger, final Level level, final String format,
18+
final Object[] params, final Throwable t) {
19+
final String logLevel = MDC.get(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_KEY);
20+
if (logLevel != null && level.isGreaterOrEqual(Level.toLevel(logLevel)) && checkPackages(logger)) {
21+
return FilterReply.ACCEPT;
22+
}
23+
return FilterReply.NEUTRAL;
24+
}
25+
26+
private boolean checkPackages(final Logger logger) {
27+
final String logLevelPackages = MDC.get(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES);
28+
if (StringUtils.isNotBlank(logLevelPackages)) {
29+
for (String current : logLevelPackages.split(",")) {
30+
if (logger.getName().startsWith(current)) {
31+
return true;
32+
}
33+
}
34+
}
35+
return false;
36+
}
37+
38+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.sap.hcp.cf.logback.filter;
2+
3+
import static org.hamcrest.CoreMatchers.equalTo;
4+
import static org.hamcrest.Matchers.hasSize;
5+
import static org.hamcrest.Matchers.is;
6+
import static org.junit.Assert.assertThat;
7+
8+
import org.junit.Before;
9+
import org.junit.Test;
10+
import org.slf4j.MDC;
11+
12+
import com.sap.hcp.cf.logging.common.helper.DynamicLogLevelHelper;
13+
14+
import ch.qos.logback.classic.Level;
15+
import ch.qos.logback.classic.Logger;
16+
import ch.qos.logback.classic.LoggerContext;
17+
import ch.qos.logback.classic.spi.ILoggingEvent;
18+
import ch.qos.logback.classic.turbo.TurboFilter;
19+
import ch.qos.logback.core.read.ListAppender;
20+
import ch.qos.logback.core.spi.FilterReply;
21+
22+
public class DynamicLevelPrefixLoggerTurboFilterTest {
23+
24+
private static final String KNOWN_PREFIX = "known.prefix";
25+
private static final String UNKNOWN_PREFIX = "unknown.prefix";
26+
27+
private LoggerContext loggerContext = new LoggerContext();
28+
private TurboFilter filter = new DynamicLevelPrefixLoggerTurboFilter();
29+
30+
@Before
31+
public void setUp() {
32+
MDC.clear();
33+
MDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_KEY, "DEBUG");
34+
MDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES, KNOWN_PREFIX);
35+
loggerContext.addTurboFilter(filter);
36+
}
37+
38+
@Test
39+
public void acceptsOnKnownPackage() throws Exception {
40+
Logger logger = loggerContext.getLogger(KNOWN_PREFIX + "acceptsOnKnownPackage");
41+
assertThat(filter.decide(null, logger, Level.INFO, null, null, null), is(FilterReply.ACCEPT));
42+
}
43+
44+
@Test
45+
public void neutralOnUnknownPackage() throws Exception {
46+
Logger logger = loggerContext.getLogger(UNKNOWN_PREFIX + "neutralOnUnknownPackage");
47+
assertThat(filter.decide(null, logger, Level.INFO, null, null, null), is(FilterReply.NEUTRAL));
48+
}
49+
50+
@Test
51+
public void neutralOnLowerLevel() throws Exception {
52+
Logger logger = loggerContext.getLogger(KNOWN_PREFIX + "neutralOnUnknownPackage");
53+
assertThat(filter.decide(null, logger, Level.TRACE, null, null, null), is(FilterReply.NEUTRAL));
54+
}
55+
56+
@Test
57+
public void neutralOnUnconfiguredLevelKey() throws Exception {
58+
MDC.remove(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_KEY);
59+
Logger logger = loggerContext.getLogger(KNOWN_PREFIX + "neutralOnUnknownPackage");
60+
assertThat(filter.decide(null, logger, Level.INFO, null, null, null), is(FilterReply.NEUTRAL));
61+
}
62+
63+
@Test
64+
public void neutralOnUnconfiguredPrefix() throws Exception {
65+
MDC.remove(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES);
66+
Logger logger = loggerContext.getLogger(KNOWN_PREFIX + "neutralOnUnknownPackage");
67+
assertThat(filter.decide(null, logger, Level.INFO, null, null, null), is(FilterReply.NEUTRAL));
68+
}
69+
70+
@Test
71+
public void integratesIntoConfiguration() throws Exception {
72+
Logger logger = loggerContext.getLogger(KNOWN_PREFIX + "integratesIntoConfiguration");
73+
ListAppender<ILoggingEvent> appender = new ListAppender<ILoggingEvent>();
74+
appender.start();
75+
logger.addAppender(appender);
76+
logger.info("test-integration-message");
77+
assertThat(appender.list, hasSize(1));
78+
assertThat(appender.list.get(0).getMessage(), is(equalTo("test-integration-message")));
79+
appender.stop();
80+
}
81+
}

cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/dynlog/DynamicLogLevelProcessor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ public void copyDynamicLogLevelToMDC(HttpServletRequest httpRequest) {
3939
try {
4040
DecodedJWT jwt = tokenDecoder.validateAndDecodeToken(logLevelToken);
4141
String dynamicLogLevel = jwt.getClaim("level").asString();
42+
String packages = jwt.getClaim("packages").asString();
4243
if (ALLOWED_DYNAMIC_LOGLEVELS.contains(dynamicLogLevel)) {
4344
MDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_KEY, dynamicLogLevel);
45+
MDC.put(DynamicLogLevelHelper.MDC_DYNAMIC_LOG_LEVEL_PREFIXES, packages);
4446
} else {
4547
throw new DynamicLogLevelException("Dynamic Log-Level [" + dynamicLogLevel +
4648
"] provided in header is not valid. Allowed Values are " +

0 commit comments

Comments
 (0)