Capture Log4j 2.x events (#182)
This commit is contained in:
parent
a57bfcf40e
commit
da7dc5412e
|
@ -0,0 +1,41 @@
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group = 'org.apache.logging.log4j'
|
||||||
|
module = 'log4j-core'
|
||||||
|
versions = '(,)'
|
||||||
|
}
|
||||||
|
|
||||||
|
pass {
|
||||||
|
group = 'org.apache.logging.log4j'
|
||||||
|
module = 'log4j-api'
|
||||||
|
versions = '(,)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "${rootDir}/gradle/java.gradle"
|
||||||
|
|
||||||
|
apply plugin: 'org.unbroken-dome.test-sets'
|
||||||
|
|
||||||
|
testSets {
|
||||||
|
latestDepTest {
|
||||||
|
dirName = 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
// In order to test the real log4j library we need to remove the log4j transitive
|
||||||
|
// dependency 'log4j-over-slf4j' brought in by :testing which would shadow
|
||||||
|
// the log4j module under test using a proxy to slf4j instead.
|
||||||
|
testCompile.exclude group: 'org.slf4j', module: 'log4j-over-slf4j'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.0'
|
||||||
|
compileOnly group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.0'
|
||||||
|
|
||||||
|
testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.0'
|
||||||
|
testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.0'
|
||||||
|
|
||||||
|
latestDepTestCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '+'
|
||||||
|
latestDepTestCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '+'
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package io.opentelemetry.auto.instrumentation.log4jevents.v2_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.apache.logging.log4j.Level;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.logging.log4j.message.Message;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class Log4jEventInstrumentation extends Instrumenter.Default {
|
||||||
|
public Log4jEventInstrumentation() {
|
||||||
|
super("log4j-events");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return safeHasSuperType(named("org.apache.logging.log4j.spi.AbstractLogger"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {packageName + ".Log4jEvents"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||||
|
transformers.put(
|
||||||
|
isMethod()
|
||||||
|
.and(isPublic())
|
||||||
|
.and(named("logMessage"))
|
||||||
|
.and(takesArguments(5))
|
||||||
|
.and(takesArgument(0, named("java.lang.String")))
|
||||||
|
.and(takesArgument(1, named("org.apache.logging.log4j.Level")))
|
||||||
|
.and(takesArgument(2, named("org.apache.logging.log4j.Marker")))
|
||||||
|
.and(takesArgument(3, named("org.apache.logging.log4j.message.Message")))
|
||||||
|
.and(takesArgument(4, named("java.lang.Throwable"))),
|
||||||
|
Log4jEventInstrumentation.class.getName() + "$LogMessageAdvice");
|
||||||
|
// log4j 2.12.1 introduced and started using this new log() method
|
||||||
|
transformers.put(
|
||||||
|
isMethod()
|
||||||
|
.and(isProtected())
|
||||||
|
.and(named("log"))
|
||||||
|
.and(takesArguments(6))
|
||||||
|
.and(takesArgument(0, named("org.apache.logging.log4j.Level")))
|
||||||
|
.and(takesArgument(1, named("org.apache.logging.log4j.Marker")))
|
||||||
|
.and(takesArgument(2, named("java.lang.String")))
|
||||||
|
.and(takesArgument(3, named("java.lang.StackTraceElement")))
|
||||||
|
.and(takesArgument(4, named("org.apache.logging.log4j.message.Message")))
|
||||||
|
.and(takesArgument(5, named("java.lang.Throwable"))),
|
||||||
|
Log4jEventInstrumentation.class.getName() + "$LogAdvice");
|
||||||
|
return transformers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LogMessageAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void methodEnter(
|
||||||
|
@Advice.This final Logger logger,
|
||||||
|
@Advice.Argument(1) final Level level,
|
||||||
|
@Advice.Argument(3) final Message message,
|
||||||
|
@Advice.Argument(4) final Throwable t) {
|
||||||
|
Log4jEvents.capture(logger, level, message, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LogAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void methodEnter(
|
||||||
|
@Advice.This final Logger logger,
|
||||||
|
@Advice.Argument(0) final Level level,
|
||||||
|
@Advice.Argument(4) final Message message,
|
||||||
|
@Advice.Argument(5) final Throwable t) {
|
||||||
|
Log4jEvents.capture(logger, level, message, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package io.opentelemetry.auto.instrumentation.log4jevents.v2_0;
|
||||||
|
|
||||||
|
import io.opentelemetry.OpenTelemetry;
|
||||||
|
import io.opentelemetry.auto.config.Config;
|
||||||
|
import io.opentelemetry.trace.AttributeValue;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import io.opentelemetry.trace.Tracer;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.logging.log4j.Level;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.logging.log4j.message.Message;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class Log4jEvents {
|
||||||
|
|
||||||
|
private static final Tracer TRACER =
|
||||||
|
OpenTelemetry.getTracerFactory().get("io.opentelemetry.auto.log4j-events-2.0");
|
||||||
|
|
||||||
|
public static void capture(
|
||||||
|
final Logger logger, final Level level, final Message message, final Throwable t) {
|
||||||
|
|
||||||
|
if (level.intLevel() > getThreshold().intLevel()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Span currentSpan = TRACER.getCurrentSpan();
|
||||||
|
if (!currentSpan.getContext().isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, AttributeValue> attributes = new HashMap<>(t == null ? 2 : 3);
|
||||||
|
attributes.put("level", newAttributeValue(level.toString()));
|
||||||
|
attributes.put("loggerName", newAttributeValue(logger.getName()));
|
||||||
|
if (t != null) {
|
||||||
|
attributes.put("error.stack", newAttributeValue(toString(t)));
|
||||||
|
}
|
||||||
|
currentSpan.addEvent(message.getFormattedMessage(), attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AttributeValue newAttributeValue(final String stringValue) {
|
||||||
|
return AttributeValue.stringAttributeValue(stringValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toString(final Throwable t) {
|
||||||
|
final StringWriter out = new StringWriter();
|
||||||
|
t.printStackTrace(new PrintWriter(out));
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Level getThreshold() {
|
||||||
|
final String level = Config.get().getLogsEventsThreshold();
|
||||||
|
if (level == null) {
|
||||||
|
return Level.OFF;
|
||||||
|
}
|
||||||
|
switch (level) {
|
||||||
|
case "OFF":
|
||||||
|
return Level.OFF;
|
||||||
|
case "FATAL":
|
||||||
|
return Level.FATAL;
|
||||||
|
case "ERROR":
|
||||||
|
case "SEVERE":
|
||||||
|
return Level.ERROR;
|
||||||
|
case "WARN":
|
||||||
|
case "WARNING":
|
||||||
|
return Level.WARN;
|
||||||
|
case "INFO":
|
||||||
|
return Level.INFO;
|
||||||
|
case "CONFIG":
|
||||||
|
case "DEBUG":
|
||||||
|
case "FINE":
|
||||||
|
case "FINER":
|
||||||
|
return Level.DEBUG;
|
||||||
|
case "TRACE":
|
||||||
|
case "FINEST":
|
||||||
|
return Level.TRACE;
|
||||||
|
case "ALL":
|
||||||
|
return Level.ALL;
|
||||||
|
default:
|
||||||
|
log.error("unexpected value for {}: {}", Config.LOGS_EVENTS_THRESHOLD, level);
|
||||||
|
return Level.OFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import io.opentelemetry.auto.test.log.events.LogEventsTestBase
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
|
||||||
|
class Log4jEventTest extends LogEventsTestBase {
|
||||||
|
|
||||||
|
static {
|
||||||
|
// need to initialize logger before tests to flush out init warning message:
|
||||||
|
// "Unable to instantiate org.fusesource.jansi.WindowsAnsiOutputStream"
|
||||||
|
LogManager.getLogger(Log4jEventTest)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Object createLogger(String name) {
|
||||||
|
LogManager.getLogger(name)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE Configuration>
|
||||||
|
<Configuration status="WARN">
|
||||||
|
<Appenders>
|
||||||
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
|
||||||
|
</Console>
|
||||||
|
</Appenders>
|
||||||
|
<Loggers>
|
||||||
|
<Root level="warn">
|
||||||
|
<AppenderRef ref="Console" />
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
|
@ -102,6 +102,7 @@ include ':instrumentation:lettuce-5.0'
|
||||||
include ':instrumentation:log4j-events-1.1'
|
include ':instrumentation:log4j-events-1.1'
|
||||||
// FIXME this instrumentation relied on scope listener
|
// FIXME this instrumentation relied on scope listener
|
||||||
// include ':instrumentation:log4j1'
|
// include ':instrumentation:log4j1'
|
||||||
|
include ':instrumentation:log4j-events-2.0'
|
||||||
// FIXME this instrumentation relied on scope listener
|
// FIXME this instrumentation relied on scope listener
|
||||||
// include ':instrumentation:log4j2'
|
// include ':instrumentation:log4j2'
|
||||||
include ':instrumentation:mongo'
|
include ':instrumentation:mongo'
|
||||||
|
|
Loading…
Reference in New Issue