diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index a8d3b11625..67d173dfb8 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -27,6 +27,13 @@ endif::[] ===== Potentially breaking changes * Create the JDBC spans as exit spans- {pull}2484[#2484] +[float] +===== Refactorings +* Logging frameworks instrumentations - {pull}2428[#2428]. This refactoring includes: +** Log correlation now works based on instrumentation rather than `ActivationListener` that directly updates the MDC +** Merging the different instrumentations (log-correlation, error-capturing and ECS-reformatting) into a single plugin +** Module structure and package naming changes + [float] ===== Features * Added support for setting service name and version for a transaction via the public api - {pull}2451[#2451] @@ -37,6 +44,10 @@ endif::[] * Added support for collecting statistics about dropped exit spans - {pull}2505[#2505] * Making AWS Lambda instrumentation GA - includes some changes in Lambda transaction metadata fields and a dedicated flush HTTP request to the AWS Lambda extension - {pull}2424[#2424] +* Changed logging correlation to be on by default. This change includes the removal of the now redundant `enable_log_correlation` config +option. If there's a need to disable the log correlation mechanism, this can be done now through the `disable_instrumentations` config - +{pull}2428[#2428] +* Added automatic error event capturing for log4j1 and JBoss LogManager - {pull}2428[#2428] [float] ===== Performance improvements diff --git a/apm-agent-benchmarks/pom.xml b/apm-agent-benchmarks/pom.xml index 722c484829..99d049a3e0 100644 --- a/apm-agent-benchmarks/pom.xml +++ b/apm-agent-benchmarks/pom.xml @@ -42,11 +42,6 @@ apm-servlet-plugin ${project.version} - - ${project.groupId} - apm-log-correlation-plugin - ${project.version} - ${project.groupId} apm-lettuce-plugin diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index a1dbe47967..69dc2899e3 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -22,13 +22,14 @@ import co.elastic.apm.agent.bci.classloading.IndyPluginClassLoader; import co.elastic.apm.agent.bci.classloading.LookupExposer; import co.elastic.apm.agent.common.JvmRuntimeInfo; +import co.elastic.apm.agent.sdk.logging.Logger; +import co.elastic.apm.agent.sdk.logging.LoggerFactory; +import co.elastic.apm.agent.sdk.state.CallDepth; import co.elastic.apm.agent.sdk.state.GlobalState; import co.elastic.apm.agent.util.PackageScanner; import net.bytebuddy.asm.Advice; import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.loading.ClassInjector; -import co.elastic.apm.agent.sdk.logging.Logger; -import co.elastic.apm.agent.sdk.logging.LoggerFactory; import org.stagemonitor.configuration.ConfigurationOptionProvider; import org.stagemonitor.util.IOUtils; @@ -211,6 +212,8 @@ public class IndyBootstrap { @Nullable static Method indyBootstrapMethod; + private static final CallDepth callDepth = CallDepth.get(IndyBootstrap.class); + public static Method getIndyBootstrapMethod(final Logger logger) { if (indyBootstrapMethod != null) { return indyBootstrapMethod; @@ -356,6 +359,14 @@ public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, MethodType adviceMethodType, Object... args) { try { + if (callDepth.isNestedCallAndIncrement()) { + // avoid re-entrancy and stack overflow errors + // may happen when bootstrapping an instrumentation that also gets triggered during the bootstrap + // for example, adding correlation ids to the thread context when executing logger.debug. + // We cannot use a static logger field as it would initialize logging before it's ready + LoggerFactory.getLogger(IndyBootstrap.class).warn("Nested instrumented invokedynamic instruction linkage detected", new Throwable()); + return null; + } String adviceClassName = (String) args[0]; int enter = (Integer) args[1]; Class instrumentedType = (Class) args[2]; @@ -411,6 +422,8 @@ public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, // must not be a static field as it would initialize logging before it's ready LoggerFactory.getLogger(IndyBootstrap.class).error(e.getMessage(), e); return null; + } finally { + callDepth.decrement(); } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ActivationListener.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ActivationListener.java index 8ba358454e..4461a537e9 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ActivationListener.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ActivationListener.java @@ -18,11 +18,10 @@ */ package co.elastic.apm.agent.impl; -import co.elastic.apm.agent.impl.error.ErrorCapture; import co.elastic.apm.agent.impl.transaction.AbstractSpan; /** - * A callback for {@link AbstractSpan} and {@link ErrorCapture} activation and deactivation events + * A callback for {@link AbstractSpan} activation and deactivation events *

* The constructor can optionally have a {@link ElasticApmTracer} parameter. *

@@ -37,14 +36,6 @@ public interface ActivationListener { */ void beforeActivate(AbstractSpan span) throws Throwable; - /** - * A callback for {@link ErrorCapture#activate()} - * - * @param error the {@link ErrorCapture} that is being activated - * @throws Throwable if there was an error while calling this method - */ - void beforeActivate(ErrorCapture error) throws Throwable; - /** * A callback for {@link AbstractSpan#deactivate()} *

@@ -56,12 +47,4 @@ public interface ActivationListener { * @throws Throwable if there was an error while calling this method */ void afterDeactivate(AbstractSpan deactivatedSpan) throws Throwable; - - /** - * A callback for {@link ErrorCapture#deactivate()} - * - * @param deactivatedError the error that has just been deactivated - * @throws Throwable if there was an error while calling this method - */ - void afterDeactivate(ErrorCapture deactivatedError) throws Throwable; } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java index 43abf0fc49..9be39a785b 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java @@ -176,7 +176,7 @@ public Transaction startRootTransaction(@Nullable ClassLoader initiatingClassLoa public Transaction startRootTransaction(Sampler sampler, long epochMicros, @Nullable ClassLoader initiatingClassLoader) { Transaction transaction = null; if (isRunning()) { - transaction = createTransaction().start(TraceContext.asRoot(), null, epochMicros, sampler, initiatingClassLoader); + transaction = createTransaction().start(TraceContext.asRoot(), null, epochMicros, sampler); afterTransactionStart(initiatingClassLoader, transaction); } return transaction; @@ -201,7 +201,7 @@ public Transaction startChildTransaction(@Nullable C headerCarrier, TextHead Transaction transaction = null; if (isRunning()) { transaction = createTransaction().start(TraceContext.getFromTraceContextTextHeaders(), headerCarrier, - textHeadersGetter, epochMicros, sampler, initiatingClassLoader); + textHeadersGetter, epochMicros, sampler); afterTransactionStart(initiatingClassLoader, transaction); } return transaction; @@ -220,7 +220,7 @@ public Transaction startChildTransaction(@Nullable C headerCarrier, BinaryHe Transaction transaction = null; if (isRunning()) { transaction = createTransaction().start(TraceContext.getFromTraceContextBinaryHeaders(), headerCarrier, - binaryHeadersGetter, epochMicros, sampler, initiatingClassLoader); + binaryHeadersGetter, epochMicros, sampler); afterTransactionStart(initiatingClassLoader, transaction); } return transaction; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java index 5632f397ce..406123f209 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java @@ -44,6 +44,8 @@ public class ErrorCapture implements Recyclable { private static final Logger logger = LoggerFactory.getLogger(ErrorCapture.class); + private static final ThreadLocal activeError = new ThreadLocal<>(); + private final TraceContext traceContext; /** @@ -205,34 +207,20 @@ private void setCulprit(StackTraceElement stackTraceElement) { } public ErrorCapture activate() { - List activationListeners = tracer.getActivationListeners(); - for (int i = 0; i < activationListeners.size(); i++) { - try { - activationListeners.get(i).beforeActivate(this); - } catch (Error e) { - throw e; - } catch (Throwable t) { - logger.warn("Exception while calling {}#beforeActivate", activationListeners.get(i).getClass().getSimpleName(), t); - } - } + activeError.set(this); return this; } public ErrorCapture deactivate() { - List activationListeners = tracer.getActivationListeners(); - for (int i = 0; i < activationListeners.size(); i++) { - try { - // `this` is guaranteed to not be recycled yet as the reference count is only decremented after this method has executed - activationListeners.get(i).afterDeactivate(this); - } catch (Error e) { - throw e; - } catch (Throwable t) { - logger.warn("Exception while calling {}#afterDeactivate", activationListeners.get(i).getClass().getSimpleName(), t); - } - } + activeError.remove(); return this; } + @Nullable + public static ErrorCapture getActive() { + return activeError.get(); + } + public static class TransactionInfo implements Recyclable { /** * A hint for UI to be able to show whether a recorded trace for the corresponding transaction is expected diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContext.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContext.java index 6f2393936f..9bfdb30e68 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContext.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContext.java @@ -23,16 +23,12 @@ import co.elastic.apm.agent.impl.Tracer; import co.elastic.apm.agent.impl.sampling.Sampler; import co.elastic.apm.agent.objectpool.Recyclable; -import co.elastic.apm.agent.sdk.weakconcurrent.WeakConcurrent; -import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap; import co.elastic.apm.agent.util.ByteUtils; -import co.elastic.apm.agent.util.ClassLoaderUtils; import co.elastic.apm.agent.util.HexUtils; import co.elastic.apm.agent.sdk.logging.Logger; import co.elastic.apm.agent.sdk.logging.LoggerFactory; import javax.annotation.Nullable; -import java.lang.ref.WeakReference; import java.nio.charset.StandardCharsets; import java.util.Objects; @@ -97,10 +93,6 @@ public class TraceContext implements Recyclable { private static final Double SAMPLE_RATE_ZERO = 0d; - /** - * Helps to reduce allocations by caching {@link WeakReference}s to {@link ClassLoader}s - */ - private static final WeakMap> classLoaderWeakReferenceCache = WeakConcurrent.buildMap(); private static final ChildContextCreator FROM_PARENT_CONTEXT = new ChildContextCreator() { @Override public boolean asChildOf(TraceContext child, TraceContext parent) { @@ -224,9 +216,7 @@ public static void copyTraceContextTextHeaders(S source, TextHeaderGetter private final StringBuilder outgoingTextHeader = new StringBuilder(TEXT_HEADER_EXPECTED_LENGTH); private byte flags; private boolean discardable = true; - // weakly referencing to avoid CL leaks in case of leaked spans - @Nullable - private WeakReference applicationClassLoader; + private final TraceState traceState; final CoreConfiguration coreConfiguration; @@ -440,7 +430,6 @@ public void asChildOf(TraceContext parent) { clock.init(parent.clock); serviceName = parent.serviceName; serviceVersion = parent.serviceVersion; - applicationClassLoader = parent.applicationClassLoader; traceState.copyFrom(parent.traceState); onMutation(); } @@ -457,7 +446,6 @@ public void resetState() { clock.resetState(); serviceName = null; serviceVersion = null; - applicationClassLoader = null; traceState.resetState(); traceState.setSizeLimit(coreConfiguration.getTracestateSizeLimit()); } @@ -658,7 +646,6 @@ public void copyFrom(TraceContext other) { clock.init(other.clock); serviceName = other.serviceName; serviceVersion = other.serviceVersion; - applicationClassLoader = other.applicationClassLoader; traceState.copyFrom(other.traceState); onMutation(); } @@ -728,27 +715,6 @@ public int hashCode() { return Objects.hash(traceId, id, parentId, flags); } - void setApplicationClassLoader(@Nullable ClassLoader classLoader) { - if (ClassLoaderUtils.isBootstrapClassLoader(classLoader) || ClassLoaderUtils.isAgentClassLoader(classLoader)) { - return; - } - WeakReference local = classLoaderWeakReferenceCache.get(classLoader); - if (local == null) { - local = new WeakReference<>(classLoader); - classLoaderWeakReferenceCache.putIfAbsent(classLoader, local); - } - applicationClassLoader = local; - } - - @Nullable - public ClassLoader getApplicationClassLoader() { - if (applicationClassLoader != null) { - return applicationClassLoader.get(); - } else { - return null; - } - } - public TraceState getTraceState() { return traceState; } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java index 4e470be1d0..e7c1b77585 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java @@ -119,17 +119,13 @@ public Transaction(ElasticApmTracer tracer) { super(tracer); } - public Transaction start(TraceContext.ChildContextCreator childContextCreator, @Nullable T parent, long epochMicros, - Sampler sampler, @Nullable ClassLoader initiatingClassLoader) { - traceContext.setApplicationClassLoader(initiatingClassLoader); + public Transaction start(TraceContext.ChildContextCreator childContextCreator, @Nullable T parent, long epochMicros, Sampler sampler) { boolean startedAsChild = parent != null && childContextCreator.asChildOf(traceContext, parent); onTransactionStart(startedAsChild, epochMicros, sampler); return this; } - public Transaction start(TraceContext.ChildContextCreatorTwoArg childContextCreator, @Nullable T parent, A arg, - long epochMicros, Sampler sampler, @Nullable ClassLoader initiatingClassLoader) { - traceContext.setApplicationClassLoader(initiatingClassLoader); + public Transaction start(TraceContext.ChildContextCreatorTwoArg childContextCreator, @Nullable T parent, A arg, long epochMicros, Sampler sampler) { boolean startedAsChild = childContextCreator.asChildOf(traceContext, parent, arg); onTransactionStart(startedAsChild, epochMicros, sampler); return this; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/LoggingConfiguration.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/LoggingConfiguration.java index 0138ab1a8d..dcfd2ccc99 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/LoggingConfiguration.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/LoggingConfiguration.java @@ -149,27 +149,6 @@ static LogLevel mapLogLevel(LogLevel original) { .dynamic(false) .buildWithDefault(DEFAULT_LOG_FILE); - private final ConfigurationOption logCorrelationEnabled = ConfigurationOption.booleanOption() - .key("enable_log_correlation") - .configurationCategory(LOGGING_CATEGORY) - .description("A boolean specifying if the agent should integrate into SLF4J's https://www.slf4j.org/api/org/slf4j/MDC.html[MDC] to enable trace-log correlation.\n" + - "If set to `true`, the agent will set the `trace.id` and `transaction.id` for the currently active spans and transactions to the MDC.\n" + - "Since version 1.16.0, the agent also adds `error.id` of captured error to the MDC just before the error message is logged.\n" + - "See <> for more details.\n" + - "\n" + - "NOTE: While it's allowed to enable this setting at runtime, you can't disable it without a restart.") - .dynamic(true) - .addValidator(new ConfigurationOption.Validator() { - @Override - public void assertValid(Boolean value) { - if (logCorrelationEnabled != null && logCorrelationEnabled.get() && Boolean.FALSE.equals(value)) { - // the reason is that otherwise the MDC will not be cleared when disabling while a span is currently active - throw new IllegalArgumentException("Disabling the log correlation at runtime is not possible."); - } - } - }) - .buildWithDefault(false); - private final ConfigurationOption logEcsReformatting = ConfigurationOption.enumOption(LogEcsReformatting.class) .key("log_ecs_reformatting") .configurationCategory(LOGGING_CATEGORY) @@ -177,8 +156,7 @@ public void assertValid(Boolean value) { .description("Specifying whether and how the agent should automatically reformat application logs \n" + "into {ecs-logging-ref}/index.html[ECS-compatible JSON], suitable for ingestion into Elasticsearch for \n" + "further Log analysis. This functionality is available for log4j1, log4j2 and Logback. \n" + - "Once this option is enabled with any valid option, log correlation will be activated as well, " + - "regardless of the <> configuration. \n" + + "The ECS log lines will include active trace/transaction/error IDs, if there are such. \n" + "\n" + "Available options:\n" + "\n" + @@ -379,11 +357,6 @@ private static void setLogLevel(@Nullable LogLevel level) { Configurator.setLevel("com.networknt.schema", org.apache.logging.log4j.Level.WARN); } - public boolean isLogCorrelationEnabled() { - // Enabling automatic ECS-reformatting implicitly enables log correlation - return logCorrelationEnabled.get() || getLogEcsReformatting() != LogEcsReformatting.OFF; - } - public LogEcsReformatting getLogEcsReformatting() { return logEcsReformatting.get(); } @@ -398,8 +371,8 @@ public List getLogEcsFormatterAllowList() { @Nullable public String getLogEcsFormattingDestinationDir() { - String logShadingDestDir = logEcsFormattingDestinationDir.get().trim(); - return (logShadingDestDir.isEmpty()) ? null : logShadingDestDir; + String logReformattingDestDir = logEcsFormattingDestinationDir.get().trim(); + return (logReformattingDestDir.isEmpty()) ? null : logReformattingDestDir; } public long getLogFileSize() { diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ClassLoaderUtils.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ClassLoaderUtils.java index e79e820c3b..d21902ac98 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ClassLoaderUtils.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ClassLoaderUtils.java @@ -44,7 +44,9 @@ public static boolean isPersistentClassLoader(@Nullable ClassLoader classLoader) } public static boolean isAgentClassLoader(@Nullable ClassLoader classLoader) { - return classLoader != null && classLoader.getClass().getName().startsWith("co.elastic.apm"); + return (classLoader != null && classLoader.getClass().getName().startsWith("co.elastic.apm")) || + // This one also covers unit tests, where the app class loader loads the agent + ClassLoaderUtils.class.getClassLoader().equals(classLoader); } public static boolean isBootstrapClassLoader(@Nullable ClassLoader classLoader) { diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/MockReporter.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/MockReporter.java index ad783686bf..0a1c69ba91 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/MockReporter.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/MockReporter.java @@ -420,6 +420,12 @@ public void awaitSpanCount(int count) { .isEqualTo(count)); } + public void awaitErrorCount(int count) { + awaitUntilAsserted(() -> assertThat(getNumReportedErrors()) + .describedAs("expecting %d errors", count) + .isEqualTo(count)); + } + @Override public synchronized void report(ErrorCapture error) { if (closed) { @@ -470,6 +476,10 @@ public synchronized List getErrors() { return Collections.unmodifiableList(errors); } + public synchronized int getNumReportedErrors() { + return errors.size(); + } + public synchronized ErrorCapture getFirstError() { assertThat(errors) .describedAs("at least one error expected, none have been reported") diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/TransactionUtils.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/TransactionUtils.java index 7be44399f6..5f0aae242c 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/TransactionUtils.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/TransactionUtils.java @@ -32,7 +32,7 @@ public class TransactionUtils { public static void fillTransaction(Transaction t) { - t.start(TraceContext.asRoot(), null, (long) 0, ConstantSampler.of(true), TransactionUtils.class.getClassLoader()) + t.start(TraceContext.asRoot(), null, (long) 0, ConstantSampler.of(true)) .withName("GET /api/types") .withType("request") .withResult("success") diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/serialize/DslJsonSerializerTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/serialize/DslJsonSerializerTest.java index 40852fd464..10a0b4b764 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/serialize/DslJsonSerializerTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/serialize/DslJsonSerializerTest.java @@ -157,7 +157,7 @@ void testSerializeUrlPort(boolean useNumericPort) { void testErrorSerialization() { ElasticApmTracer tracer = MockTracer.create(); Transaction transaction = new Transaction(tracer); - transaction.start(TraceContext.asRoot(), null, -1, ConstantSampler.of(true), null); + transaction.start(TraceContext.asRoot(), null, -1, ConstantSampler.of(true)); ErrorCapture error = new ErrorCapture(tracer).asChildOf(transaction).withTimestamp(5000); error.setTransactionSampled(true); error.setTransactionType("test-type"); @@ -206,7 +206,7 @@ void testErrorSerializationAllFrames() { void testErrorSerializationWithEmptyTraceId() { ElasticApmTracer tracer = MockTracer.create(); Transaction transaction = new Transaction(tracer); - transaction.start(TraceContext.asRoot(), null, -1, ConstantSampler.of(true), null); + transaction.start(TraceContext.asRoot(), null, -1, ConstantSampler.of(true)); transaction.getTraceContext().getTraceId().resetState(); ErrorCapture error = new ErrorCapture(tracer).asChildOf(transaction).withTimestamp(5000); @@ -1213,7 +1213,7 @@ private FaaSMetaDataExtension createFaaSMetaDataExtension() { private Transaction createRootTransaction(Sampler sampler) { Transaction t = new Transaction(MockTracer.create()); - t.start(TraceContext.asRoot(), null, 0, sampler, getClass().getClassLoader()); + t.start(TraceContext.asRoot(), null, 0, sampler); t.withType("type"); t.getContext().getRequest().withMethod("GET"); t.getContext().getRequest().getUrl().withFull("http://localhost:8080/foo/bar"); diff --git a/apm-agent-core/src/test/java/org/example/stacktrace/ErrorCaptureTest.java b/apm-agent-core/src/test/java/org/example/stacktrace/ErrorCaptureTest.java index 9420dc3af7..df9e168a07 100644 --- a/apm-agent-core/src/test/java/org/example/stacktrace/ErrorCaptureTest.java +++ b/apm-agent-core/src/test/java/org/example/stacktrace/ErrorCaptureTest.java @@ -109,4 +109,13 @@ void testTransactionContextTransferNonFinishedBody() { .describedAs("Body buffer should be null when copying from a transaction where capturing was not finished yet") .isNull(); } + + @Test + void testActiveError() { + assertThat(ErrorCapture.getActive()).isNull(); + ErrorCapture errorCapture = new ErrorCapture(tracer).activate(); + assertThat(ErrorCapture.getActive()).isNotNull(); + errorCapture.deactivate().end(); + assertThat(ErrorCapture.getActive()).isNull(); + } } diff --git a/apm-agent-plugins/apm-ecs-logging-plugin/pom.xml b/apm-agent-plugins/apm-ecs-logging-plugin/pom.xml index 0bd3c0f80e..b753b44c03 100644 --- a/apm-agent-plugins/apm-ecs-logging-plugin/pom.xml +++ b/apm-agent-plugins/apm-ecs-logging-plugin/pom.xml @@ -34,6 +34,11 @@ 2.14.1 provided + + org.apache.ivy + ivy + test + diff --git a/apm-agent-plugins/apm-ecs-logging-plugin/src/test/java/co/elastic/apm/agent/ecs_logging/Log4j2ServiceNameInstrumentationTest.java b/apm-agent-plugins/apm-ecs-logging-plugin/src/test/java/co/elastic/apm/agent/ecs_logging/Log4j2ServiceNameInstrumentationTest.java index 72b885bfb4..9a8bdc0508 100644 --- a/apm-agent-plugins/apm-ecs-logging-plugin/src/test/java/co/elastic/apm/agent/ecs_logging/Log4j2ServiceNameInstrumentationTest.java +++ b/apm-agent-plugins/apm-ecs-logging-plugin/src/test/java/co/elastic/apm/agent/ecs_logging/Log4j2ServiceNameInstrumentationTest.java @@ -25,29 +25,31 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.message.SimpleMessage; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -class Log4j2ServiceNameInstrumentationTest extends AbstractInstrumentationTest { +@Ignore +public class Log4j2ServiceNameInstrumentationTest extends AbstractInstrumentationTest { - @BeforeAll - static void setUp() { + @BeforeClass + public static void setUp() { when(tracer.getConfig(CoreConfiguration.class).getServiceName()).thenReturn("foo"); } @Test - void testBuildWithNoServiceNameSet() throws JsonProcessingException { + public void testBuildWithNoServiceNameSet() throws JsonProcessingException { EcsLayout ecsLayout = EcsLayout.newBuilder().build(); assertThat(getServiceName(ecsLayout.toSerializable(createLogEvent()))).isEqualTo("foo"); } @Test - void testBuildWithServiceNameSet() throws JsonProcessingException { + public void testBuildWithServiceNameSet() throws JsonProcessingException { EcsLayout ecsLayout = EcsLayout.newBuilder().setServiceName("bar").build(); assertThat(getServiceName(ecsLayout.toSerializable(createLogEvent()))).isEqualTo("bar"); } diff --git a/apm-agent-plugins/apm-ecs-logging-plugin/src/test/java/co/elastic/apm/agent/ecs_logging/Log4j2_17_1ServiceNameInstrumentationTest.java b/apm-agent-plugins/apm-ecs-logging-plugin/src/test/java/co/elastic/apm/agent/ecs_logging/Log4j2_17_1ServiceNameInstrumentationTest.java new file mode 100644 index 0000000000..96fae41ad6 --- /dev/null +++ b/apm-agent-plugins/apm-ecs-logging-plugin/src/test/java/co/elastic/apm/agent/ecs_logging/Log4j2_17_1ServiceNameInstrumentationTest.java @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.ecs_logging; + +import co.elastic.apm.agent.TestClassWithDependencyRunner; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class Log4j2_17_1ServiceNameInstrumentationTest { + private final TestClassWithDependencyRunner runner; + + public Log4j2_17_1ServiceNameInstrumentationTest() throws Exception { + List dependencies = List.of("co.elastic.logging:log4j2-ecs-layout:1.3.2"); + runner = new TestClassWithDependencyRunner(dependencies, Log4j2ServiceNameInstrumentationTest.class); + } + + @Test + public void testVersions() { + runner.run(); + } +} diff --git a/apm-agent-plugins/apm-error-logging-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-error-logging-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation deleted file mode 100644 index b755ef8ab2..0000000000 --- a/apm-agent-plugins/apm-error-logging-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation +++ /dev/null @@ -1,2 +0,0 @@ -co.elastic.apm.agent.errorlogging.Slf4jLoggerErrorCapturingInstrumentation -co.elastic.apm.agent.errorlogging.Log4j2LoggerErrorCapturingInstrumentation diff --git a/apm-agent-plugins/apm-log-correlation-plugin/pom.xml b/apm-agent-plugins/apm-log-correlation-plugin/pom.xml deleted file mode 100644 index 50af18fa39..0000000000 --- a/apm-agent-plugins/apm-log-correlation-plugin/pom.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - 4.0.0 - - - apm-agent-plugins - co.elastic.apm - 1.29.1-SNAPSHOT - - - apm-log-correlation-plugin - ${project.groupId}:${project.artifactId} - - - ${project.basedir}/../.. - - - - - - ch.qos.logback - logback-classic - 1.2.3 - test - - - org.apache.logging.log4j - log4j-core - 2.12.0 - test - - - log4j - log4j - 1.2.17 - test - - - org.jboss.logging - jboss-logging - 3.4.1.Final - test - - - ${project.groupId} - apm-error-logging-plugin - ${project.version} - test - - - - diff --git a/apm-agent-plugins/apm-log-correlation-plugin/src/main/java/co/elastic/apm/agent/mdc/MdcActivationListener.java b/apm-agent-plugins/apm-log-correlation-plugin/src/main/java/co/elastic/apm/agent/mdc/MdcActivationListener.java deleted file mode 100644 index 769e94a800..0000000000 --- a/apm-agent-plugins/apm-log-correlation-plugin/src/main/java/co/elastic/apm/agent/mdc/MdcActivationListener.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.mdc; - -import co.elastic.apm.agent.cache.WeakKeySoftValueLoadingCache; -import co.elastic.apm.agent.impl.ActivationListener; -import co.elastic.apm.agent.impl.ElasticApmTracer; -import co.elastic.apm.agent.impl.error.ErrorCapture; -import co.elastic.apm.agent.impl.transaction.AbstractSpan; -import co.elastic.apm.agent.impl.transaction.TraceContext; -import co.elastic.apm.agent.logging.LoggingConfiguration; -import co.elastic.apm.agent.sdk.logging.Logger; -import co.elastic.apm.agent.sdk.logging.LoggerFactory; - -import javax.annotation.Nullable; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -public class MdcActivationListener implements ActivationListener { - - private static final String SLF4J_MDC = "org.slf4j.MDC"; - - private static final String LOG4J_MDC = "org.apache.log4j.MDC"; - - private static final String LOG4J2_MDC = "org.apache.logging.log4j.ThreadContext"; - - private static final String JBOSS_LOGGING_MDC = "org.jboss.logging.MDC"; - - private static final String TRACE_ID = "trace.id"; - private static final String TRANSACTION_ID = "transaction.id"; - private static final String ERROR_ID = "error.id"; - private static final Logger logger = LoggerFactory.getLogger(MdcActivationListener.class); - - // Never invoked- only used for caching ClassLoaders that can't load the MDC/ThreadContext class - private static final MethodHandle NOOP = MethodHandles.constant(String.class, "ClassLoader cannot load MDC/ThreadContext"); - - private final WeakKeySoftValueLoadingCache[] mdcPutMethodHandleCaches = new WeakKeySoftValueLoadingCache[]{ - new WeakKeySoftValueLoadingCache<>(new WeakKeySoftValueLoadingCache.ValueSupplier() { - @Nullable - @Override - public MethodHandle get(ClassLoader classLoader) { - try { - return MethodHandles.lookup() - .findStatic(classLoader.loadClass(SLF4J_MDC), "put", MethodType.methodType(void.class, String.class, String.class)); - } catch (Exception e) { - logger.debug("Class loader " + classLoader + " cannot load slf4j API", e); - return NOOP; - } - } - }), - new WeakKeySoftValueLoadingCache<>(new WeakKeySoftValueLoadingCache.ValueSupplier() { - @Nullable - @Override - public MethodHandle get(ClassLoader classLoader) { - try { - return MethodHandles.lookup() - .findStatic(classLoader.loadClass(LOG4J_MDC), "put", MethodType.methodType(void.class, String.class, Object.class)); - } catch (Exception e) { - logger.debug("Class loader " + classLoader + " cannot load log4j API", e); - return NOOP; - } - } - }), - new WeakKeySoftValueLoadingCache<>(new WeakKeySoftValueLoadingCache.ValueSupplier() { - @Nullable - @Override - public MethodHandle get(ClassLoader classLoader) { - try { - return MethodHandles.lookup() - .findStatic(classLoader.loadClass(LOG4J2_MDC), "put", MethodType.methodType(void.class, String.class, String.class)); - } catch (Exception e) { - logger.debug("Class loader " + classLoader + " cannot load log4j2 API", e); - return NOOP; - } - } - }), - new WeakKeySoftValueLoadingCache<>(new WeakKeySoftValueLoadingCache.ValueSupplier() { - @Nullable - @Override - public MethodHandle get(ClassLoader classLoader) { - try { - return MethodHandles.lookup() - .findStatic(classLoader.loadClass(JBOSS_LOGGING_MDC), "put", MethodType.methodType(Object.class, String.class, Object.class)); - } catch (Exception e) { - logger.debug("Class loader " + classLoader + " cannot load JBoss Logging API", e); - return NOOP; - } - } - }) - }; - - private final WeakKeySoftValueLoadingCache[] mdcRemoveMethodHandleCaches = new WeakKeySoftValueLoadingCache[]{ - new WeakKeySoftValueLoadingCache<>(new WeakKeySoftValueLoadingCache.ValueSupplier() { - @Nullable - @Override - public MethodHandle get(ClassLoader classLoader) { - try { - return MethodHandles.lookup() - .findStatic(classLoader.loadClass(SLF4J_MDC), "remove", MethodType.methodType(void.class, String.class)); - } catch (Exception ignore) { - // No need to log - logged already when populated the put cache - return NOOP; - } - } - }), - new WeakKeySoftValueLoadingCache<>(new WeakKeySoftValueLoadingCache.ValueSupplier() { - @Nullable - @Override - public MethodHandle get(ClassLoader classLoader) { - try { - return MethodHandles.lookup() - .findStatic(classLoader.loadClass(LOG4J_MDC), "remove", MethodType.methodType(void.class, String.class)); - } catch (Exception ignore) { - // No need to log - logged already when populated the put cache - return NOOP; - } - } - }), - new WeakKeySoftValueLoadingCache<>(new WeakKeySoftValueLoadingCache.ValueSupplier() { - @Nullable - @Override - public MethodHandle get(ClassLoader classLoader) { - try { - return MethodHandles.lookup() - .findStatic(classLoader.loadClass(LOG4J2_MDC), "remove", MethodType.methodType(void.class, String.class)); - } catch (Exception ignore) { - // No need to log - logged already when populated the put cache - return NOOP; - } - } - }), - new WeakKeySoftValueLoadingCache<>(new WeakKeySoftValueLoadingCache.ValueSupplier() { - @Nullable - @Override - public MethodHandle get(ClassLoader classLoader) { - try { - return MethodHandles.lookup() - .findStatic(classLoader.loadClass(JBOSS_LOGGING_MDC), "remove", MethodType.methodType(void.class, String.class)); - } catch (Exception ignore) { - // No need to log - logged already when populated the put cache - return NOOP; - } - } - }) - }; - private final LoggingConfiguration loggingConfiguration; - private final ElasticApmTracer tracer; - - public MdcActivationListener(ElasticApmTracer tracer) { - this.tracer = tracer; - this.loggingConfiguration = tracer.getConfig(LoggingConfiguration.class); - } - - @Override - public void beforeActivate(AbstractSpan span) throws Throwable { - before(span.getTraceContext(), false); - } - - @Override - public void beforeActivate(ErrorCapture error) throws Throwable { - before(error.getTraceContext(), true); - } - - public void before(TraceContext traceContext, boolean isError) throws Throwable { - if (loggingConfiguration.isLogCorrelationEnabled() && tracer.isRunning()) { - for (WeakKeySoftValueLoadingCache mdcPutMethodHandleCache : mdcPutMethodHandleCaches) { - MethodHandle put = mdcPutMethodHandleCache.get(getApplicationClassLoader(traceContext)); - if (put != null && put != NOOP) { - if (isError) { - put.invoke(ERROR_ID, traceContext.getId().toString()); - } else if (tracer.getActive() == null) { - put.invoke(TRACE_ID, traceContext.getTraceId().toString()); - put.invoke(TRANSACTION_ID, traceContext.getTransactionId().toString()); - } - } - } - } - } - - @Override - public void afterDeactivate(AbstractSpan deactivatedSpan) throws Throwable { - after(deactivatedSpan.getTraceContext(), false); - } - - @Override - public void afterDeactivate(ErrorCapture deactivatedError) throws Throwable { - after(deactivatedError.getTraceContext(), true); - } - - public void after(TraceContext deactivatedContext, boolean isError) throws Throwable { - if (loggingConfiguration.isLogCorrelationEnabled()) { - for (WeakKeySoftValueLoadingCache mdcRemoveMethodHandleCache : mdcRemoveMethodHandleCaches) { - MethodHandle remove = mdcRemoveMethodHandleCache.get(getApplicationClassLoader(deactivatedContext)); - if (remove != null && remove != NOOP) { - if (isError) { - remove.invokeExact(ERROR_ID); - } else if (tracer.getActive() == null) { - remove.invokeExact(TRACE_ID); - remove.invokeExact(TRANSACTION_ID); - } - } - } - } - } - - /** - * Looks up the class loader which corresponds to the application the current transaction belongs to. - * @param context - * @return - */ - private ClassLoader getApplicationClassLoader(TraceContext context) { - ClassLoader applicationClassLoader = context.getApplicationClassLoader(); - if (applicationClassLoader != null) { - return applicationClassLoader; - } else { - return getFallbackClassLoader(); - } - } - private ClassLoader getFallbackClassLoader() { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - if (classLoader == null) { - classLoader = ClassLoader.getSystemClassLoader(); - } - return classLoader; - } - -} diff --git a/apm-agent-plugins/apm-log-correlation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.impl.ActivationListener b/apm-agent-plugins/apm-log-correlation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.impl.ActivationListener deleted file mode 100644 index a316c31226..0000000000 --- a/apm-agent-plugins/apm-log-correlation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.impl.ActivationListener +++ /dev/null @@ -1 +0,0 @@ -co.elastic.apm.agent.mdc.MdcActivationListener diff --git a/apm-agent-plugins/apm-log-correlation-plugin/src/test/java/co/elastic/apm/agent/mdc/MdcActivationListenerIT.java b/apm-agent-plugins/apm-log-correlation-plugin/src/test/java/co/elastic/apm/agent/mdc/MdcActivationListenerIT.java deleted file mode 100644 index 056ec1d9d2..0000000000 --- a/apm-agent-plugins/apm-log-correlation-plugin/src/test/java/co/elastic/apm/agent/mdc/MdcActivationListenerIT.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.mdc; - -import co.elastic.apm.agent.AbstractInstrumentationTest; -import co.elastic.apm.agent.impl.transaction.Transaction; -import co.elastic.apm.agent.logging.LoggingConfiguration; -import org.apache.logging.log4j.ThreadContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.stubbing.Answer; -import org.slf4j.MDC; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class MdcActivationListenerIT extends AbstractInstrumentationTest { - - private LoggingConfiguration loggingConfiguration; - - @BeforeEach - void setUp() { - MDC.clear(); - org.apache.log4j.MDC.clear(); - ThreadContext.clearAll(); - org.jboss.logging.MDC.clear(); - loggingConfiguration = config.getConfig(LoggingConfiguration.class); - } - - @Test - void testVerifyThatWithEnabledCorrelationAndLoggedErrorMdcErrorIdIsNotBlankWithSlf4j() { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - org.slf4j.Logger mockedLogger = mock(org.slf4j.Logger.class); - doAnswer(invocation -> assertMdcErrorIdIsNotEmpty()).when(mockedLogger).error(anyString(), any(Exception.class)); - - assertMdcErrorIdIsEmpty(); - - Transaction transaction = tracer.startRootTransaction(getClass().getClassLoader()).withType("request").withName("test"); - transaction.activate(); - mockedLogger.error("Some slf4j exception", new RuntimeException("Hello exception")); - - assertMdcErrorIdIsEmpty(); - - transaction.deactivate().end(); - - assertMdcErrorIdIsEmpty(); - } - - @Test - void testVerifyThatWithEnabledCorrelationAndLoggedErrorMdcErrorIdIsNotBlankWithSlf4jNotInTransaction() { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - org.slf4j.Logger mockedLogger = mock(org.slf4j.Logger.class); - doAnswer(invocation -> assertMdcErrorIdIsNotEmpty()).when(mockedLogger).error(anyString(), any(Exception.class)); - - assertMdcErrorIdIsEmpty(); - - mockedLogger.error("Some slf4j exception", new RuntimeException("Hello exception")); - - assertMdcErrorIdIsEmpty(); - } - - @Test - void testVerifyThatWithEnabledCorrelationAndLoggedErrorMdcErrorIdIsNotBlankWithLog4j() { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - org.apache.logging.log4j.Logger logger = mock(org.apache.logging.log4j.Logger.class); - doAnswer(invocation -> assertMdcErrorIdIsNotEmpty()).when(logger).error(anyString(), any(Exception.class)); - - assertMdcErrorIdIsEmpty(); - - Transaction transaction = tracer.startRootTransaction(getClass().getClassLoader()).withType("request").withName("test"); - transaction.activate(); - logger.error("Some apache logger exception", new RuntimeException("Hello exception")); - assertMdcErrorIdIsEmpty(); - transaction.deactivate().end(); - - assertMdcErrorIdIsEmpty(); - } - - @Test - void testVerifyThatWithEnabledCorrelationAndLoggedErrorMdcErrorIdIsNotBlankWithLog4jNotInTransaction() { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - org.apache.logging.log4j.Logger logger = mock(org.apache.logging.log4j.Logger.class); - doAnswer(invocation -> assertMdcErrorIdIsNotEmpty()).when(logger).error(anyString(), any(Exception.class)); - - assertMdcErrorIdIsEmpty(); - - logger.error("Some apache logger exception", new RuntimeException("Hello exception")); - - assertMdcErrorIdIsEmpty(); - } - - private void assertMdcErrorIdIsEmpty() { - assertThat(MDC.get("error.id")).isNull(); - assertThat(org.apache.log4j.MDC.get("error.id")).isNull(); - assertThat(org.jboss.logging.MDC.get("error.id")).isNull(); - } - - private Answer assertMdcErrorIdIsNotEmpty() { - assertThat(MDC.get("error.id")).isNotBlank(); - assertThat(org.apache.log4j.MDC.get("error.id")).isNotNull(); - assertThat(org.jboss.logging.MDC.get("error.id")).isNotNull(); - return null; - } -} diff --git a/apm-agent-plugins/apm-log-correlation-plugin/src/test/java/co/elastic/apm/agent/mdc/MdcActivationListenerTest.java b/apm-agent-plugins/apm-log-correlation-plugin/src/test/java/co/elastic/apm/agent/mdc/MdcActivationListenerTest.java deleted file mode 100644 index d0ba1beb71..0000000000 --- a/apm-agent-plugins/apm-log-correlation-plugin/src/test/java/co/elastic/apm/agent/mdc/MdcActivationListenerTest.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.mdc; - -import co.elastic.apm.agent.AbstractInstrumentationTest; -import co.elastic.apm.agent.configuration.SpyConfiguration; -import co.elastic.apm.agent.impl.Scope; -import co.elastic.apm.agent.impl.TracerInternalApiUtils; -import co.elastic.apm.agent.impl.transaction.AbstractSpan; -import co.elastic.apm.agent.impl.transaction.Span; -import co.elastic.apm.agent.impl.transaction.Transaction; -import co.elastic.apm.agent.logging.LoggingConfiguration; -import org.apache.logging.log4j.ThreadContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.MDC; - -import javax.annotation.Nullable; -import java.io.IOException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.mockito.Mockito.when; - -class MdcActivationListenerTest extends AbstractInstrumentationTest { - - private LoggingConfiguration loggingConfiguration; - private Boolean log4jMdcWorking; - - @BeforeEach - void setUp() { - org.apache.log4j.MDC.put("test", true); - log4jMdcWorking = (Boolean) org.apache.log4j.MDC.get("test"); - assertThat(log4jMdcWorking) - .describedAs("MDC not working as expected") - .isNotNull(); - - forAllMdc(MdcImpl::clear); - loggingConfiguration = config.getConfig(LoggingConfiguration.class); - } - - @Test - void testMdcIntegration() { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - Transaction transaction = tracer.startRootTransaction(getClass().getClassLoader()).withType("request").withName("test"); - assertMdcIsEmpty(); - try (Scope scope = transaction.activateInScope()) { - assertMdcIsSet(transaction); - Span child = transaction.createSpan(); - try (Scope childScope = child.activateInScope()) { - assertMdcIsSet(child); - Span grandchild = child.createSpan(); - try (Scope grandchildScope = grandchild.activateInScope()) { - assertMdcIsSet(grandchild); - } - grandchild.end(); - assertMdcIsSet(child); - } - child.end(); - assertMdcIsSet(transaction); - } - assertMdcIsEmpty(); - transaction.end(); - } - - @Test - void testDisabledWhenInactive() { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - TracerInternalApiUtils.pauseTracer(tracer); - Transaction transaction = tracer.startRootTransaction(getClass().getClassLoader()); - assertThat(transaction).isNull(); - assertMdcIsEmpty(); - } - - @Test - void testInactivationWhileMdcIsSet() { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - Transaction transaction = tracer.startRootTransaction(getClass().getClassLoader()).withType("request").withName("test"); - assertMdcIsEmpty(); - try (Scope scope = transaction.activateInScope()) { - assertMdcIsSet(transaction); - TracerInternalApiUtils.pauseTracer(tracer); - Span child = transaction.createSpan(); - try (Scope childScope = child.activateInScope()) { - assertMdcIsSet(transaction); - } - child.end(); - assertMdcIsSet(transaction); - } - assertMdcIsEmpty(); - transaction.end(); - } - - @Test - void testMdcIntegrationTransactionScopeInDifferentThread() throws Exception { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - Transaction transaction = tracer.startRootTransaction(getClass().getClassLoader()).withType("request").withName("test"); - assertMdcIsEmpty(); - final CompletableFuture result = new CompletableFuture<>(); - Thread thread = new Thread(() -> { - assertMdcIsEmpty(); - try (Scope scope = transaction.activateInScope()) { - assertMdcIsSet(transaction); - } - assertMdcIsEmpty(); - result.complete(true); - }); - thread.start(); - assertThat(result.get(1000, TimeUnit.MILLISECONDS).booleanValue()).isTrue(); - assertMdcIsEmpty(); - thread.join(); - transaction.end(); - } - - @Test - void testNoopWhenClassLoaderCantLoadMdc() throws Exception { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - Transaction transaction = tracer.startRootTransaction(null).withType("request").withName("test"); - assertMdcIsEmpty(); - final CompletableFuture result = new CompletableFuture<>(); - Thread thread = new Thread(() -> { - assertMdcIsEmpty(); - try (Scope scope = transaction.activateInScope()) { - assertMdcIsEmpty(); - } - result.complete(true); - }); - thread.setContextClassLoader(ClassLoader.getPlatformClassLoader()); - thread.start(); - assertThat(result.get(1000, TimeUnit.MILLISECONDS).booleanValue()).isTrue(); - assertMdcIsEmpty(); - thread.join(); - transaction.end(); - } - - @Test - void testWithNullContextClassLoader() throws Exception { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - Transaction transaction = tracer.startRootTransaction(getClass().getClassLoader()).withType("request").withName("test"); - assertMdcIsEmpty(); - final CompletableFuture result = new CompletableFuture<>(); - Thread thread = new Thread(() -> { - assertMdcIsEmpty(); - try (Scope scope = transaction.activateInScope()) { - assertMdcIsSet(transaction); - } - assertMdcIsEmpty(); - result.complete(true); - }); - thread.setContextClassLoader(null); - thread.start(); - assertThat(result.get().booleanValue()).isTrue(); - assertMdcIsEmpty(); - thread.join(); - transaction.end(); - } - - @Test - void testWithNullApplicationClassLoaderFallbackPlatformCL() throws Exception { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - Transaction transaction = tracer.startRootTransaction(null).withType("request").withName("test"); - assertMdcIsEmpty(); - final CompletableFuture result = new CompletableFuture<>(); - Thread thread = new Thread(() -> { - assertMdcIsEmpty(); - try (Scope scope = transaction.activateInScope()) { - // the platform CL can't load the MDC - assertMdcIsEmpty(); - } - assertMdcIsEmpty(); - result.complete(true); - }); - thread.setContextClassLoader(ClassLoader.getPlatformClassLoader()); - thread.start(); - assertThat(result.get().booleanValue()).isTrue(); - assertMdcIsEmpty(); - thread.join(); - transaction.end(); - } - - @Test - void testWithNullApplicationClassLoaderFallbackCL() throws Exception { - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - Transaction transaction = tracer.startRootTransaction(null).withType("request").withName("test"); - assertMdcIsEmpty(); - final CompletableFuture result = new CompletableFuture<>(); - Thread thread = new Thread(() -> { - assertMdcIsEmpty(); - try (Scope scope = transaction.activateInScope()) { - assertMdcIsSet(transaction); - } - assertMdcIsEmpty(); - result.complete(true); - }); - thread.setContextClassLoader(getClass().getClassLoader()); - thread.start(); - assertThat(result.get().booleanValue()).isTrue(); - assertMdcIsEmpty(); - thread.join(); - transaction.end(); - } - - @Test - void testMdcIntegrationContextScopeInDifferentThread() throws Exception { - ExecutorService executorService = Executors.newSingleThreadExecutor(); - try { - // initializes thread eagerly to avoid InheritableThreadLocal to inherit values to this thread - executorService.submit(() -> { - }).get(); - - when(loggingConfiguration.isLogCorrelationEnabled()).thenReturn(true); - final Transaction transaction = tracer.startRootTransaction(getClass().getClassLoader()).withType("request").withName("test"); - assertMdcIsEmpty(); - try (Scope scope = transaction.activateInScope()) { - assertMdcIsSet(transaction); - final Span child = transaction.createSpan(); - try (Scope childScope = child.activateInScope()) { - assertMdcIsSet(child); - executorService.submit(() -> { - assertMdcIsEmpty(); - try (Scope otherThreadScope = child.activateInScope()) { - assertMdcIsSet(child); - } - assertMdcIsEmpty(); - }).get(); - assertMdcIsSet(child); - } - child.end(); - assertMdcIsSet(transaction); - } finally { - transaction.end(); - } - } finally { - executorService.shutdownNow(); - } - assertMdcIsEmpty(); - } - - @Test - void testDisablingAtRuntimeNotPossible() throws IOException { - assertThat(loggingConfiguration.isLogCorrelationEnabled()).isFalse(); - config.save("enable_log_correlation", "true", SpyConfiguration.CONFIG_SOURCE_NAME); - assertThat(loggingConfiguration.isLogCorrelationEnabled()).isTrue(); - assertThatCode(() -> config.save("enable_log_correlation", "false", SpyConfiguration.CONFIG_SOURCE_NAME)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Disabling the log correlation at runtime is not possible."); - assertThat(loggingConfiguration.isLogCorrelationEnabled()).isTrue(); - } - - private void assertMdcIsSet(AbstractSpan span) { - forAllMdc(mdc -> { - assertThat(mdc.get("trace.id")).isEqualTo(span.getTraceContext().getTraceId().toString()); - assertThat(mdc.get("transaction.id")).isEqualTo(span.getTraceContext().getTransactionId().toString()); - }); - } - - private enum MdcImpl { - Slf4j( - MDC::get, - MDC::clear), - Log4j1( - k -> (String) org.apache.log4j.MDC.get(k), - org.apache.log4j.MDC::clear), - Log4j2( - ThreadContext::get, - ThreadContext::clearAll), - JbossLogging( - k -> (String) org.jboss.logging.MDC.get(k), - org.jboss.logging.MDC::clear); - - private final Function get; - private final Runnable clear; - - MdcImpl(Function get, Runnable clear) { - this.get = get; - this.clear = clear; - } - - @Nullable - String get(String key) { - return get.apply(key); - } - - void clear() { - clear.run(); - } - } - - private void assertMdcIsEmpty() { - forAllMdc(mdc -> { - assertThat(mdc.get("trace.id")).isNull(); - assertThat(mdc.get("transaction.id")).isNull(); - }); - } - - private void forAllMdc(Consumer task) { - Stream.of(MdcImpl.values()) - .filter(mdc -> mdc != MdcImpl.Log4j1 || log4jMdcWorking) - .forEach(task); - } - -} diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/test/java/co/elastic/apm/agent/log/shader/UtilsTest.java b/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/test/java/co/elastic/apm/agent/log/shader/UtilsTest.java deleted file mode 100644 index f9fdfdad6c..0000000000 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/test/java/co/elastic/apm/agent/log/shader/UtilsTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log.shader; - -import co.elastic.apm.agent.AbstractInstrumentationTest; -import co.elastic.apm.agent.logging.LogEcsReformatting; -import co.elastic.apm.agent.logging.LoggingConfiguration; -import org.junit.jupiter.api.Test; - -import javax.annotation.Nullable; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.when; - -public class UtilsTest extends AbstractInstrumentationTest { - - private static final String fileSeparator = System.getProperty("file.separator"); - - @Nullable - private final String logEcsFormattingDestinationDir = config.getConfig(LoggingConfiguration.class).getLogEcsFormattingDestinationDir(); - - private String computeShadeLogFilePathWithConfiguredDir(String logFilePath) { - return Utils.computeShadeLogFilePath(logFilePath, logEcsFormattingDestinationDir); - } - - @Test - void testShadePathComputation() { - assertThat(computeShadeLogFilePathWithConfiguredDir("/test/absolute/path/app.log")).isEqualTo(replaceFileSeparator("/test/absolute/path/app.ecs.json")); - assertThat(computeShadeLogFilePathWithConfiguredDir("test/relative/path/app.log")).isEqualTo(replaceFileSeparator("test/relative/path/app.ecs.json")); - assertThat(computeShadeLogFilePathWithConfiguredDir("/app.log")).isEqualTo(replaceFileSeparator("/app.ecs.json")); - assertThat(computeShadeLogFilePathWithConfiguredDir("app.log")).isEqualTo(replaceFileSeparator("app.ecs.json")); - } - - @Test - void testReplace() { - when(config.getConfig(LoggingConfiguration.class).getLogEcsReformatting()).thenReturn(LogEcsReformatting.REPLACE); - assertThat(computeShadeLogFilePathWithConfiguredDir("/test/absolute/path/app.log")).isEqualTo(replaceFileSeparator("/test/absolute/path/app.ecs.json")); - assertThat(computeShadeLogFilePathWithConfiguredDir("/test/absolute/path/app")).isEqualTo(replaceFileSeparator("/test/absolute/path/app.ecs.json")); - assertThat(computeShadeLogFilePathWithConfiguredDir("/test/absolute/path/app.log.1")).isEqualTo(replaceFileSeparator("/test/absolute/path/app.log.ecs.json")); - } - - @Test - void testAlternativeShadeLogsDestination_AbsolutePath() { - String shadeDir = "/some/alt/location"; - assertThat(Utils.computeShadeLogFilePath("/test/absolute/path/app.log", shadeDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); - assertThat(Utils.computeShadeLogFilePath("test/relative/path/app.log", shadeDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); - assertThat(Utils.computeShadeLogFilePath("/app.log", shadeDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); - assertThat(Utils.computeShadeLogFilePath("app.log", shadeDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); - } - - @Test - void testAlternativeShadeLogsDestination_RelativePath() { - String shadeDir = "some/alt/location"; - assertThat(Utils.computeShadeLogFilePath("/test/absolute/path/app.log", shadeDir)).isEqualTo(replaceFileSeparator("/test/absolute/path/some/alt/location/app.ecs.json")); - assertThat(Utils.computeShadeLogFilePath("test/relative/path/app.log", shadeDir)).isEqualTo(replaceFileSeparator("test/relative/path/some/alt/location/app.ecs.json")); - assertThat(Utils.computeShadeLogFilePath("/app.log", shadeDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); - assertThat(Utils.computeShadeLogFilePath("app.log", shadeDir)).isEqualTo(replaceFileSeparator("some/alt/location/app.ecs.json")); - } - - @Test - void testFileExtensionReplacement() { - assertThat(Utils.replaceFileExtensionToEcsJson("app.log")).isEqualTo("app.ecs.json"); - assertThat(Utils.replaceFileExtensionToEcsJson("app")).isEqualTo("app.ecs.json"); - assertThat(Utils.replaceFileExtensionToEcsJson("app.some.log")).isEqualTo("app.some.ecs.json"); - } - - private String replaceFileSeparator(String input) { - return input.replace("/", fileSeparator); - } - -} diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation deleted file mode 100644 index 995f5a9940..0000000000 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation +++ /dev/null @@ -1,2 +0,0 @@ -co.elastic.apm.agent.log4j1.Log4j1LogShadingInstrumentation$ShadingInstrumentation -co.elastic.apm.agent.log4j1.Log4j1LogShadingInstrumentation$StopAppenderInstrumentation diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/test/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/test/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation deleted file mode 100644 index 61495f0b58..0000000000 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/test/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation +++ /dev/null @@ -1,2 +0,0 @@ -# Allows to make Log4j 1.x work on Java 17 even if not supported -co.elastic.apm.agent.testinstr.OptionConverterInstrumentation diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation deleted file mode 100644 index fb387b841f..0000000000 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation +++ /dev/null @@ -1,3 +0,0 @@ -co.elastic.apm.agent.log4j2.Log4j2EcsReformattingInstrumentation$ShadingInstrumentation -co.elastic.apm.agent.log4j2.Log4j2EcsReformattingInstrumentation$StopAppenderInstrumentation -co.elastic.apm.agent.log4j2.Log4j2EcsReformattingInstrumentation$OverridingInstrumentation diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation deleted file mode 100644 index b1dfd3524b..0000000000 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation +++ /dev/null @@ -1,2 +0,0 @@ -co.elastic.apm.agent.logback.LogbackLogShadingInstrumentation$ShadingInstrumentation -co.elastic.apm.agent.logback.LogbackLogShadingInstrumentation$StopAppenderInstrumentation diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/pom.xml new file mode 100644 index 0000000000..6989066900 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + + apm-logging-plugin + co.elastic.apm + 1.29.1-SNAPSHOT + + + apm-jboss-logging-plugin + ${project.groupId}:${project.artifactId} + + + ${project.basedir}/../../.. + + + + + ${project.groupId} + apm-logging-plugin-common + ${project.version} + + + org.jboss.logging + jboss-logging + 3.4.2.Final + provided + + + org.jboss.logmanager + jboss-logmanager + 2.1.18.Final + provided + + + + + + + maven-surefire-plugin + + + org.jboss.logmanager.LogManager + + + + + + diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLogManagerCorrelationHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLogManagerCorrelationHelper.java new file mode 100644 index 0000000000..f7bf3889fd --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLogManagerCorrelationHelper.java @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.jbosslogging.correlation; + +import co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper; +import org.jboss.logmanager.ExtLogRecord; + +public class JBossLogManagerCorrelationHelper extends AbstractLogCorrelationHelper.DefaultLogCorrelationHelper { + + private final ThreadLocal cachedRecord = new ThreadLocal<>(); + + public boolean beforeLoggingEvent(ExtLogRecord record) { + cachedRecord.set(record); + return super.beforeLoggingEvent(); + } + + @Override + public void afterLoggingEvent(boolean addedToMdc) { + super.afterLoggingEvent(addedToMdc); + cachedRecord.remove(); + } + + @Override + protected void addToMdc(String key, String value) { + cachedRecord.get().putMdc(key, value); + } + + @Override + protected void removeFromMdc(String key) { + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLogManagerCorrelationInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLogManagerCorrelationInstrumentation.java new file mode 100644 index 0000000000..5d73d83056 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLogManagerCorrelationInstrumentation.java @@ -0,0 +1,76 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.jbosslogging.correlation; + +import co.elastic.apm.agent.bci.TracerAwareInstrumentation; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.jboss.logmanager.ExtLogRecord; + +import java.util.Collection; +import java.util.Collections; + +import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.classLoaderCanLoadClass; +import static net.bytebuddy.matcher.ElementMatchers.isBootstrapClassLoader; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +/** + * Instruments {@link org.jboss.logmanager.Logger#logRaw} + */ +public class JBossLogManagerCorrelationInstrumentation extends TracerAwareInstrumentation { + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singleton("jboss-logging-correlation"); + } + + @Override + public ElementMatcher.Junction getClassLoaderMatcher() { + return not(isBootstrapClassLoader()) + .and(classLoaderCanLoadClass("org.jboss.logmanager.Logger")); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named("org.jboss.logmanager.Logger"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("logRaw").and(takesArgument(0, named("org.jboss.logmanager.ExtLogRecord"))); + } + + public static class AdviceClass { + private static final JBossLogManagerCorrelationHelper helper = new JBossLogManagerCorrelationHelper(); + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static boolean addToMdc(@Advice.Argument(0) ExtLogRecord record) { + return helper.beforeLoggingEvent(record); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void removeFromMdc(@Advice.Enter boolean addedToMdc) { + helper.afterLoggingEvent(addedToMdc); + } + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLoggingCorrelationHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLoggingCorrelationHelper.java new file mode 100644 index 0000000000..2aff9a9345 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLoggingCorrelationHelper.java @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.jbosslogging.correlation; + +import co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper; +import org.jboss.logging.MDC; + +public class JBossLoggingCorrelationHelper extends AbstractLogCorrelationHelper.DefaultLogCorrelationHelper { + + @Override + protected void addToMdc(String key, String value) { + MDC.put(key, value); + } + + @Override + protected void removeFromMdc(String key) { + MDC.remove(key); + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLoggingCorrelationInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLoggingCorrelationInstrumentation.java new file mode 100644 index 0000000000..1d5c440789 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLoggingCorrelationInstrumentation.java @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.jbosslogging.correlation; + +import co.elastic.apm.agent.bci.TracerAwareInstrumentation; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import java.util.Collection; +import java.util.Collections; + +import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.classLoaderCanLoadClass; +import static net.bytebuddy.matcher.ElementMatchers.isBootstrapClassLoader; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; + +/** + * Instruments {@link org.jboss.logging.JDKLogger#doLog} and {@link org.jboss.logging.JDKLogger#doLogf} + * as well as {@link org.jboss.logging.JBossLogManagerLogger#doLog} and {@link org.jboss.logging.JBossLogManagerLogger#doLogf}. + * This enables log correlation when JBoss is used with JULI. + */ +public class JBossLoggingCorrelationInstrumentation extends TracerAwareInstrumentation { + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singleton("jboss-logging-correlation"); + } + + @Override + public ElementMatcher.Junction getClassLoaderMatcher() { + return not(isBootstrapClassLoader()) + .and(classLoaderCanLoadClass("org.jboss.logging.JDKLogger")); + } + + @Override + public ElementMatcher getTypeMatcher() { + return (named("org.jboss.logging.JDKLogger") + .or(named("org.jboss.logging.JBossLogManagerLogger")) + ); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("doLog").or(named("doLogf")); + } + + public static class AdviceClass { + private static final JBossLoggingCorrelationHelper helper = new JBossLoggingCorrelationHelper(); + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static boolean addToMdc() { + return helper.beforeLoggingEvent(); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void removeFromMdc(@Advice.Enter boolean addedToMdc) { + helper.afterLoggingEvent(addedToMdc); + } + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/package-info.java new file mode 100644 index 0000000000..5580a7c40a --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/java/co/elastic/apm/agent/jbosslogging/correlation/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.jbosslogging.correlation; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation new file mode 100644 index 0000000000..97c7e41a1a --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation @@ -0,0 +1,2 @@ +co.elastic.apm.agent.jbosslogging.correlation.JBossLoggingCorrelationInstrumentation +co.elastic.apm.agent.jbosslogging.correlation.JBossLogManagerCorrelationInstrumentation diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/test/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLogManagerCorrelationInstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/test/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLogManagerCorrelationInstrumentationTest.java new file mode 100644 index 0000000000..61357f37f0 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/test/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLogManagerCorrelationInstrumentationTest.java @@ -0,0 +1,137 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.jbosslogging.correlation; + +import co.elastic.apm.agent.AbstractInstrumentationTest; +import co.elastic.apm.agent.impl.TextHeaderMapAccessor; +import co.elastic.apm.agent.impl.error.ErrorCapture; +import co.elastic.apm.agent.impl.transaction.TraceContext; +import co.elastic.apm.agent.impl.transaction.Transaction; +import co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper; +import org.jboss.logmanager.ExtHandler; +import org.jboss.logmanager.ExtLogRecord; +import org.jboss.logmanager.Level; +import org.jboss.logmanager.LogManager; +import org.jboss.logmanager.Logger; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.Objects; + +import static co.elastic.apm.agent.impl.transaction.TraceContext.W3C_TRACE_PARENT_TEXTUAL_HEADER_NAME; +import static org.assertj.core.api.Assertions.assertThat; + +public class JBossLogManagerCorrelationInstrumentationTest extends AbstractInstrumentationTest { + + public static final String LOGGER_NAME = "Test-Logger"; + + private static final LoggingCorrelationVerifier loggingCorrelationVerifier = new LoggingCorrelationVerifier(); + private static Logger logger; + + private Transaction transaction; + + @BeforeAll + static void initializeLogger() { + logger = (org.jboss.logmanager.Logger) LogManager.getLogManager().getLogger(LOGGER_NAME); + logger.addHandler(new LoggingCorrelationVerifier()); + } + + @BeforeEach + public void setup() { + transaction = Objects.requireNonNull(tracer.startRootTransaction(null)).activate(); + loggingCorrelationVerifier.reset(); + } + + @AfterEach + public void tearDown() { + transaction.deactivate().end(); + } + + @Test + public void testSimple() { + assertThat(loggingCorrelationVerifier.isVerified()).isFalse(); + // TraceContext#toString() returns the text representation of the traceparent header + logger.info(transaction.getTraceContext().toString()); + assertThat(loggingCorrelationVerifier.isVerified()).isTrue(); + } + + // todo: enable once support for jboss-logmanager error logging is added + @Test + @Disabled + public void testErrorLogging() { + assertThat(loggingCorrelationVerifier.isVerified()).isFalse(); + // TraceContext#toString() returns the text representation of the traceparent header + logger.log(Level.ERROR, transaction.getTraceContext().toString(), new RuntimeException()); + assertThat(loggingCorrelationVerifier.isVerified()).isTrue(); + } + + private static class LoggingCorrelationVerifier extends ExtHandler { + private boolean verified; + + public void reset() { + verified = false; + } + + public boolean isVerified() { + return verified; + } + + public void setVerified() { + verified = true; + } + + @Override + protected void doPublish(ExtLogRecord record) { + try { + Object traceId = record.getMdc(AbstractLogCorrelationHelper.TRACE_ID_MDC_KEY); + System.out.println("traceId = " + traceId); + assertThat(traceId).isNotNull(); + Object transactionId = record.getMdc(AbstractLogCorrelationHelper.TRANSACTION_ID_MDC_KEY); + System.out.println("transactionId = " + transactionId); + assertThat(transactionId).isNotNull(); + Object errorId = record.getMdc(AbstractLogCorrelationHelper.ERROR_ID_MDC_KEY); + System.out.println("errorId = " + transactionId); + boolean shouldContainErrorId = record.getLevel().getName().equals("ERROR"); + String traceParent = record.getMessage(); + assertThat(traceParent).isNotNull(); + Map textHeaderMap = Map.of(W3C_TRACE_PARENT_TEXTUAL_HEADER_NAME, traceParent); + TraceContext childTraceContext = TraceContext.with64BitId(tracer); + TraceContext.>getFromTraceContextTextHeaders().asChildOf(childTraceContext, textHeaderMap, TextHeaderMapAccessor.INSTANCE); + System.out.println("childTraceContext = " + childTraceContext); + assertThat(childTraceContext.getTraceId().toString()).isEqualTo(traceId.toString()); + assertThat(childTraceContext.getParentId().toString()).isEqualTo(transactionId.toString()); + if (shouldContainErrorId) { + assertThat(errorId).isNotNull(); + ErrorCapture activeError = ErrorCapture.getActive(); + assertThat(activeError).isNotNull(); + assertThat(activeError.getTraceContext().getId().toString()).isEqualTo(errorId.toString()); + } else { + assertThat(errorId).isNull(); + } + loggingCorrelationVerifier.setVerified(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/test/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLoggingCorrelationInstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/test/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLoggingCorrelationInstrumentationTest.java new file mode 100644 index 0000000000..97407028de --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/src/test/java/co/elastic/apm/agent/jbosslogging/correlation/JBossLoggingCorrelationInstrumentationTest.java @@ -0,0 +1,151 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.jbosslogging.correlation; + +import co.elastic.apm.agent.AbstractInstrumentationTest; +import co.elastic.apm.agent.impl.TextHeaderMapAccessor; +import co.elastic.apm.agent.impl.error.ErrorCapture; +import co.elastic.apm.agent.impl.transaction.TraceContext; +import co.elastic.apm.agent.impl.transaction.Transaction; +import co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper; +import org.jboss.logging.Logger; +import org.jboss.logging.MDC; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.Objects; +import java.util.logging.Handler; +import java.util.logging.LogRecord; + +import static co.elastic.apm.agent.impl.transaction.TraceContext.W3C_TRACE_PARENT_TEXTUAL_HEADER_NAME; +import static org.assertj.core.api.Assertions.assertThat; + +public class JBossLoggingCorrelationInstrumentationTest extends AbstractInstrumentationTest { + + public static final String LOGGER_NAME = "Test-Logger"; + + private static final LoggingCorrelationVerifier loggingCorrelationVerifier = new LoggingCorrelationVerifier(); + private static Logger logger; + + private Transaction transaction; + + @BeforeAll + static void initializeLogger() { + System.setProperty("org.jboss.logging.provider", "jdk"); + logger = Logger.getLogger(LOGGER_NAME); + + // Getting the underlying JULI logger in order to set a handler + java.util.logging.Logger juliLogger = java.util.logging.Logger.getLogger(LOGGER_NAME); + assertThat(juliLogger).isNotNull(); + juliLogger.addHandler(new LoggingCorrelationVerifier()); + } + + @BeforeEach + public void setup() { + transaction = Objects.requireNonNull(tracer.startRootTransaction(null)).activate(); + loggingCorrelationVerifier.reset(); + } + + @AfterEach + public void tearDown() { + transaction.deactivate().end(); + } + + @Test + public void testSimple() { + assertThat(loggingCorrelationVerifier.isVerified()).isFalse(); + // TraceContext#toString() returns the text representation of the traceparent header + logger.info(transaction.getTraceContext()); + assertThat(loggingCorrelationVerifier.isVerified()).isTrue(); + } + + // todo: enable once support for JULI error logging is added + @Test + @Disabled + public void testErrorLogging() { + assertThat(loggingCorrelationVerifier.isVerified()).isFalse(); + // TraceContext#toString() returns the text representation of the traceparent header + logger.error(transaction.getTraceContext()); + assertThat(loggingCorrelationVerifier.isVerified()).isTrue(); + } + + private static class LoggingCorrelationVerifier extends Handler { + private boolean verified; + + public void reset() { + verified = false; + } + + public boolean isVerified() { + return verified; + } + + public void setVerified() { + verified = true; + } + + @Override + public void publish(LogRecord record) { + try { + Object traceId = MDC.get(AbstractLogCorrelationHelper.TRACE_ID_MDC_KEY); + System.out.println("traceId = " + traceId); + assertThat(traceId).isNotNull(); + Object transactionId = MDC.get(AbstractLogCorrelationHelper.TRANSACTION_ID_MDC_KEY); + System.out.println("transactionId = " + transactionId); + assertThat(transactionId).isNotNull(); + Object errorId = MDC.get(AbstractLogCorrelationHelper.ERROR_ID_MDC_KEY); + System.out.println("errorId = " + transactionId); + boolean shouldContainErrorId = record.getLevel().getName().equals("ERROR"); + String traceParent = record.getMessage(); + assertThat(traceParent).isNotNull(); + Map textHeaderMap = Map.of(W3C_TRACE_PARENT_TEXTUAL_HEADER_NAME, traceParent); + TraceContext childTraceContext = TraceContext.with64BitId(tracer); + TraceContext.>getFromTraceContextTextHeaders().asChildOf(childTraceContext, textHeaderMap, TextHeaderMapAccessor.INSTANCE); + System.out.println("childTraceContext = " + childTraceContext); + assertThat(childTraceContext.getTraceId().toString()).isEqualTo(traceId.toString()); + assertThat(childTraceContext.getParentId().toString()).isEqualTo(transactionId.toString()); + if (shouldContainErrorId) { + assertThat(errorId).isNotNull(); + ErrorCapture activeError = ErrorCapture.getActive(); + assertThat(activeError).isNotNull(); + assertThat(activeError.getTraceContext().getId().toString()).isEqualTo(errorId.toString()); + } else { + assertThat(errorId).isNull(); + } + loggingCorrelationVerifier.setVerified(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } + + @Override + public void flush() { + + } + + @Override + public void close() throws SecurityException { + + } + } +} diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/pom.xml similarity index 88% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/pom.xml rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/pom.xml index afb3c251bc..f907224e71 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - apm-log-shader-plugin + apm-logging-plugin co.elastic.apm 1.29.1-SNAPSHOT @@ -18,7 +18,7 @@ ${project.groupId} - apm-log-shader-plugin-common + apm-logging-plugin-common ${project.version} @@ -34,7 +34,7 @@ ${project.groupId} - apm-log-shader-plugin-common + apm-logging-plugin-common ${project.version} test-jar test diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1PluginClassLoaderRootPackageCustomizer.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1PluginClassLoaderRootPackageCustomizer.java similarity index 73% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1PluginClassLoaderRootPackageCustomizer.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1PluginClassLoaderRootPackageCustomizer.java index 47a1c3352d..cae6e0a1cb 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1PluginClassLoaderRootPackageCustomizer.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1PluginClassLoaderRootPackageCustomizer.java @@ -18,14 +18,6 @@ */ package co.elastic.apm.agent.log4j1; -import co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer; +import co.elastic.apm.agent.loginstr.LoggingPluginClassLoaderRootPackageCustomizer; -import java.util.Arrays; -import java.util.Collection; - -public class Log4j1PluginClassLoaderRootPackageCustomizer extends PluginClassLoaderRootPackageCustomizer { - @Override - public Collection pluginClassLoaderRootPackages() { - return Arrays.asList(getPluginPackage(), "co.elastic.logging"); - } -} +public class Log4j1PluginClassLoaderRootPackageCustomizer extends LoggingPluginClassLoaderRootPackageCustomizer {} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/correlation/Log4j1LogCorrelationHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/correlation/Log4j1LogCorrelationHelper.java new file mode 100644 index 0000000000..220adf050f --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/correlation/Log4j1LogCorrelationHelper.java @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j1.correlation; + +import co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper; +import org.apache.log4j.MDC; + +public class Log4j1LogCorrelationHelper extends AbstractLogCorrelationHelper.DefaultLogCorrelationHelper { + + @Override + protected void addToMdc(String key, String value) { + MDC.put(key, value); + } + + @Override + protected void removeFromMdc(String key) { + MDC.remove(key); + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/correlation/Log4j1LogCorrelationInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/correlation/Log4j1LogCorrelationInstrumentation.java new file mode 100644 index 0000000000..f7e963373c --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/correlation/Log4j1LogCorrelationInstrumentation.java @@ -0,0 +1,71 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j1.correlation; + +import co.elastic.apm.agent.bci.TracerAwareInstrumentation; +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.log4j.spi.LoggingEvent; + +import java.util.Collection; +import java.util.Collections; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +/** + * Instruments {@link org.apache.log4j.Category#callAppenders(LoggingEvent)} + */ +public class Log4j1LogCorrelationInstrumentation extends TracerAwareInstrumentation { + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singleton("log4j1-correlation"); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named("org.apache.log4j.Category"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("callAppenders") + .and(takesArguments(1)) + .and(takesArgument(0, named("org.apache.log4j.spi.LoggingEvent"))); + } + + public static class AdviceClass { + + private static final Log4j1LogCorrelationHelper helper = new Log4j1LogCorrelationHelper(); + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static boolean addToMdc() { + return helper.beforeLoggingEvent(); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void removeFromMdc(@Advice.Enter boolean addedToMdc) { + helper.afterLoggingEvent(addedToMdc); + } + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/correlation/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/correlation/package-info.java new file mode 100644 index 0000000000..043c14c5d9 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/correlation/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.log4j1.correlation; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/error/Log4j1LoggerErrorCapturingInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/error/Log4j1LoggerErrorCapturingInstrumentation.java new file mode 100644 index 0000000000..623d3d4077 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/error/Log4j1LoggerErrorCapturingInstrumentation.java @@ -0,0 +1,59 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j1.error; + +import co.elastic.apm.agent.loginstr.error.AbstractLoggerErrorCapturingInstrumentation; +import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import java.util.Collection; + +import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +/** + * Instruments {@link org.apache.log4j.Category#error(Object, Throwable)} and {@link org.apache.log4j.Category#fatal(Object, Throwable)} + */ +public class Log4j1LoggerErrorCapturingInstrumentation extends AbstractLoggerErrorCapturingInstrumentation { + + @Override + public ElementMatcher getTypeMatcherPreFilter() { + return any(); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named("org.apache.log4j.Category"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("fatal").and(takesArgument(1, named("java.lang.Throwable"))).or(super.getMethodMatcher()); + } + + @Override + public Collection getInstrumentationGroupNames() { + Collection ret = super.getInstrumentationGroupNames(); + ret.add("log4j1-error"); + return ret; + } +} diff --git a/apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/error/package-info.java similarity index 95% rename from apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/package-info.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/error/package-info.java index 782cd27e4e..e2c78abad5 100644 --- a/apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/package-info.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/error/package-info.java @@ -17,6 +17,6 @@ * under the License. */ @NonnullApi -package co.elastic.apm.agent.errorlogging; +package co.elastic.apm.agent.log4j1.error; import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/package-info.java similarity index 100% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/package-info.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/package-info.java diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4J1EcsReformattingHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4J1EcsReformattingHelper.java similarity index 91% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4J1EcsReformattingHelper.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4J1EcsReformattingHelper.java index 64c8683aa2..b83f819bbb 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4J1EcsReformattingHelper.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4J1EcsReformattingHelper.java @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log4j1; +package co.elastic.apm.agent.log4j1.reformatting; -import co.elastic.apm.agent.log.shader.AbstractEcsReformattingHelper; -import co.elastic.apm.agent.log.shader.Utils; +import co.elastic.apm.agent.loginstr.reformatting.AbstractEcsReformattingHelper; +import co.elastic.apm.agent.loginstr.reformatting.Utils; import co.elastic.apm.agent.sdk.logging.Logger; import co.elastic.apm.agent.sdk.logging.LoggerFactory; @@ -79,9 +79,9 @@ protected WriterAppender createAndStartEcsAppender(WriterAppender originalAppend if (originalAppender instanceof FileAppender) { try { FileAppender fileAppender = (FileAppender) originalAppender; - String shadeFile = Utils.computeShadeLogFilePath(fileAppender.getFile(), getConfiguredShadeDir()); + String reformattedFile = Utils.computeLogReformattingFilePath(fileAppender.getFile(), getConfiguredReformattingDir()); - shadeAppender = new RollingFileAppender(ecsLayout, shadeFile, true); + shadeAppender = new RollingFileAppender(ecsLayout, reformattedFile, true); shadeAppender.setMaxBackupIndex(1); shadeAppender.setMaximumFileSize(getMaxLogFileSize()); shadeAppender.setImmediateFlush(originalAppender.getImmediateFlush()); diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1AppenderAppendAdvice.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4j1AppenderAppendAdvice.java similarity index 73% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1AppenderAppendAdvice.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4j1AppenderAppendAdvice.java index b032731970..92d565a34b 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1AppenderAppendAdvice.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4j1AppenderAppendAdvice.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log4j1; +package co.elastic.apm.agent.log4j1.reformatting; import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -29,15 +29,15 @@ public class Log4j1AppenderAppendAdvice { @SuppressWarnings("unused") @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class, inline = false) - public static boolean shadeAndSkipIfOverrideEnabled(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final LoggingEvent eventObject, - @Advice.This(typing = Assigner.Typing.DYNAMIC) WriterAppender thisAppender) { + public static boolean initializeReformatting(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final LoggingEvent eventObject, + @Advice.This(typing = Assigner.Typing.DYNAMIC) WriterAppender thisAppender) { return helper.onAppendEnter(thisAppender); } @SuppressWarnings({"unused"}) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) - public static void shadeLoggingEvent(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final LoggingEvent eventObject, - @Advice.This(typing = Assigner.Typing.DYNAMIC) WriterAppender thisAppender) { + public static void reformatLoggingEvent(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final LoggingEvent eventObject, + @Advice.This(typing = Assigner.Typing.DYNAMIC) WriterAppender thisAppender) { WriterAppender shadeAppender = helper.onAppendExit(thisAppender); if (shadeAppender != null) { diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1AppenderStopAdvice.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4j1AppenderStopAdvice.java similarity index 96% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1AppenderStopAdvice.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4j1AppenderStopAdvice.java index c4b7116afd..df99b1ac8e 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1AppenderStopAdvice.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4j1AppenderStopAdvice.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log4j1; +package co.elastic.apm.agent.log4j1.reformatting; import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bytecode.assign.Assigner; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1LogShadingInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4j1LogReformattingInstrumentation.java similarity index 83% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1LogShadingInstrumentation.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4j1LogReformattingInstrumentation.java index 72ac175e0d..c3843fb4db 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/Log4j1LogShadingInstrumentation.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/Log4j1LogReformattingInstrumentation.java @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log4j1; +package co.elastic.apm.agent.log4j1.reformatting; -import co.elastic.apm.agent.log.shader.AbstractLogShadingInstrumentation; +import co.elastic.apm.agent.loginstr.AbstractLogIntegrationInstrumentation; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -32,7 +32,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; -public abstract class Log4j1LogShadingInstrumentation extends AbstractLogShadingInstrumentation { +public abstract class Log4j1LogReformattingInstrumentation extends AbstractLogIntegrationInstrumentation { @Override public Collection getInstrumentationGroupNames() { @@ -52,7 +52,7 @@ public ElementMatcher getTypeMatcher() { return named("org.apache.log4j.WriterAppender"); } - public static class ShadingInstrumentation extends Log4j1LogShadingInstrumentation { + public static class ReformattingInstrumentation extends Log4j1LogReformattingInstrumentation { /** * Instrumenting {@link org.apache.log4j.WriterAppender#subAppend(org.apache.log4j.spi.LoggingEvent)} @@ -64,12 +64,12 @@ public ElementMatcher getMethodMatcher() { @Override public String getAdviceClassName() { - return "co.elastic.apm.agent.log4j1.Log4j1AppenderAppendAdvice"; + return "co.elastic.apm.agent.log4j1.reformatting.Log4j1AppenderAppendAdvice"; } } - public static class StopAppenderInstrumentation extends Log4j1LogShadingInstrumentation { + public static class StopAppenderInstrumentation extends Log4j1LogReformattingInstrumentation { /** * Instrumenting {@link org.apache.log4j.WriterAppender#close()} @@ -81,7 +81,7 @@ public ElementMatcher getMethodMatcher() { @Override public String getAdviceClassName() { - return "co.elastic.apm.agent.log4j1.Log4j1AppenderStopAdvice"; + return "co.elastic.apm.agent.log4j1.reformatting.Log4j1AppenderStopAdvice"; } } diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/package-info.java new file mode 100644 index 0000000000..1ac21293f5 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/java/co/elastic/apm/agent/log4j1/reformatting/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.log4j1.reformatting; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer similarity index 100% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation new file mode 100644 index 0000000000..70a88d3421 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation @@ -0,0 +1,9 @@ +# ECS re-formatting +co.elastic.apm.agent.log4j1.reformatting.Log4j1LogReformattingInstrumentation$ReformattingInstrumentation +co.elastic.apm.agent.log4j1.reformatting.Log4j1LogReformattingInstrumentation$StopAppenderInstrumentation + +# Trace context correlation +co.elastic.apm.agent.log4j1.correlation.Log4j1LogCorrelationInstrumentation + +# Error creation +co.elastic.apm.agent.log4j1.error.Log4j1LoggerErrorCapturingInstrumentation diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/test/java/co/elastic/apm/agent/log4j1/Log4j1ShadingTest.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/java/co/elastic/apm/agent/log4j1/Log4j1InstrumentationTest.java similarity index 90% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/test/java/co/elastic/apm/agent/log4j1/Log4j1ShadingTest.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/java/co/elastic/apm/agent/log4j1/Log4j1InstrumentationTest.java index b5f816b0e6..a55c2cedb7 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/test/java/co/elastic/apm/agent/log4j1/Log4j1ShadingTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/java/co/elastic/apm/agent/log4j1/Log4j1InstrumentationTest.java @@ -18,8 +18,8 @@ */ package co.elastic.apm.agent.log4j1; -import co.elastic.apm.agent.log.shader.LogShadingInstrumentationTest; -import co.elastic.apm.agent.log.shader.LoggerFacade; +import co.elastic.apm.agent.loginstr.LoggingInstrumentationTest; +import co.elastic.apm.agent.loginstr.LoggerFacade; import org.apache.log4j.FileAppender; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; @@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -public class Log4j1ShadingTest extends LogShadingInstrumentationTest { +public class Log4j1InstrumentationTest extends LoggingInstrumentationTest { @Override protected LoggerFacade createLoggerFacade() { @@ -41,7 +41,7 @@ protected LoggerFacade createLoggerFacade() { @Override protected void waitForFileRolling() { - await().untilAsserted(() -> assertThat(new File(getShadeLogFilePath()).length()).isEqualTo(0)); + await().untilAsserted(() -> assertThat(new File(getLogReformattingFilePath()).length()).isEqualTo(0)); } private static class Log4j1LoggerFacade implements LoggerFacade { diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/java/co/elastic/apm/agent/log4j1/error/Log4j1LoggerErrorCapturingInstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/java/co/elastic/apm/agent/log4j1/error/Log4j1LoggerErrorCapturingInstrumentationTest.java new file mode 100644 index 0000000000..be7d8f67b7 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/java/co/elastic/apm/agent/log4j1/error/Log4j1LoggerErrorCapturingInstrumentationTest.java @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j1.error; + +import co.elastic.apm.agent.loginstr.error.AbstractErrorLoggingInstrumentationTest; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.junit.jupiter.api.Test; + +class Log4j1LoggerErrorCapturingInstrumentationTest extends AbstractErrorLoggingInstrumentationTest { + + private static final Logger logger = LogManager.getLogger(Log4j1LoggerErrorCapturingInstrumentationTest.class); + + @Test + void captureErrorExceptionWithStringMessage() { + logger.error("exception captured", new RuntimeException("some business exception")); + verifyThatExceptionCaptured(1, "some business exception", RuntimeException.class); + } + + @Test + void captureFatalException() { + logger.fatal("exception captured", new RuntimeException("some business exception")); + verifyThatExceptionCaptured(1, "some business exception", RuntimeException.class); + } +} diff --git a/apm-agent-plugins/apm-log-correlation-plugin/src/test/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation similarity index 100% rename from apm-agent-plugins/apm-log-correlation-plugin/src/test/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/test/resources/log4j1.properties b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/resources/log4j1.properties similarity index 100% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j1-plugin/src/test/resources/log4j1.properties rename to apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/src/test/resources/log4j1.properties diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/pom.xml similarity index 89% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/pom.xml rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/pom.xml index 38fe67cc13..334a39ad13 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - apm-log-shader-plugin + apm-logging-plugin co.elastic.apm 1.29.1-SNAPSHOT @@ -18,7 +18,7 @@ ${project.groupId} - apm-log-shader-plugin-common + apm-logging-plugin-common ${project.version} @@ -34,7 +34,7 @@ ${project.groupId} - apm-log-shader-plugin-common + apm-logging-plugin-common ${project.version} test-jar test diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2PluginClassLoaderRootPackageCustomizer.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2PluginClassLoaderRootPackageCustomizer.java similarity index 73% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2PluginClassLoaderRootPackageCustomizer.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2PluginClassLoaderRootPackageCustomizer.java index a9457449e3..2a6e34f8f0 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2PluginClassLoaderRootPackageCustomizer.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2PluginClassLoaderRootPackageCustomizer.java @@ -18,14 +18,6 @@ */ package co.elastic.apm.agent.log4j2; -import co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer; +import co.elastic.apm.agent.loginstr.LoggingPluginClassLoaderRootPackageCustomizer; -import java.util.Arrays; -import java.util.Collection; - -public class Log4j2PluginClassLoaderRootPackageCustomizer extends PluginClassLoaderRootPackageCustomizer { - @Override - public Collection pluginClassLoaderRootPackages() { - return Arrays.asList(getPluginPackage(), "co.elastic.logging"); - } -} +public class Log4j2PluginClassLoaderRootPackageCustomizer extends LoggingPluginClassLoaderRootPackageCustomizer {} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/CorrelationIdMapAdapter.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/CorrelationIdMapAdapter.java new file mode 100644 index 0000000000..6ef8abaeb9 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/CorrelationIdMapAdapter.java @@ -0,0 +1,179 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j2.correlation; + +import co.elastic.apm.agent.impl.GlobalTracer; +import co.elastic.apm.agent.impl.Tracer; +import co.elastic.apm.agent.impl.error.ErrorCapture; +import co.elastic.apm.agent.impl.transaction.AbstractSpan; + +import javax.annotation.Nullable; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.Callable; + +import static co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper.ERROR_ID_MDC_KEY; +import static co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper.TRACE_ID_MDC_KEY; +import static co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper.TRANSACTION_ID_MDC_KEY; + +class CorrelationIdMapAdapter extends AbstractMap { + + private static final CorrelationIdMapAdapter INSTANCE = new CorrelationIdMapAdapter(); + private static final Set> ENTRY_SET = new TraceIdentifierEntrySet(); + private static final List ALL_KEYS = Arrays.asList(TRACE_ID_MDC_KEY, TRANSACTION_ID_MDC_KEY, ERROR_ID_MDC_KEY); + private static final Tracer tracer = GlobalTracer.get(); + private static final List> ENTRIES = Arrays.>asList( + new LazyEntry(TRACE_ID_MDC_KEY, new Callable() { + @Override + @Nullable + public String call() { + AbstractSpan activeSpan = tracer.getActive(); + if (activeSpan == null) { + return null; + } + return activeSpan.getTraceContext().getTraceId().toString(); + } + }), + new LazyEntry(TRANSACTION_ID_MDC_KEY, new Callable() { + @Override + @Nullable + public String call() { + AbstractSpan activeSpan = tracer.getActive(); + if (activeSpan == null) { + return null; + } + return activeSpan.getTraceContext().getTransactionId().toString(); + } + }), + new LazyEntry(ERROR_ID_MDC_KEY, new Callable() { + @Override + @Nullable + public String call() { + ErrorCapture error = ErrorCapture.getActive(); + if (error == null) { + return null; + } + return error.getTraceContext().getId().toString(); + } + }) + ); + + public static Map get() { + return INSTANCE; + } + + public static Iterable allKeys() { + return ALL_KEYS; + } + + private CorrelationIdMapAdapter() { + } + + @Override + public Set> entrySet() { + return ENTRY_SET; + } + + private static class TraceIdentifierEntrySet extends AbstractSet> { + + @Override + public int size() { + int size = 0; + for (Entry ignored : this) { + size++; + } + return size; + } + + @Override + public Iterator> iterator() { + return new Iterator>() { + private int i = 0; + @Nullable + private Entry next = findNext(); + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public Entry next() { + if (next != null) { + try { + return next; + } finally { + next = findNext(); + } + } else { + throw new NoSuchElementException(); + } + } + + @Nullable + private Entry findNext() { + Entry next = null; + while (next == null && i < ENTRIES.size()) { + next = ENTRIES.get(i++); + if (next.getValue() == null) { + next = null; + } + } + return next; + } + }; + } + + } + + private static class LazyEntry implements Entry { + private final String key; + private final Callable valueSupplier; + + private LazyEntry(String key, Callable valueSupplier) { + this.key = key; + this.valueSupplier = valueSupplier; + } + + @Override + public String getKey() { + return this.key; + } + + @Override + public String getValue() { + try { + return this.valueSupplier.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public String setValue(String value) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/Log4j2LogCorrelationInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/Log4j2LogCorrelationInstrumentation.java new file mode 100644 index 0000000000..dd758d4dd2 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/Log4j2LogCorrelationInstrumentation.java @@ -0,0 +1,119 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j2.correlation; + +import co.elastic.apm.agent.bci.TracerAwareInstrumentation; +import co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import java.security.ProtectionDomain; +import java.util.Collection; +import java.util.Collections; + +import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.classLoaderCanLoadClass; +import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.implementationVersionGte; +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isBootstrapClassLoader; +import static net.bytebuddy.matcher.ElementMatchers.nameEndsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; + +/** + * Instruments {@link org.apache.logging.log4j.core.impl.LogEventFactory#createEvent} + */ +public abstract class Log4j2LogCorrelationInstrumentation extends TracerAwareInstrumentation { + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singleton("log4j2-correlation"); + } + + @Override + public ElementMatcher.Junction getClassLoaderMatcher() { + return not(isBootstrapClassLoader()) + // Do not instrument the agent log4j2 lib + .and(not(CustomElementMatchers.isAgentClassLoader())) + .and(classLoaderCanLoadClass("org.apache.logging.log4j.core.impl.LogEventFactory")); + } + + @Override + public ElementMatcher getTypeMatcherPreFilter() { + return nameEndsWith("LogEventFactory"); + } + + @Override + public ElementMatcher getTypeMatcher() { + return hasSuperType( + named("org.apache.logging.log4j.core.impl.LogEventFactory") + .or(named("org.apache.logging.log4j.core.impl.LocationAwareLogEventFactory")) + ); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("createEvent"); + } + + public static class Log4J2_6LogCorrelationInstrumentation extends Log4j2LogCorrelationInstrumentation { + @Override + public ElementMatcher.Junction getProtectionDomainPostFilter() { + return implementationVersionGte("2.6").and(not(implementationVersionGte("2.7"))); + } + + public static class AdviceClass { + private static final Log4j2_6LogCorrelationHelper helper = new Log4j2_6LogCorrelationHelper(); + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static boolean addToThreadContext() { + return helper.beforeLoggingEvent(); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void removeFromThreadContext(@Advice.Enter boolean addedToMdc) { + helper.afterLoggingEvent(addedToMdc); + } + } + } + + public static class Log4J2_7PlusLogCorrelationInstrumentation extends Log4j2LogCorrelationInstrumentation { + @Override + public ElementMatcher.Junction getProtectionDomainPostFilter() { + return implementationVersionGte("2.7"); + } + + public static class AdviceClass { + + private static final Log4j2_7PlusLogCorrelationHelper helper = new Log4j2_7PlusLogCorrelationHelper(); + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static boolean addToThreadContext() { + return helper.beforeLoggingEvent(); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void removeFromThreadContext(@Advice.Enter boolean addedToThreadContext) { + helper.afterLoggingEvent(addedToThreadContext); + } + } + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/Log4j2_6LogCorrelationHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/Log4j2_6LogCorrelationHelper.java new file mode 100644 index 0000000000..62028da7d1 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/Log4j2_6LogCorrelationHelper.java @@ -0,0 +1,40 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j2.correlation; + +import co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper; +import org.apache.logging.log4j.ThreadContext; + +import java.util.Map; + +/** + * In log4j 2.6 {@link ThreadContext#putAll(Map)} is not yet available + */ +public class Log4j2_6LogCorrelationHelper extends AbstractLogCorrelationHelper.DefaultLogCorrelationHelper { + + @Override + protected void addToMdc(String key, String value) { + ThreadContext.put(key, value); + } + + @Override + protected void removeFromMdc(String key) { + ThreadContext.remove(key); + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/Log4j2_7PlusLogCorrelationHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/Log4j2_7PlusLogCorrelationHelper.java new file mode 100644 index 0000000000..1d5547c2a0 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/Log4j2_7PlusLogCorrelationHelper.java @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j2.correlation; + +import co.elastic.apm.agent.impl.GlobalTracer; +import co.elastic.apm.agent.impl.Tracer; +import co.elastic.apm.agent.impl.error.ErrorCapture; +import co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper; +import org.apache.logging.log4j.ThreadContext; + +import java.util.Map; + +/** + * Using {@link ThreadContext#putAll(Map)} that is available since 2.7 for improved efficiency + */ +public class Log4j2_7PlusLogCorrelationHelper extends AbstractLogCorrelationHelper { + + private final Tracer tracer = GlobalTracer.get(); + + @Override + protected boolean addToMdc() { + if (tracer.currentTransaction() == null && ErrorCapture.getActive() == null) { + return false; + } + ThreadContext.putAll(CorrelationIdMapAdapter.get()); + return true; + } + + @Override + protected void removeFromMdc() { + ThreadContext.removeAll(CorrelationIdMapAdapter.allKeys()); + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/package-info.java new file mode 100644 index 0000000000..f1775118ea --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/correlation/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.log4j2.correlation; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/Log4j2LoggerErrorCapturingInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/error/Log4j2LoggerErrorCapturingInstrumentation.java similarity index 77% rename from apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/Log4j2LoggerErrorCapturingInstrumentation.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/error/Log4j2LoggerErrorCapturingInstrumentation.java index ebcce601d1..7fa79c15db 100644 --- a/apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/Log4j2LoggerErrorCapturingInstrumentation.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/error/Log4j2LoggerErrorCapturingInstrumentation.java @@ -16,24 +16,26 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.errorlogging; +package co.elastic.apm.agent.log4j2.error; +import co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers; +import co.elastic.apm.agent.loginstr.error.AbstractLoggerErrorCapturingInstrumentation; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import java.util.Collection; -import static co.elastic.apm.agent.errorlogging.Slf4jLoggerErrorCapturingInstrumentation.SLF4J_LOGGER; import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +/** + * Instruments {@link org.apache.logging.log4j.Logger#error(String, Throwable)} and {@link org.apache.logging.log4j.Logger#fatal(Object, Throwable)} + */ public class Log4j2LoggerErrorCapturingInstrumentation extends AbstractLoggerErrorCapturingInstrumentation { - static final String LOG4J2_LOGGER = "org.apache.logging.log4j.Logger"; - @Override public ElementMatcher getTypeMatcher() { return hasSuperType(named(LOG4J2_LOGGER)).and(not(hasSuperType(named(SLF4J_LOGGER)))); @@ -50,4 +52,10 @@ public Collection getInstrumentationGroupNames() { ret.add("log4j2-error"); return ret; } + + @Override + public ElementMatcher.Junction getClassLoaderMatcher() { + // Do not instrument the internal agent log4j2 loggers + return not(CustomElementMatchers.isAgentClassLoader()); + } } diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/error/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/error/package-info.java new file mode 100644 index 0000000000..c02c08ac08 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/error/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.log4j2.error; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/package-info.java similarity index 100% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/package-info.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/package-info.java diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4J2EcsReformattingHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4J2EcsReformattingHelper.java similarity index 94% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4J2EcsReformattingHelper.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4J2EcsReformattingHelper.java index 322dddff22..eebd270e0f 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4J2EcsReformattingHelper.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4J2EcsReformattingHelper.java @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log4j2; +package co.elastic.apm.agent.log4j2.reformatting; -import co.elastic.apm.agent.log.shader.AbstractEcsReformattingHelper; -import co.elastic.apm.agent.log.shader.Utils; +import co.elastic.apm.agent.loginstr.reformatting.AbstractEcsReformattingHelper; +import co.elastic.apm.agent.loginstr.reformatting.Utils; import co.elastic.apm.agent.sdk.logging.Logger; import co.elastic.apm.agent.sdk.logging.LoggerFactory; import co.elastic.logging.log4j2.EcsLayout; @@ -112,7 +112,7 @@ protected Appender createAndStartEcsAppender(Appender originalAppender, String e } if (logFile != null) { - String shadeFile = Utils.computeShadeLogFilePath(logFile, getConfiguredShadeDir()); + String ecsLogFile = Utils.computeLogReformattingFilePath(logFile, getConfiguredReformattingDir()); // The deprecated configuration API is used in order to support older versions where the Builder API is not yet available //noinspection deprecation @@ -123,7 +123,7 @@ protected Appender createAndStartEcsAppender(Appender originalAppender, String e // The deprecated configuration API is used in order to support older versions where the Builder API is not yet available //noinspection deprecation - ecsAppender = RollingRandomAccessFileAppender.createAppender(shadeFile, shadeFile + ".%i", "true", + ecsAppender = RollingRandomAccessFileAppender.createAppender(ecsLogFile, ecsLogFile + ".%i", "true", ecsAppenderName, String.valueOf(((AbstractOutputStreamAppender) originalAppender).getImmediateFlush()), null, triggeringPolicy, rolloverStrategy, ecsFormatter, null, null, null, null, null); diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderAppendAdvice.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2AppenderAppendAdvice.java similarity index 74% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderAppendAdvice.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2AppenderAppendAdvice.java index 1384406a20..002adc8a5e 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderAppendAdvice.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2AppenderAppendAdvice.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log4j2; +package co.elastic.apm.agent.log4j2.reformatting; import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -29,15 +29,15 @@ public class Log4j2AppenderAppendAdvice { @SuppressWarnings("unused") @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class, inline = false) - public static boolean shadeAndSkipIfReplaceEnabled(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final LogEvent eventObject, - @Advice.This(typing = Assigner.Typing.DYNAMIC) Appender thisAppender) { + public static boolean initializeReformatting(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final LogEvent eventObject, + @Advice.This(typing = Assigner.Typing.DYNAMIC) Appender thisAppender) { return helper.onAppendEnter(thisAppender); } @SuppressWarnings({"unused"}) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) - public static void shadeLoggingEvent(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final LogEvent eventObject, - @Advice.This(typing = Assigner.Typing.DYNAMIC) Appender thisAppender) { + public static void reformatLoggingEvent(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final LogEvent eventObject, + @Advice.This(typing = Assigner.Typing.DYNAMIC) Appender thisAppender) { Appender shadeAppender = helper.onAppendExit(thisAppender); if (shadeAppender != null) { diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderGetLayoutAdvice.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2AppenderGetLayoutAdvice.java similarity index 88% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderGetLayoutAdvice.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2AppenderGetLayoutAdvice.java index 15dd91f1cc..a60475e6e5 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderGetLayoutAdvice.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2AppenderGetLayoutAdvice.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log4j2; +package co.elastic.apm.agent.log4j2.reformatting; import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -40,8 +40,8 @@ public class Log4j2AppenderGetLayoutAdvice { @Nullable @Advice.AssignReturned.ToReturned @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) - public static Layout shadeLoggingEvent(@Advice.This(typing = Assigner.Typing.DYNAMIC) Appender thisAppender, - @Advice.Return @Nullable Layout originalLayout) { + public static Layout reformatLoggingEvent(@Advice.This(typing = Assigner.Typing.DYNAMIC) Appender thisAppender, + @Advice.Return @Nullable Layout originalLayout) { if (originalLayout == null) { // Effectively disables instrumentation to all database appenders diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderStopAdvice.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2AppenderStopAdvice.java similarity index 96% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderStopAdvice.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2AppenderStopAdvice.java index 358897c3d7..6a5e59147d 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2AppenderStopAdvice.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2AppenderStopAdvice.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log4j2; +package co.elastic.apm.agent.log4j2.reformatting; import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bytecode.assign.Assigner; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2EcsReformattingInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2EcsReformattingInstrumentation.java similarity index 88% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2EcsReformattingInstrumentation.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2EcsReformattingInstrumentation.java index 197a047e65..184afb0354 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/Log4j2EcsReformattingInstrumentation.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/Log4j2EcsReformattingInstrumentation.java @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log4j2; +package co.elastic.apm.agent.log4j2.reformatting; import co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers; -import co.elastic.apm.agent.log.shader.AbstractLogShadingInstrumentation; +import co.elastic.apm.agent.loginstr.AbstractLogIntegrationInstrumentation; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -36,7 +36,7 @@ import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; -public abstract class Log4j2EcsReformattingInstrumentation extends AbstractLogShadingInstrumentation { +public abstract class Log4j2EcsReformattingInstrumentation extends AbstractLogIntegrationInstrumentation { @Override public Collection getInstrumentationGroupNames() { @@ -48,6 +48,7 @@ public Collection getInstrumentationGroupNames() { @Override public ElementMatcher.Junction getClassLoaderMatcher() { return not(isBootstrapClassLoader()) + // Do not instrument the internal agent log4j2 appenders .and(not(CustomElementMatchers.isAgentClassLoader())) .and(classLoaderCanLoadClass("org.apache.logging.log4j.core.Appender")); } @@ -74,7 +75,7 @@ public ElementMatcher getMethodMatcher() { @Override public String getAdviceClassName() { - return "co.elastic.apm.agent.log4j2.Log4j2AppenderAppendAdvice"; + return "co.elastic.apm.agent.log4j2.reformatting.Log4j2AppenderAppendAdvice"; } } @@ -91,7 +92,7 @@ public ElementMatcher getMethodMatcher() { @Override public String getAdviceClassName() { - return "co.elastic.apm.agent.log4j2.Log4j2AppenderStopAdvice"; + return "co.elastic.apm.agent.log4j2.reformatting.Log4j2AppenderStopAdvice"; } } @@ -108,7 +109,7 @@ public ElementMatcher getMethodMatcher() { @Override public String getAdviceClassName() { - return "co.elastic.apm.agent.log4j2.Log4j2AppenderGetLayoutAdvice"; + return "co.elastic.apm.agent.log4j2.reformatting.Log4j2AppenderGetLayoutAdvice"; } } } diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/package-info.java new file mode 100644 index 0000000000..ef0b46891d --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/java/co/elastic/apm/agent/log4j2/reformatting/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.log4j2.reformatting; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer similarity index 100% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation new file mode 100644 index 0000000000..e9a812ebf7 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation @@ -0,0 +1,11 @@ +# ECS re-formatting +co.elastic.apm.agent.log4j2.reformatting.Log4j2EcsReformattingInstrumentation$ShadingInstrumentation +co.elastic.apm.agent.log4j2.reformatting.Log4j2EcsReformattingInstrumentation$StopAppenderInstrumentation +co.elastic.apm.agent.log4j2.reformatting.Log4j2EcsReformattingInstrumentation$OverridingInstrumentation + +# Trace context correlation +co.elastic.apm.agent.log4j2.correlation.Log4j2LogCorrelationInstrumentation$Log4J2_6LogCorrelationInstrumentation +co.elastic.apm.agent.log4j2.correlation.Log4j2LogCorrelationInstrumentation$Log4J2_7PlusLogCorrelationInstrumentation + +# Error creation +co.elastic.apm.agent.log4j2.error.Log4j2LoggerErrorCapturingInstrumentation diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2ShadingTest.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2InstrumentationTest.java similarity index 87% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2ShadingTest.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2InstrumentationTest.java index 31dede076a..3e3ef33df9 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2ShadingTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2InstrumentationTest.java @@ -18,8 +18,8 @@ */ package co.elastic.apm.agent.log4j2; -import co.elastic.apm.agent.log.shader.LogShadingInstrumentationTest; -import co.elastic.apm.agent.log.shader.LoggerFacade; +import co.elastic.apm.agent.loginstr.LoggingInstrumentationTest; +import co.elastic.apm.agent.loginstr.LoggerFacade; import co.elastic.apm.agent.logging.LoggingConfiguration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -30,13 +30,19 @@ import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Objects; -public class Log4j2ShadingTest extends LogShadingInstrumentationTest { +/** + * Since the agent core uses log4j 2.12.4, and since both agent core and tests are loaded by the system class loader in unit tests, + * the proper way to test integration tests with log4j2 is only through dedicated class loaders. + */ +@Disabled +public class Log4j2InstrumentationTest extends LoggingInstrumentationTest { @BeforeAll static void resetConfigFactory() { @@ -76,7 +82,7 @@ static class Log4j2LoggerFacade implements LoggerFacade { public Log4j2LoggerFacade() { try { - configLocation = Objects.requireNonNull(Log4j2ShadingTest.class.getClassLoader() + configLocation = Objects.requireNonNull(Log4j2InstrumentationTest.class.getClassLoader() .getResource("log4j2.xml")).toURI(); } catch (URISyntaxException e) { e.printStackTrace(); diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/LegacyLog4j2ShadingTest.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2InstrumentationTestVersions.java similarity index 84% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/LegacyLog4j2ShadingTest.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2InstrumentationTestVersions.java index cefa169e8d..86e13687fd 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/LegacyLog4j2ShadingTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2InstrumentationTestVersions.java @@ -38,7 +38,7 @@ * takes place during packaging). */ @Ignore -public class LegacyLog4j2ShadingTest extends Log4j2ShadingTest { +public class Log4j2InstrumentationTestVersions extends Log4j2InstrumentationTest { @BeforeClass public static void resetConfigFactory() { @@ -64,8 +64,8 @@ public void closeLogger() { @Test @Override - public void testSimpleLogShading() throws Exception { - super.testSimpleLogShading(); + public void testSimpleLogReformatting() throws Exception { + super.testSimpleLogReformatting(); } @Test @@ -82,14 +82,14 @@ public void testShadingIntoOriginalLogsDir() throws Exception { @Test @Override - public void testLazyShadeFileCreation() throws Exception { - super.testLazyShadeFileCreation(); + public void testLazyEcsFileCreation() throws Exception { + super.testLazyEcsFileCreation(); } @Test @Override - public void testLogShadingReplaceOriginal() throws IOException { - super.testLogShadingReplaceOriginal(); + public void testLogReformattingReplaceOriginal() throws IOException { + super.testLogReformattingReplaceOriginal(); } @Test @@ -112,7 +112,7 @@ public void testEmptyFormatterAllowList() throws Exception { @Test @Override - public void testShadeLogRolling() throws IOException { - super.testShadeLogRolling(); + public void testReformattedLogRolling() throws IOException { + super.testReformattedLogRolling(); } } diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2_17_1InstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2_17_1InstrumentationTest.java new file mode 100644 index 0000000000..f2a5c0f106 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2_17_1InstrumentationTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j2; + +import co.elastic.apm.agent.TestClassWithDependencyRunner; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class Log4j2_17_1InstrumentationTest { + private final TestClassWithDependencyRunner runner; + + public Log4j2_17_1InstrumentationTest() throws Exception { + List dependencies = List.of( + "org.apache.logging.log4j:log4j-core:2.17.1", + "org.apache.logging.log4j:log4j-api:2.17.1", + "co.elastic.logging:log4j2-ecs-layout:1.3.2" + ); + runner = new TestClassWithDependencyRunner(dependencies, Log4j2InstrumentationTestVersions.class, Log4j2InstrumentationTest.class, + Log4j2InstrumentationTest.Log4j2LoggerFacade.class); + } + + @Test + public void testVersions() { + runner.run(); + } +} diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/LegacyLog4j2ShadingTestRunner.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2_6SInstrumentationTest.java similarity index 84% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/LegacyLog4j2ShadingTestRunner.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2_6SInstrumentationTest.java index a13b6777f6..80d3265e95 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/LegacyLog4j2ShadingTestRunner.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/Log4j2_6SInstrumentationTest.java @@ -23,17 +23,17 @@ import java.util.List; -public class LegacyLog4j2ShadingTestRunner { +public class Log4j2_6SInstrumentationTest { private final TestClassWithDependencyRunner runner; - public LegacyLog4j2ShadingTestRunner() throws Exception { + public Log4j2_6SInstrumentationTest() throws Exception { List dependencies = List.of( "org.apache.logging.log4j:log4j-core:2.6", "org.apache.logging.log4j:log4j-api:2.6", "co.elastic.logging:log4j2-ecs-layout:1.3.2" ); - runner = new TestClassWithDependencyRunner(dependencies, LegacyLog4j2ShadingTest.class, Log4j2ShadingTest.class, - Log4j2ShadingTest.Log4j2LoggerFacade.class); + runner = new TestClassWithDependencyRunner(dependencies, Log4j2InstrumentationTestVersions.class, Log4j2InstrumentationTest.class, + Log4j2InstrumentationTest.Log4j2LoggerFacade.class); } @Test diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/correlation/CorrelationIdMapAdapterTest.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/correlation/CorrelationIdMapAdapterTest.java new file mode 100644 index 0000000000..a9b2427162 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/correlation/CorrelationIdMapAdapterTest.java @@ -0,0 +1,84 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j2.correlation; + +import co.elastic.apm.agent.MockTracer; +import co.elastic.apm.agent.impl.ElasticApmTracer; +import co.elastic.apm.agent.impl.GlobalTracer; +import co.elastic.apm.agent.impl.Scope; +import co.elastic.apm.agent.impl.transaction.Span; +import co.elastic.apm.agent.impl.transaction.Transaction; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +class CorrelationIdMapAdapterTest { + + private final ElasticApmTracer tracer = MockTracer.createRealTracer(); + + @BeforeEach + void setUp() { + GlobalTracer.init(tracer); + } + + @AfterEach + void tearDown() { + tracer.stop(); + GlobalTracer.setNoop(); + } + + @Test + void testNoContext() { + assertThat(CorrelationIdMapAdapter.get()).isEmpty(); + } + + @Test + void testTransactionContext() { + Transaction transaction = tracer.startRootTransaction(null); + try (Scope scope = transaction.activateInScope()) { + assertThat(CorrelationIdMapAdapter.get()).containsOnlyKeys("trace.id", "transaction.id"); + } finally { + transaction.end(); + } + assertThat(CorrelationIdMapAdapter.get()).isEmpty(); + } + + @Test + void testSpanContext() { + Transaction transaction = tracer.startRootTransaction(null); + Span span = transaction.createSpan(); + try (Scope scope = span.activateInScope()) { + assertThat(CorrelationIdMapAdapter.get()).containsOnlyKeys("trace.id", "transaction.id"); + } finally { + span.end(); + } + transaction.end(); + assertThat(CorrelationIdMapAdapter.get()).isEmpty(); + } + + @Test + void testSingleInstance() { + assertThat(CorrelationIdMapAdapter.get()).isSameAs(CorrelationIdMapAdapter.get()); + assertThat(CorrelationIdMapAdapter.get().entrySet()).isSameAs(CorrelationIdMapAdapter.get().entrySet()); + assertThat(CorrelationIdMapAdapter.allKeys()).isSameAs(CorrelationIdMapAdapter.allKeys()); + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/error/Log4j2ErrorCapturingTestVersions.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/error/Log4j2ErrorCapturingTestVersions.java new file mode 100644 index 0000000000..5dc5a42a7d --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/error/Log4j2ErrorCapturingTestVersions.java @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j2.error; + +import co.elastic.apm.agent.TestClassWithDependencyRunner; +import org.junit.Ignore; +import org.junit.Test; + +/** + * This class only delegates tests to the current-version log4j2 tests through JUnit 4, so that it can be ran using + * {@link TestClassWithDependencyRunner} in a dedicated CL where an older log4j2 version is loaded. + * This is required because the agent is using log4j2 and in tests they retain their original packages (relocation only + * takes place during packaging). + */ +@Ignore +public class Log4j2ErrorCapturingTestVersions extends Log4j2LoggerErrorCapturingInstrumentationTest { + + @Test + @Override + public void captureErrorExceptionWithStringMessage() { + super.captureErrorExceptionWithStringMessage(); + } + + @Test + @Override + public void captureErrorExceptionWithMessageMessage() { + super.captureErrorExceptionWithMessageMessage(); + } + + @Test + @Override + public void captureFatalException() { + super.captureFatalException(); + } +} diff --git a/apm-agent-plugins/apm-error-logging-plugin/src/test/java/co/elastic/apm/agent/errorlogging/Log4j2LoggerErrorCapturingInstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/error/Log4j2LoggerErrorCapturingInstrumentationTest.java similarity index 81% rename from apm-agent-plugins/apm-error-logging-plugin/src/test/java/co/elastic/apm/agent/errorlogging/Log4j2LoggerErrorCapturingInstrumentationTest.java rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/error/Log4j2LoggerErrorCapturingInstrumentationTest.java index 82dd385d91..c50bce8aed 100644 --- a/apm-agent-plugins/apm-error-logging-plugin/src/test/java/co/elastic/apm/agent/errorlogging/Log4j2LoggerErrorCapturingInstrumentationTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/error/Log4j2LoggerErrorCapturingInstrumentationTest.java @@ -16,14 +16,21 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.errorlogging; +package co.elastic.apm.agent.log4j2.error; +import co.elastic.apm.agent.loginstr.error.AbstractErrorLoggingInstrumentationTest; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessageFactory; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -class Log4j2LoggerErrorCapturingInstrumentationTest extends AbstractErrorLoggingInstrumentationTest { +/** + * Only tested through dedicated class loaders for latest and oldest-supported versions. + * See {@link Log4j2ErrorCapturingTestVersions} + */ +@Disabled +public class Log4j2LoggerErrorCapturingInstrumentationTest extends AbstractErrorLoggingInstrumentationTest { private static final Logger logger = LogManager.getLogger(Log4j2LoggerErrorCapturingInstrumentationTest.class); diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/error/Log4j2_17_1ErrorCapturingTest.java b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/error/Log4j2_17_1ErrorCapturingTest.java new file mode 100644 index 0000000000..2153b15435 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/java/co/elastic/apm/agent/log4j2/error/Log4j2_17_1ErrorCapturingTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.log4j2.error; + +import co.elastic.apm.agent.TestClassWithDependencyRunner; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class Log4j2_17_1ErrorCapturingTest { + private final TestClassWithDependencyRunner runner; + + public Log4j2_17_1ErrorCapturingTest() throws Exception { + List dependencies = List.of( + "org.apache.logging.log4j:log4j-core:2.17.1", + "org.apache.logging.log4j:log4j-api:2.17.1" + ); + runner = new TestClassWithDependencyRunner(dependencies, Log4j2ErrorCapturingTestVersions.class, + Log4j2LoggerErrorCapturingInstrumentationTest.class); + } + + @Test + public void testVersions() { + runner.run(); + } +} diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/resources/log4j2.xml b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/resources/log4j2.xml similarity index 100% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log4j2-plugin/src/test/resources/log4j2.xml rename to apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/src/test/resources/log4j2.xml diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml similarity index 84% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml index 9f558575dd..50c956e68c 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml @@ -18,7 +18,7 @@ ${project.groupId} - apm-log-shader-plugin-common + apm-logging-plugin-common ${project.version} @@ -34,11 +34,17 @@ ${project.groupId} - apm-log-shader-plugin-common + apm-logging-plugin-common ${project.version} test-jar test + + ${project.groupId} + apm-slf4j-plugin + ${project.version} + test + diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackPluginClassLoaderRootPackageCustomizer.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackPluginClassLoaderRootPackageCustomizer.java similarity index 73% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackPluginClassLoaderRootPackageCustomizer.java rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackPluginClassLoaderRootPackageCustomizer.java index 8b81bef7cf..9787729e1b 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackPluginClassLoaderRootPackageCustomizer.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackPluginClassLoaderRootPackageCustomizer.java @@ -18,14 +18,6 @@ */ package co.elastic.apm.agent.logback; -import co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer; +import co.elastic.apm.agent.loginstr.LoggingPluginClassLoaderRootPackageCustomizer; -import java.util.Arrays; -import java.util.Collection; - -public class LogbackPluginClassLoaderRootPackageCustomizer extends PluginClassLoaderRootPackageCustomizer { - @Override - public Collection pluginClassLoaderRootPackages() { - return Arrays.asList(getPluginPackage(), "co.elastic.logging"); - } -} +public class LogbackPluginClassLoaderRootPackageCustomizer extends LoggingPluginClassLoaderRootPackageCustomizer {} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/correlation/LogbackLogCorrelationHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/correlation/LogbackLogCorrelationHelper.java new file mode 100644 index 0000000000..1180691464 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/correlation/LogbackLogCorrelationHelper.java @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.logback.correlation; + +import co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper; +import org.slf4j.MDC; + +public class LogbackLogCorrelationHelper extends AbstractLogCorrelationHelper.DefaultLogCorrelationHelper { + + @Override + protected void addToMdc(String key, String value) { + MDC.put(key, value); + } + + @Override + protected void removeFromMdc(String key) { + MDC.remove(key); + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/correlation/LogbackLogCorrelationInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/correlation/LogbackLogCorrelationInstrumentation.java new file mode 100644 index 0000000000..c34f532ea3 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/correlation/LogbackLogCorrelationInstrumentation.java @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.logback.correlation; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import co.elastic.apm.agent.bci.TracerAwareInstrumentation; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import java.util.Collection; +import java.util.Collections; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +/** + * Instruments {@link ch.qos.logback.classic.Logger#callAppenders(ILoggingEvent)} + */ +public class LogbackLogCorrelationInstrumentation extends TracerAwareInstrumentation { + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singleton("logback-correlation"); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named("ch.qos.logback.classic.Logger"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("callAppenders"); + } + + public static class AdviceClass { + + private static final LogbackLogCorrelationHelper helper = new LogbackLogCorrelationHelper(); + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static boolean addToMdc() { + return helper.beforeLoggingEvent(); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void removeFromMdc(@Advice.Enter boolean addedToMdc) { + helper.afterLoggingEvent(addedToMdc); + } + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/correlation/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/correlation/package-info.java new file mode 100644 index 0000000000..4ad64865d3 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/correlation/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.logback.correlation; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/package-info.java similarity index 100% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/package-info.java rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/package-info.java diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackAppenderAppendAdvice.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackAppenderAppendAdvice.java similarity index 75% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackAppenderAppendAdvice.java rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackAppenderAppendAdvice.java index c1ca20c8f6..179fe64878 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackAppenderAppendAdvice.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackAppenderAppendAdvice.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.logback; +package co.elastic.apm.agent.logback.reformatting; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.OutputStreamAppender; @@ -29,16 +29,16 @@ public class LogbackAppenderAppendAdvice { @SuppressWarnings("unused") @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class, inline = false) - public static boolean shadeAndSkipIfOverrideEnabled(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final Object eventObject, - @Advice.This(typing = Assigner.Typing.DYNAMIC) OutputStreamAppender thisAppender) { + public static boolean initializeReformatting(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final Object eventObject, + @Advice.This(typing = Assigner.Typing.DYNAMIC) OutputStreamAppender thisAppender) { return eventObject instanceof ILoggingEvent && helper.onAppendEnter(thisAppender); } @SuppressWarnings({"unused"}) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) - public static void shadeLoggingEvent(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final Object eventObject, - @Advice.This(typing = Assigner.Typing.DYNAMIC) OutputStreamAppender thisAppender) { + public static void reformatLoggingEvent(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) final Object eventObject, + @Advice.This(typing = Assigner.Typing.DYNAMIC) OutputStreamAppender thisAppender) { if (!(eventObject instanceof ILoggingEvent)) { return; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackAppenderStopAdvice.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackAppenderStopAdvice.java similarity index 96% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackAppenderStopAdvice.java rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackAppenderStopAdvice.java index 07779e6c0b..00c02c1895 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackAppenderStopAdvice.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackAppenderStopAdvice.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.logback; +package co.elastic.apm.agent.logback.reformatting; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.OutputStreamAppender; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackEcsReformattingHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackEcsReformattingHelper.java similarity index 92% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackEcsReformattingHelper.java rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackEcsReformattingHelper.java index 92b25e1f55..5ca5271dec 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackEcsReformattingHelper.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackEcsReformattingHelper.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.logback; +package co.elastic.apm.agent.logback.reformatting; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; @@ -27,8 +27,8 @@ import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; import ch.qos.logback.core.rolling.RollingFileAppender; import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; -import co.elastic.apm.agent.log.shader.AbstractEcsReformattingHelper; -import co.elastic.apm.agent.log.shader.Utils; +import co.elastic.apm.agent.loginstr.reformatting.AbstractEcsReformattingHelper; +import co.elastic.apm.agent.loginstr.reformatting.Utils; import co.elastic.apm.agent.matcher.WildcardMatcher; import co.elastic.apm.agent.sdk.logging.Logger; import co.elastic.apm.agent.sdk.logging.LoggerFactory; @@ -105,13 +105,13 @@ protected OutputStreamAppender createAndStartEcsAppender(OutputSt ecsAppender = new RollingFileAppender<>(); ecsAppender.setEncoder(ecsEncoder); - String shadeFile = Utils.computeShadeLogFilePath(fileAppender.getFile(), getConfiguredShadeDir()); - ecsAppender.setFile(shadeFile); + String ecsFile = Utils.computeLogReformattingFilePath(fileAppender.getFile(), getConfiguredReformattingDir()); + ecsAppender.setFile(ecsFile); FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy(); rollingPolicy.setMinIndex(1); rollingPolicy.setMaxIndex(1); - rollingPolicy.setFileNamePattern(shadeFile + ".%i"); + rollingPolicy.setFileNamePattern(ecsFile + ".%i"); rollingPolicy.setParent(ecsAppender); rollingPolicy.setContext(defaultLoggerContext); rollingPolicy.start(); @@ -121,7 +121,7 @@ protected OutputStreamAppender createAndStartEcsAppender(OutputSt try { VersionUtils.setMaxFileSize(triggeringPolicy, getMaxLogFileSize()); } catch (Throwable throwable) { - logger.info("Failed to set max file size for log shader file-rolling strategy. Using the default " + + logger.info("Failed to set max file size for log ECS-reformatting file-rolling strategy. Using the default " + "Logback setting instead - " + SizeBasedTriggeringPolicy.DEFAULT_MAX_FILE_SIZE + ". Error message: " + throwable.getMessage()); } diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackLogShadingInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackLogReformattingInstrumentation.java similarity index 84% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackLogShadingInstrumentation.java rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackLogReformattingInstrumentation.java index c8019d6cf9..b94b51453f 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/LogbackLogShadingInstrumentation.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/LogbackLogReformattingInstrumentation.java @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.logback; +package co.elastic.apm.agent.logback.reformatting; -import co.elastic.apm.agent.log.shader.AbstractLogShadingInstrumentation; +import co.elastic.apm.agent.loginstr.AbstractLogIntegrationInstrumentation; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -32,7 +32,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesGenericArgument; -public abstract class LogbackLogShadingInstrumentation extends AbstractLogShadingInstrumentation { +public abstract class LogbackLogReformattingInstrumentation extends AbstractLogIntegrationInstrumentation { @Override public Collection getInstrumentationGroupNames() { @@ -53,7 +53,7 @@ public ElementMatcher getTypeMatcher() { return named("ch.qos.logback.core.OutputStreamAppender"); } - public static class ShadingInstrumentation extends LogbackLogShadingInstrumentation { + public static class ReformattingInstrumentation extends LogbackLogReformattingInstrumentation { /** * Instrumenting {@link ch.qos.logback.core.OutputStreamAppender#append(Object)} @@ -65,12 +65,12 @@ public ElementMatcher getMethodMatcher() { @Override public String getAdviceClassName() { - return "co.elastic.apm.agent.logback.LogbackAppenderAppendAdvice"; + return "co.elastic.apm.agent.logback.reformatting.LogbackAppenderAppendAdvice"; } } - public static class StopAppenderInstrumentation extends LogbackLogShadingInstrumentation { + public static class StopAppenderInstrumentation extends LogbackLogReformattingInstrumentation { /** * Instrumenting {@link ch.qos.logback.core.OutputStreamAppender#stop} @@ -82,7 +82,7 @@ public ElementMatcher getMethodMatcher() { @Override public String getAdviceClassName() { - return "co.elastic.apm.agent.logback.LogbackAppenderStopAdvice"; + return "co.elastic.apm.agent.logback.reformatting.LogbackAppenderStopAdvice"; } } diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/VersionUtils.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/VersionUtils.java similarity index 98% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/VersionUtils.java rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/VersionUtils.java index 78d1bfdda0..d303a6908d 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/VersionUtils.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/VersionUtils.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.logback; +package co.elastic.apm.agent.logback.reformatting; import ch.qos.logback.core.OutputStreamAppender; import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/package-info.java new file mode 100644 index 0000000000..f4074f9449 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/java/co/elastic/apm/agent/logback/reformatting/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.logback.reformatting; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer similarity index 100% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation new file mode 100644 index 0000000000..70d85e07ab --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation @@ -0,0 +1,6 @@ +# ECS re-formatting +co.elastic.apm.agent.logback.reformatting.LogbackLogReformattingInstrumentation$ReformattingInstrumentation +co.elastic.apm.agent.logback.reformatting.LogbackLogReformattingInstrumentation$StopAppenderInstrumentation + +# Trace context correlation +co.elastic.apm.agent.logback.correlation.LogbackLogCorrelationInstrumentation diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/java/co/elastic/apm/agent/logback/LogbackShadingTest.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/java/co/elastic/apm/agent/logback/LogbackInstrumentationTest.java similarity index 95% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/java/co/elastic/apm/agent/logback/LogbackShadingTest.java rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/java/co/elastic/apm/agent/logback/LogbackInstrumentationTest.java index 3fbde3cdb5..16e794123d 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/java/co/elastic/apm/agent/logback/LogbackShadingTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/java/co/elastic/apm/agent/logback/LogbackInstrumentationTest.java @@ -23,15 +23,15 @@ import ch.qos.logback.classic.util.ContextInitializer; import ch.qos.logback.core.FileAppender; import ch.qos.logback.core.joran.spi.JoranException; -import co.elastic.apm.agent.log.shader.LogShadingInstrumentationTest; -import co.elastic.apm.agent.log.shader.LoggerFacade; +import co.elastic.apm.agent.loginstr.LoggingInstrumentationTest; +import co.elastic.apm.agent.loginstr.LoggerFacade; import org.slf4j.MDC; import org.slf4j.Marker; import org.slf4j.MarkerFactory; import java.net.URL; -public class LogbackShadingTest extends LogShadingInstrumentationTest { +public class LogbackInstrumentationTest extends LoggingInstrumentationTest { private static final Marker TEST_MARKER = MarkerFactory.getMarker("TEST"); diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/resources/logback.xml b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/resources/logback.xml similarity index 100% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/resources/logback.xml rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/src/test/resources/logback.xml diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml similarity index 84% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml index 0b72418e35..6a0b8995b1 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml @@ -18,14 +18,14 @@ ${project.groupId} - apm-log-shader-plugin-common + apm-logging-plugin-common ${project.version} test-jar test ${project.groupId} - apm-log-shader-plugin-common + apm-logging-plugin-common ${project.version} test @@ -49,5 +49,11 @@ 1.1.0 test + + ${project.groupId} + apm-slf4j-plugin + ${project.version} + test + diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/src/test/java/co/elastic/apm/agent/logback/LegacyLogbackShadingTest.java b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/src/test/java/co/elastic/apm/agent/logback/LegacyLogbackInstrumentationTest.java similarity index 91% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/src/test/java/co/elastic/apm/agent/logback/LegacyLogbackShadingTest.java rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/src/test/java/co/elastic/apm/agent/logback/LegacyLogbackInstrumentationTest.java index c494b26a14..cb03d4bfcc 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/src/test/java/co/elastic/apm/agent/logback/LegacyLogbackShadingTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/src/test/java/co/elastic/apm/agent/logback/LegacyLogbackInstrumentationTest.java @@ -18,10 +18,10 @@ */ package co.elastic.apm.agent.logback; -public class LegacyLogbackShadingTest extends LogbackShadingTest { +public class LegacyLogbackInstrumentationTest extends LogbackInstrumentationTest { @Override - public void testShadeLogRolling() { + public void testReformattedLogRolling() { // Logback's SizeBasedTriggeringPolicy relies on something called InvocationGate to limit the frequency of // heavy operations like file rolling. In versions older than 1.1.8, its implementation made it unsuitable // for unit testing as it required minimal number of invocations (starts with 16 an increasing) that cannot be diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/pom.xml similarity index 93% rename from apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/pom.xml rename to apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/pom.xml index d52439f019..f5a20136e3 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-logback-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - apm-log-shader-plugin + apm-logging-plugin co.elastic.apm 1.29.1-SNAPSHOT diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/pom.xml similarity index 89% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/pom.xml rename to apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/pom.xml index 9dfddf3610..254ea7d105 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/pom.xml @@ -3,12 +3,12 @@ 4.0.0 - apm-log-shader-plugin + apm-logging-plugin co.elastic.apm 1.29.1-SNAPSHOT - apm-log-shader-plugin-common + apm-logging-plugin-common ${project.groupId}:${project.artifactId} diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/AbstractLogShadingInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/AbstractLogIntegrationInstrumentation.java similarity index 89% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/AbstractLogShadingInstrumentation.java rename to apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/AbstractLogIntegrationInstrumentation.java index fb181d621f..5cfe7814b6 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/AbstractLogShadingInstrumentation.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/AbstractLogIntegrationInstrumentation.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log.shader; +package co.elastic.apm.agent.loginstr; import co.elastic.apm.agent.bci.TracerAwareInstrumentation; @@ -24,7 +24,7 @@ import java.util.ArrayList; import java.util.Collection; -public abstract class AbstractLogShadingInstrumentation extends TracerAwareInstrumentation { +public abstract class AbstractLogIntegrationInstrumentation extends TracerAwareInstrumentation { @Override public Collection getInstrumentationGroupNames() { diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/LoggingPluginClassLoaderRootPackageCustomizer.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/LoggingPluginClassLoaderRootPackageCustomizer.java new file mode 100644 index 0000000000..426d360962 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/LoggingPluginClassLoaderRootPackageCustomizer.java @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.loginstr; + +import co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer; + +import java.util.Arrays; +import java.util.Collection; + +/** + * An abstract class providing the common implementation for all logging plugin submodules. + * This class is abstract because it cannot be used as is, which is also why it is not listed as a {@link PluginClassLoaderRootPackageCustomizer} + * service provider - it would be loaded by the service loader, but since it's not in the same package as all actual + * plugins, it won't be used, as the lookup depends on the plugin package (e.g. {@code co.elastic.apm.agent.log4j2}). + * In order to use it by a logging plugin, the plugin needs to extend it and list the implementation as a service provider. + */ +public abstract class LoggingPluginClassLoaderRootPackageCustomizer extends PluginClassLoaderRootPackageCustomizer { + + @Override + public Collection pluginClassLoaderRootPackages() { + return Arrays.asList(getPluginPackage(), "co.elastic.logging"); + } +} diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/correlation/AbstractLogCorrelationHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/correlation/AbstractLogCorrelationHelper.java new file mode 100644 index 0000000000..ecd47cbe98 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/correlation/AbstractLogCorrelationHelper.java @@ -0,0 +1,102 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.loginstr.correlation; + +import co.elastic.apm.agent.impl.GlobalTracer; +import co.elastic.apm.agent.impl.Tracer; +import co.elastic.apm.agent.impl.error.ErrorCapture; +import co.elastic.apm.agent.impl.transaction.AbstractSpan; +import co.elastic.apm.agent.sdk.state.CallDepth; +import co.elastic.apm.agent.sdk.state.GlobalState; + +@GlobalState +public abstract class AbstractLogCorrelationHelper { + + public static final String TRACE_ID_MDC_KEY = "trace.id"; + public static final String TRANSACTION_ID_MDC_KEY = "transaction.id"; + public static final String ERROR_ID_MDC_KEY = "error.id"; + + private static final CallDepth callDepth = CallDepth.get(AbstractLogCorrelationHelper.class); + + /** + * Invokes the addition of active context metadata to the MDC (or framework-equivalent) + * @return returns {@code true} if the active context metadata was added to the MDC + */ + public boolean beforeLoggingEvent() { + if (callDepth.isNestedCallAndIncrement()) { + return false; + } + return addToMdc(); + } + + /** + * Clears context metadata from the MDC if required + * @param addedToMdc should reflect the value returned from {@link #beforeLoggingEvent()} for the corresponding API call + */ + public void afterLoggingEvent(boolean addedToMdc) { + if (!callDepth.isNestedCallAndDecrement() && addedToMdc) { + removeFromMdc(); + } + } + + /** + * Add details of the currently active context to the logging framework MDC (or framework-equivalent) + * @return {@code true} if context metadata was added to the MDC, otherwise should return {@code false} + */ + protected abstract boolean addToMdc(); + + protected abstract void removeFromMdc(); + + /** + * Default abstract implementation for {@link AbstractLogCorrelationHelper}, which retrieves the currently active context and + * adds metadata key-by-key. + */ + public static abstract class DefaultLogCorrelationHelper extends AbstractLogCorrelationHelper { + + private final Tracer tracer = GlobalTracer.get(); + + @Override + protected boolean addToMdc() { + boolean addedToMdc = false; + AbstractSpan activeSpan = tracer.getActive(); + if (activeSpan != null) { + addToMdc(TRACE_ID_MDC_KEY, activeSpan.getTraceContext().getTraceId().toString()); + addToMdc(TRANSACTION_ID_MDC_KEY, activeSpan.getTraceContext().getTransactionId().toString()); + addedToMdc = true; + } + ErrorCapture activeError = ErrorCapture.getActive(); + if (activeError != null) { + addToMdc(ERROR_ID_MDC_KEY, activeError.getTraceContext().getId().toString()); + addedToMdc = true; + } + return addedToMdc; + } + + @Override + protected void removeFromMdc() { + removeFromMdc(TRACE_ID_MDC_KEY); + removeFromMdc(TRANSACTION_ID_MDC_KEY); + removeFromMdc(ERROR_ID_MDC_KEY); + } + + protected abstract void addToMdc(String key, String value); + + protected abstract void removeFromMdc(String key); + } +} diff --git a/apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/AbstractLoggerErrorCapturingInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/error/AbstractLoggerErrorCapturingInstrumentation.java similarity index 84% rename from apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/AbstractLoggerErrorCapturingInstrumentation.java rename to apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/error/AbstractLoggerErrorCapturingInstrumentation.java index dc9aae8238..96647d05ab 100644 --- a/apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/AbstractLoggerErrorCapturingInstrumentation.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/error/AbstractLoggerErrorCapturingInstrumentation.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.errorlogging; +package co.elastic.apm.agent.loginstr.error; import co.elastic.apm.agent.bci.TracerAwareInstrumentation; import co.elastic.apm.agent.impl.error.ErrorCapture; @@ -31,17 +31,17 @@ import java.util.Collection; import static net.bytebuddy.matcher.ElementMatchers.nameContains; -import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.not; -import static net.bytebuddy.matcher.ElementMatchers.ofType; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; public abstract class AbstractLoggerErrorCapturingInstrumentation extends TracerAwareInstrumentation { + public static final String SLF4J_LOGGER = "org.slf4j.Logger"; + public static final String LOG4J2_LOGGER = "org.apache.logging.log4j.Logger"; + @Override public String getAdviceClassName() { - return "co.elastic.apm.agent.errorlogging.AbstractLoggerErrorCapturingInstrumentation$LoggingAdvice"; + return "co.elastic.apm.agent.loginstr.error.AbstractLoggerErrorCapturingInstrumentation$LoggingAdvice"; } public static class LoggingAdvice { @@ -50,8 +50,7 @@ public static class LoggingAdvice { @Nullable @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) - public static Object logEnter(@Advice.Argument(1) Throwable exception, - @Advice.Origin Class clazz) { + public static Object logEnter(@Advice.Argument(1) Throwable exception, @Advice.Origin Class clazz) { if (!callDepth.isNestedCallAndIncrement()) { ErrorCapture error = tracer.captureException(exception, tracer.getActive(), clazz.getClassLoader()); if (error != null) { @@ -89,9 +88,4 @@ public Collection getInstrumentationGroupNames() { ret.add("logging"); return ret; } - - @Override - public ElementMatcher.Junction getClassLoaderMatcher() { - return not(ofType(nameStartsWith("co.elastic.apm."))); - } } diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/error/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/error/package-info.java new file mode 100644 index 0000000000..73d992f622 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/error/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.loginstr.error; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-log-correlation-plugin/src/main/java/co/elastic/apm/agent/mdc/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/package-info.java similarity index 95% rename from apm-agent-plugins/apm-log-correlation-plugin/src/main/java/co/elastic/apm/agent/mdc/package-info.java rename to apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/package-info.java index a26024ab34..136f445e2f 100644 --- a/apm-agent-plugins/apm-log-correlation-plugin/src/main/java/co/elastic/apm/agent/mdc/package-info.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/package-info.java @@ -17,6 +17,6 @@ * under the License. */ @NonnullApi -package co.elastic.apm.agent.mdc; +package co.elastic.apm.agent.loginstr; import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/AbstractEcsReformattingHelper.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/reformatting/AbstractEcsReformattingHelper.java similarity index 98% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/AbstractEcsReformattingHelper.java rename to apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/reformatting/AbstractEcsReformattingHelper.java index 6e07de5723..1c9b11d79e 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/AbstractEcsReformattingHelper.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/reformatting/AbstractEcsReformattingHelper.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log.shader; +package co.elastic.apm.agent.loginstr.reformatting; import co.elastic.apm.agent.collections.DetachedThreadLocalImpl; import co.elastic.apm.agent.configuration.CoreConfiguration; @@ -40,7 +40,7 @@ import java.util.Map; /** - * The abstract Log shading helper- loaded as part of the agent core (agent CL / bootstrap CL / System CL). + * The abstract Log ECS-reformatting helper- loaded as part of the agent core (agent CL / bootstrap CL / System CL). * Annotated with {@link GlobalState} because it holds global mappings from original appender to corresponding * ECS-appender and from ECS-formatter to original formatter. Otherwise, it would be loaded by the plugin class loader * due to it's package. @@ -103,7 +103,6 @@ @GlobalState public abstract class AbstractEcsReformattingHelper { - // Escape shading private static final String ECS_LOGGING_PACKAGE_NAME = "co.elastic.logging"; // We can use regular agent logging here as this class is loaded from the agent CL @@ -176,7 +175,7 @@ public AbstractEcsReformattingHelper() { } /** - * Must be called exactly once at the enter to each {@code append()} method (or equivalent) invocation in order to + * Must be called exactly once at the entrance to each {@code append()} method (or equivalent) invocation in order to * properly detect nested invocations. * @param appender the instrumented appender * @return true if log events should be ignored for the given appender; false otherwise @@ -298,7 +297,7 @@ public A onAppendExit(A appender) { } @Nullable - protected String getConfiguredShadeDir() { + protected String getConfiguredReformattingDir() { return loggingConfiguration.getLogEcsFormattingDestinationDir(); } @@ -378,7 +377,7 @@ public void closeShadeAppenderFor(A originalAppender) { } /** - * Checks whether the given appender is a shading appender, so to avoid recursive shading + * Checks whether the given appender is a shading appender, so to avoid recursive reformatting * @return true if the provide appender is a shading appender; false otherwise */ private boolean isShadingAppender(A appender) { diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/Utils.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/reformatting/Utils.java similarity index 58% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/Utils.java rename to apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/reformatting/Utils.java index c4893ef84f..a4cd93969d 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/Utils.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/reformatting/Utils.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log.shader; +package co.elastic.apm.agent.loginstr.reformatting; import javax.annotation.Nullable; import java.nio.file.Path; @@ -24,39 +24,39 @@ public class Utils { - private static final String SHADE_FILE_EXTENSION = ".ecs.json"; + private static final String ECS_JSON_FILE_EXTENSION = ".ecs.json"; /** - * Computes a shade log file path based on a given log file. The shade log file will have the same name as the + * Computes a path for the ECS-reformatted file based on a given log file. The ECS log file will have the same name as the * original log file, but with the ".ecs.json" extension. Depending on configuration, it will be located in * the same directory alongside the original logs, or in an alternative directory. * - * @param originalLogFile the log file to which a shade file path is required - * @return the shade log file path + * @param originalLogFile the log file to which an ECS file path is required + * @return the ECS log file path */ - public static String computeShadeLogFilePath(String originalLogFile, @Nullable String configuredShadeFileDestinationDir) { + public static String computeLogReformattingFilePath(String originalLogFile, @Nullable String configuredReformattingDestinationDir) { Path originalFilePath = Paths.get(originalLogFile); Path logFileName = Paths.get(replaceFileExtensionToEcsJson(originalFilePath.getFileName().toString())); - Path shadeDir = computeShadeLogsDir(originalFilePath, configuredShadeFileDestinationDir); - if (shadeDir != null) { - logFileName = shadeDir.resolve(logFileName); + Path reformattingDir = computeLogReformattingDir(originalFilePath, configuredReformattingDestinationDir); + if (reformattingDir != null) { + logFileName = reformattingDir.resolve(logFileName); } return logFileName.toString(); } @Nullable - private static Path computeShadeLogsDir(Path originalFilePath, @Nullable String configuredShadeFileDestinationDir) { - Path shadeDir; + private static Path computeLogReformattingDir(Path originalFilePath, @Nullable String configuredReformattingDestinationDir) { + Path ecsDir; Path logsDir = originalFilePath.getParent(); - if (configuredShadeFileDestinationDir == null) { - shadeDir = logsDir; + if (configuredReformattingDestinationDir == null) { + ecsDir = logsDir; } else { - shadeDir = Paths.get(configuredShadeFileDestinationDir); - if (!shadeDir.isAbsolute() && logsDir != null) { - shadeDir = logsDir.resolve(shadeDir); + ecsDir = Paths.get(configuredReformattingDestinationDir); + if (!ecsDir.isAbsolute() && logsDir != null) { + ecsDir = logsDir.resolve(ecsDir); } } - return shadeDir; + return ecsDir; } static String replaceFileExtensionToEcsJson(String originalFileName) { @@ -64,6 +64,6 @@ static String replaceFileExtensionToEcsJson(String originalFileName) { if (extensionIndex > 0) { originalFileName = originalFileName.substring(0, extensionIndex); } - return originalFileName.concat(SHADE_FILE_EXTENSION); + return originalFileName.concat(ECS_JSON_FILE_EXTENSION); } } diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/reformatting/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/reformatting/package-info.java new file mode 100644 index 0000000000..139e950b81 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/main/java/co/elastic/apm/agent/loginstr/reformatting/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ +@NonnullApi +package co.elastic.apm.agent.loginstr.reformatting; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/test/java/co/elastic/apm/agent/log/shader/LoggerFacade.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggerFacade.java similarity index 96% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/test/java/co/elastic/apm/agent/log/shader/LoggerFacade.java rename to apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggerFacade.java index 4818f86365..af022576e7 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/test/java/co/elastic/apm/agent/log/shader/LoggerFacade.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggerFacade.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log.shader; +package co.elastic.apm.agent.loginstr; public interface LoggerFacade { diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/test/java/co/elastic/apm/agent/log/shader/LogShadingInstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggingInstrumentationTest.java similarity index 65% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/test/java/co/elastic/apm/agent/log/shader/LogShadingInstrumentationTest.java rename to apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggingInstrumentationTest.java index 2c3ea39d18..9b7456738d 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/test/java/co/elastic/apm/agent/log/shader/LogShadingInstrumentationTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggingInstrumentationTest.java @@ -16,33 +16,38 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.log.shader; +package co.elastic.apm.agent.loginstr; import co.elastic.apm.agent.AbstractInstrumentationTest; import co.elastic.apm.agent.configuration.CoreConfiguration; +import co.elastic.apm.agent.impl.error.ErrorCapture; +import co.elastic.apm.agent.impl.transaction.Span; +import co.elastic.apm.agent.impl.transaction.Transaction; import co.elastic.apm.agent.logging.LogEcsReformatting; import co.elastic.apm.agent.logging.LoggingConfiguration; import co.elastic.apm.agent.logging.TestUtils; +import co.elastic.apm.agent.loginstr.correlation.AbstractLogCorrelationHelper; +import co.elastic.apm.agent.loginstr.reformatting.Utils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.TimeZone; -import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -51,7 +56,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; -public abstract class LogShadingInstrumentationTest extends AbstractInstrumentationTest { +public abstract class LoggingInstrumentationTest extends AbstractInstrumentationTest { public static final String TRACE_MESSAGE = "Trace-this"; public static final String DEBUG_MESSAGE = "Debug-this"; @@ -63,12 +68,20 @@ public abstract class LogShadingInstrumentationTest extends AbstractInstrumentat private final LoggerFacade logger; private final ObjectMapper objectMapper; + private final SimpleDateFormat timestampFormat; + private final SimpleDateFormat utcTimestampFormat; + private LoggingConfiguration loggingConfig; private String serviceName; + private Transaction transaction; + private Span childSpan; - public LogShadingInstrumentationTest() { + public LoggingInstrumentationTest() { logger = createLoggerFacade(); objectMapper = new ObjectMapper(); + timestampFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + utcTimestampFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + utcTimestampFormat.setTimeZone(TimeZone.getTimeZone("UTC")); } @BeforeEach @@ -82,52 +95,51 @@ public void setup() throws Exception { // IMPORTANT: keep this last, so that it doesn't interfere with Mockito settings above serviceName = Objects.requireNonNull(tracer.getMetaDataFuture().get(2000, TimeUnit.MILLISECONDS).getService().getName()); + + transaction = startTestRootTransaction(); + childSpan = transaction.createSpan().activate(); } private void setEcsReformattingConfig(LogEcsReformatting ecsReformattingConfig) { doReturn(ecsReformattingConfig).when(loggingConfig).getLogEcsReformatting(); } - private void initializeShadeDir(String dirName) throws IOException { + private void initializeReformattingDir(String dirName) throws IOException { when(loggingConfig.getLogEcsFormattingDestinationDir()).thenReturn(dirName); - Files.deleteIfExists(Paths.get(getShadeLogFilePath())); - Files.deleteIfExists(Paths.get(getShadeLogFilePath() + ".1")); + Files.deleteIfExists(Paths.get(getLogReformattingFilePath())); + Files.deleteIfExists(Paths.get(getLogReformattingFilePath() + ".1")); } @AfterEach public void closeLogger() { + childSpan.deactivate().end(); + transaction.deactivate().end(); logger.close(); } protected abstract LoggerFacade createLoggerFacade(); @Test - public void testSimpleLogShading() throws Exception { + public void testSimpleLogReformatting() throws Exception { setEcsReformattingConfig(LogEcsReformatting.SHADE); - initializeShadeDir("simple"); + initializeReformattingDir("simple"); runSimpleScenario(); } private void runSimpleScenario() throws Exception { - String traceId = UUID.randomUUID().toString(); - logger.putTraceIdToMdc(traceId); - try { - logger.trace(TRACE_MESSAGE); - logger.debug(DEBUG_MESSAGE); - logger.warn(WARN_MESSAGE); - logger.error(ERROR_MESSAGE); - } finally { - logger.removeTraceIdFromMdc(); - } + logger.trace(TRACE_MESSAGE); + logger.debug(DEBUG_MESSAGE); + logger.warn(WARN_MESSAGE); + logger.error(ERROR_MESSAGE, new Throwable()); ArrayList rawLogLines = readRawLogLines(); assertThat(rawLogLines).hasSize(4); - ArrayList ecsLogLines = readShadeLogFile(); + ArrayList ecsLogLines = readEcsLogFile(); assertThat(ecsLogLines).hasSize(4); for (int i = 0; i < 4; i++) { - verifyEcsFormat(rawLogLines.get(i), ecsLogLines.get(i), traceId); + verifyEcsFormat(rawLogLines.get(i), ecsLogLines.get(i)); } } @@ -135,18 +147,18 @@ private void runSimpleScenario() throws Exception { public void testMarkers() throws Exception { if (markersSupported()) { setEcsReformattingConfig(LogEcsReformatting.SHADE); - initializeShadeDir("markers"); + initializeReformattingDir("markers"); logger.debugWithMarker(DEBUG_MESSAGE); ArrayList rawLogLines = readRawLogLines(); assertThat(rawLogLines).hasSize(1); String[] rawLogLine = rawLogLines.get(0); - ArrayList ecsLogLines = readShadeLogFile(); + ArrayList ecsLogLines = readEcsLogFile(); assertThat(ecsLogLines).hasSize(1); JsonNode ecsLogLine = ecsLogLines.get(0); - verifyEcsFormat(rawLogLine, ecsLogLine, null); + verifyEcsFormat(rawLogLine, ecsLogLine); JsonNode tagsJson = ecsLogLine.get("tags"); assertThat(tagsJson.isArray()).isTrue(); @@ -161,43 +173,44 @@ protected boolean markersSupported() { @Test public void testShadingIntoOriginalLogsDir() throws Exception { setEcsReformattingConfig(LogEcsReformatting.SHADE); - initializeShadeDir(""); + initializeReformattingDir(""); runSimpleScenario(); } @Test - public void testLazyShadeFileCreation() throws Exception { - initializeShadeDir("delayed"); + public void testLazyEcsFileCreation() throws Exception { + initializeReformattingDir("delayed"); logger.trace(TRACE_MESSAGE); logger.debug(DEBUG_MESSAGE); - assertThat(Files.exists(Paths.get(getShadeLogFilePath()))).isFalse(); + assertThat(Paths.get(getLogReformattingFilePath())).doesNotExist(); setEcsReformattingConfig(LogEcsReformatting.SHADE); logger.warn(WARN_MESSAGE); - assertThat(Files.exists(Paths.get(getShadeLogFilePath()))).isTrue(); - logger.error(ERROR_MESSAGE); + assertThat(Paths.get(getLogReformattingFilePath())).exists(); + logger.error(ERROR_MESSAGE, new Throwable()); ArrayList rawLogLines = readRawLogLines(); assertThat(rawLogLines).hasSize(4); + assertThat(rawLogLines).hasSize(4); - ArrayList ecsLogLines = readShadeLogFile(); + ArrayList ecsLogLines = readEcsLogFile(); assertThat(ecsLogLines).hasSize(2); - verifyEcsFormat(rawLogLines.get(2), ecsLogLines.get(0), null); - verifyEcsFormat(rawLogLines.get(3), ecsLogLines.get(1), null); + verifyEcsFormat(rawLogLines.get(2), ecsLogLines.get(0)); + verifyEcsFormat(rawLogLines.get(3), ecsLogLines.get(1)); } @Test - public void testLogShadingReplaceOriginal() throws IOException { - initializeShadeDir("replace"); + public void testLogReformattingReplaceOriginal() throws IOException { + initializeReformattingDir("replace"); setEcsReformattingConfig(LogEcsReformatting.REPLACE); logger.trace(TRACE_MESSAGE); logger.debug(DEBUG_MESSAGE); logger.warn(WARN_MESSAGE); - logger.error(ERROR_MESSAGE); + logger.error(ERROR_MESSAGE, new Throwable()); assertThat(readRawLogLines()).isEmpty(); - ArrayList shadeLogEvents = readShadeLogFile(); - assertThat(shadeLogEvents).hasSize(4); - for (JsonNode ecsLogLineTree : shadeLogEvents) { + ArrayList ecsLogEvents = readEcsLogFile(); + assertThat(ecsLogEvents).hasSize(4); + for (JsonNode ecsLogLineTree : ecsLogEvents) { verifyEcsLogLine(ecsLogLineTree); } } @@ -208,7 +221,7 @@ public void testLogOverride() throws IOException { logger.trace(TRACE_MESSAGE); logger.debug(DEBUG_MESSAGE); logger.warn(WARN_MESSAGE); - logger.error(ERROR_MESSAGE); + logger.error(ERROR_MESSAGE, new Throwable()); ArrayList overriddenLogEvents = TestUtils.readJsonFile(getOriginalLogFilePath().toString()); assertThat(overriddenLogEvents).hasSize(4); @@ -219,20 +232,20 @@ public void testLogOverride() throws IOException { @Test public void testEmptyFormatterAllowList() throws Exception { - initializeShadeDir("disabled"); + initializeReformattingDir("disabled"); setEcsReformattingConfig(LogEcsReformatting.SHADE); doReturn(Collections.EMPTY_LIST).when(loggingConfig).getLogEcsFormatterAllowList(); logger.trace(TRACE_MESSAGE); logger.debug(DEBUG_MESSAGE); logger.warn(WARN_MESSAGE); - logger.error(ERROR_MESSAGE); + logger.error(ERROR_MESSAGE, new Throwable()); assertThat(readRawLogLines()).hasSize(4); - assertThat(Files.exists(Paths.get(getShadeLogFilePath()))).isFalse(); + assertThat(Paths.get(getLogReformattingFilePath())).doesNotExist(); } @Test public void testDynamicConfiguration() throws Exception { - initializeShadeDir("dynamic"); + initializeReformattingDir("dynamic"); for (int i = 0; i < 2; i++) { setEcsReformattingConfig(LogEcsReformatting.OFF); logger.trace(TRACE_MESSAGE); @@ -241,15 +254,15 @@ public void testDynamicConfiguration() throws Exception { setEcsReformattingConfig(LogEcsReformatting.SHADE); logger.warn(WARN_MESSAGE); setEcsReformattingConfig(LogEcsReformatting.REPLACE); - logger.error(ERROR_MESSAGE); + logger.error(ERROR_MESSAGE, new Throwable()); } // ERROR messages should not appear in original log as they were replaced ArrayList originalLogLines = Files.lines(getOriginalLogFilePath()).collect(Collectors.toCollection(ArrayList::new)); assertThat(originalLogLines).hasSize(6); - // ECS shade file should contain only WARN and ERROR messages - ArrayList ecsLogLines = readShadeLogFile(); + // ECS log file should contain only WARN and ERROR messages + ArrayList ecsLogLines = readEcsLogFile(); assertThat(ecsLogLines).hasSize(4); // TRACE messages should be only in original file in original format @@ -265,12 +278,12 @@ public void testDynamicConfiguration() throws Exception { assertThat(debugLogLine.get("log.level").textValue()).isEqualTo("DEBUG"); // WARN messages should match content but not format - verifyEcsFormat(originalLogLines.get(2).split("\\s+"), ecsLogLines.get(0), null); + verifyEcsFormat(originalLogLines.get(2).split("\\s+"), ecsLogLines.get(0)); assertThat(ecsLogLines.get(0).get("log.level").textValue()).isEqualTo("WARN"); - verifyEcsFormat(originalLogLines.get(5).split("\\s+"), ecsLogLines.get(2), null); + verifyEcsFormat(originalLogLines.get(5).split("\\s+"), ecsLogLines.get(2)); assertThat(ecsLogLines.get(2).get("log.level").textValue()).isEqualTo("WARN"); - // ERROR messages should be only in shade file in ECS format + // ERROR messages should be only in ECS log file verifyEcsLogLine(ecsLogLines.get(1)); assertThat(ecsLogLines.get(1).get("log.level").textValue()).isEqualTo("ERROR"); verifyEcsLogLine(ecsLogLines.get(3)); @@ -280,7 +293,9 @@ public void testDynamicConfiguration() throws Exception { private void verifyEcsLogLine(JsonNode ecsLogLineTree) { assertThat(ecsLogLineTree.get("@timestamp")).isNotNull(); assertThat(ecsLogLineTree.get("process.thread.name").textValue()).isEqualTo("main"); - assertThat(ecsLogLineTree.get("log.level")).isNotNull(); + JsonNode logLevel = ecsLogLineTree.get("log.level"); + assertThat(logLevel).isNotNull(); + boolean isErrorLine = logLevel.textValue().equalsIgnoreCase("error"); assertThat(ecsLogLineTree.get("log.logger").textValue()).isEqualTo("Test-File-Logger"); assertThat(ecsLogLineTree.get("message")).isNotNull(); assertThat(ecsLogLineTree.get("service.name").textValue()).isEqualTo(serviceName); @@ -288,40 +303,66 @@ private void verifyEcsLogLine(JsonNode ecsLogLineTree) { assertThat(ecsLogLineTree.get("event.dataset").textValue()).isEqualTo(serviceName + ".FILE"); assertThat(ecsLogLineTree.get("service.version").textValue()).isEqualTo("v42"); assertThat(ecsLogLineTree.get("some.field").textValue()).isEqualTo("some-value"); + assertThat(ecsLogLineTree.get(AbstractLogCorrelationHelper.TRACE_ID_MDC_KEY).textValue()).isEqualTo(transaction.getTraceContext().getTraceId().toString()); + assertThat(ecsLogLineTree.get(AbstractLogCorrelationHelper.TRANSACTION_ID_MDC_KEY).textValue()).isEqualTo(transaction.getTraceContext().getTransactionId().toString()); + verifyErrorCaptureAndCorrelation(isErrorLine, ecsLogLineTree); } - @Nonnull - private ArrayList readShadeLogFile() throws IOException { - return TestUtils.readJsonFile(getShadeLogFilePath()); + private void verifyErrorCaptureAndCorrelation(boolean isErrorLine, JsonNode ecsLogLineTree) { + final JsonNode errorJsonNode = ecsLogLineTree.get(AbstractLogCorrelationHelper.ERROR_ID_MDC_KEY); + if (isErrorLine) { + assertThat(errorJsonNode).isNotNull(); + List errors = reporter.getErrors().stream() + .filter(error -> errorJsonNode.textValue().equals(error.getTraceContext().getId().toString())) + .collect(Collectors.toList()); + assertThat(errors).hasSize(1); + } else { + assertThat(errorJsonNode).isNull(); + } + } + + private ArrayList readEcsLogFile() throws IOException { + return TestUtils.readJsonFile(getLogReformattingFilePath()); } - @Nonnull private ArrayList readRawLogLines() throws IOException { ArrayList rawLogLines; try (Stream stream = Files.lines(getOriginalLogFilePath())) { - rawLogLines = stream.map(line -> line.split("\\s+")).collect(Collectors.toCollection(ArrayList::new)); + rawLogLines = stream + .map(line -> line.split("\\s+")) + // ignoring lines related to Throwable logging + .filter(lineParts -> safelyParseDate(lineParts[0]) != null) + .collect(Collectors.toCollection(ArrayList::new)); } return rawLogLines; } - @Nonnull + @Nullable + private Date safelyParseDate(String dateAsString) { + try { + return timestampFormat.parse(dateAsString); + } catch (ParseException e) { + // the purpose of this method is to find valid date formats + return null; + } + } + private Path getOriginalLogFilePath() { return Paths.get(logger.getLogFilePath()); } - @Nonnull - protected String getShadeLogFilePath() { - return Utils.computeShadeLogFilePath(logger.getLogFilePath(), loggingConfig.getLogEcsFormattingDestinationDir()); + protected String getLogReformattingFilePath() { + return Utils.computeLogReformattingFilePath(logger.getLogFilePath(), loggingConfig.getLogEcsFormattingDestinationDir()); } - private void verifyEcsFormat(String[] splitRawLogLine, JsonNode ecsLogLineTree, @Nullable String traceId) throws Exception { - SimpleDateFormat timestampFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + private void verifyEcsFormat(String[] splitRawLogLine, JsonNode ecsLogLineTree) throws Exception { Date rawTimestamp = timestampFormat.parse(splitRawLogLine[0]); - timestampFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - Date ecsTimestamp = timestampFormat.parse(ecsLogLineTree.get("@timestamp").textValue()); + Date ecsTimestamp = utcTimestampFormat.parse(ecsLogLineTree.get("@timestamp").textValue()); assertThat(rawTimestamp).isEqualTo(ecsTimestamp); assertThat(splitRawLogLine[1]).isEqualTo(ecsLogLineTree.get("process.thread.name").textValue()); - assertThat(splitRawLogLine[2]).isEqualTo(ecsLogLineTree.get("log.level").textValue()); + JsonNode logLevel = ecsLogLineTree.get("log.level"); + assertThat(splitRawLogLine[2]).isEqualTo(logLevel.textValue()); + boolean isErrorLine = logLevel.textValue().equalsIgnoreCase("error"); assertThat(splitRawLogLine[3]).isEqualTo(ecsLogLineTree.get("log.logger").textValue()); assertThat(splitRawLogLine[4]).isEqualTo(ecsLogLineTree.get("message").textValue()); assertThat(ecsLogLineTree.get("service.name").textValue()).isEqualTo(serviceName); @@ -329,13 +370,13 @@ private void verifyEcsFormat(String[] splitRawLogLine, JsonNode ecsLogLineTree, assertThat(ecsLogLineTree.get("event.dataset").textValue()).isEqualTo(serviceName + ".FILE"); assertThat(ecsLogLineTree.get("service.version").textValue()).isEqualTo("v42"); assertThat(ecsLogLineTree.get("some.field").textValue()).isEqualTo("some-value"); - JsonNode jsonTraceId = ecsLogLineTree.get("trace.id"); - if (traceId != null) { - assertThat(jsonTraceId).isNotNull(); - assertThat(jsonTraceId.asText()).isEqualTo(traceId); - } else { - assertThat(jsonTraceId).isNull(); - } + JsonNode traceId = ecsLogLineTree.get(AbstractLogCorrelationHelper.TRACE_ID_MDC_KEY); + assertThat(traceId).withFailMessage("Logging correlation does not work as expected").isNotNull(); + assertThat(traceId.textValue()).isEqualTo(transaction.getTraceContext().getTraceId().toString()); + JsonNode transactionId = ecsLogLineTree.get(AbstractLogCorrelationHelper.TRANSACTION_ID_MDC_KEY); + assertThat(transactionId).withFailMessage("Logging correlation does not work as expected").isNotNull(); + assertThat(transactionId.textValue()).isEqualTo(transaction.getTraceContext().getTransactionId().toString()); + verifyErrorCaptureAndCorrelation(isErrorLine, ecsLogLineTree); } /** @@ -348,9 +389,9 @@ private void verifyEcsFormat(String[] splitRawLogLine, JsonNode ecsLogLineTree, * @throws IOException thrown if we fail to read the shade log file */ @Test - public void testShadeLogRolling() throws IOException { + public void testReformattedLogRolling() throws IOException { setEcsReformattingConfig(LogEcsReformatting.SHADE); - initializeShadeDir("rolling"); + initializeReformattingDir("rolling"); when(loggingConfig.getLogFileSize()).thenReturn(100L); logger.trace("First line"); waitForFileRolling(); @@ -365,8 +406,8 @@ public void testShadeLogRolling() throws IOException { // However, while in log4j2 and Logback file rolling takes place BEFORE appending the new log event, in // log4j1 this happens AFTER the event is logged. This means we can only count on the non-active file to // contain a single line - String shadeLogFilePath = getShadeLogFilePath(); - ArrayList jsonNodes = TestUtils.readJsonFile(shadeLogFilePath + ".1"); + String ecsLogFilePath = getLogReformattingFilePath(); + ArrayList jsonNodes = TestUtils.readJsonFile(ecsLogFilePath + ".1"); assertThat(jsonNodes).hasSize(1); } diff --git a/apm-agent-plugins/apm-error-logging-plugin/src/test/java/co/elastic/apm/agent/errorlogging/AbstractErrorLoggingInstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/error/AbstractErrorLoggingInstrumentationTest.java similarity index 83% rename from apm-agent-plugins/apm-error-logging-plugin/src/test/java/co/elastic/apm/agent/errorlogging/AbstractErrorLoggingInstrumentationTest.java rename to apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/error/AbstractErrorLoggingInstrumentationTest.java index d05becbe96..24581c44ed 100644 --- a/apm-agent-plugins/apm-error-logging-plugin/src/test/java/co/elastic/apm/agent/errorlogging/AbstractErrorLoggingInstrumentationTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/error/AbstractErrorLoggingInstrumentationTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.errorlogging; +package co.elastic.apm.agent.loginstr.error; import co.elastic.apm.agent.AbstractInstrumentationTest; import co.elastic.apm.agent.impl.transaction.Transaction; @@ -25,7 +25,7 @@ import static org.junit.Assert.assertEquals; -abstract class AbstractErrorLoggingInstrumentationTest extends AbstractInstrumentationTest { +public abstract class AbstractErrorLoggingInstrumentationTest extends AbstractInstrumentationTest { private Transaction transaction; @@ -40,8 +40,8 @@ void endTransaction() { transaction.deactivate().end(); } - void verifyThatExceptionCaptured(int errorCount, String exceptionMessage, Class exceptionClass) { - assertEquals(errorCount, reporter.getErrors().size()); + protected void verifyThatExceptionCaptured(int errorCount, String exceptionMessage, Class exceptionClass) { + reporter.awaitErrorCount(1); Throwable exception = reporter.getErrors().get(0).getException(); assertEquals(exceptionMessage, exception.getMessage()); assertEquals(exceptionClass, exception.getClass()); diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/reformatting/UtilsTest.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/reformatting/UtilsTest.java new file mode 100644 index 0000000000..045051be41 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/reformatting/UtilsTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.loginstr.reformatting; + +import co.elastic.apm.agent.AbstractInstrumentationTest; +import co.elastic.apm.agent.logging.LogEcsReformatting; +import co.elastic.apm.agent.logging.LoggingConfiguration; +import org.junit.jupiter.api.Test; + +import javax.annotation.Nullable; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +public class UtilsTest extends AbstractInstrumentationTest { + + private static final String fileSeparator = System.getProperty("file.separator"); + + @Nullable + private final String logEcsFormattingDestinationDir = config.getConfig(LoggingConfiguration.class).getLogEcsFormattingDestinationDir(); + + private String computeReformattedLogFilePathWithConfiguredDir(String logFilePath) { + return Utils.computeLogReformattingFilePath(logFilePath, logEcsFormattingDestinationDir); + } + + @Test + void testReformattedPathComputation() { + assertThat(computeReformattedLogFilePathWithConfiguredDir("/test/absolute/path/app.log")).isEqualTo(replaceFileSeparator("/test/absolute/path/app.ecs.json")); + assertThat(computeReformattedLogFilePathWithConfiguredDir("test/relative/path/app.log")).isEqualTo(replaceFileSeparator("test/relative/path/app.ecs.json")); + assertThat(computeReformattedLogFilePathWithConfiguredDir("/app.log")).isEqualTo(replaceFileSeparator("/app.ecs.json")); + assertThat(computeReformattedLogFilePathWithConfiguredDir("app.log")).isEqualTo(replaceFileSeparator("app.ecs.json")); + } + + @Test + void testReplace() { + when(config.getConfig(LoggingConfiguration.class).getLogEcsReformatting()).thenReturn(LogEcsReformatting.REPLACE); + assertThat(computeReformattedLogFilePathWithConfiguredDir("/test/absolute/path/app.log")).isEqualTo(replaceFileSeparator("/test/absolute/path/app.ecs.json")); + assertThat(computeReformattedLogFilePathWithConfiguredDir("/test/absolute/path/app")).isEqualTo(replaceFileSeparator("/test/absolute/path/app.ecs.json")); + assertThat(computeReformattedLogFilePathWithConfiguredDir("/test/absolute/path/app.log.1")).isEqualTo(replaceFileSeparator("/test/absolute/path/app.log.ecs.json")); + } + + @Test + void testAlternativeLogReformattingDestination_AbsolutePath() { + String reformattedDir = "/some/alt/location"; + assertThat(Utils.computeLogReformattingFilePath("/test/absolute/path/app.log", reformattedDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); + assertThat(Utils.computeLogReformattingFilePath("test/relative/path/app.log", reformattedDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); + assertThat(Utils.computeLogReformattingFilePath("/app.log", reformattedDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); + assertThat(Utils.computeLogReformattingFilePath("app.log", reformattedDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); + } + + @Test + void testAlternativeLogsReformattingDestination_RelativePath() { + String reformattedDir = "some/alt/location"; + assertThat(Utils.computeLogReformattingFilePath("/test/absolute/path/app.log", reformattedDir)).isEqualTo(replaceFileSeparator("/test/absolute/path/some/alt/location/app.ecs.json")); + assertThat(Utils.computeLogReformattingFilePath("test/relative/path/app.log", reformattedDir)).isEqualTo(replaceFileSeparator("test/relative/path/some/alt/location/app.ecs.json")); + assertThat(Utils.computeLogReformattingFilePath("/app.log", reformattedDir)).isEqualTo(replaceFileSeparator("/some/alt/location/app.ecs.json")); + assertThat(Utils.computeLogReformattingFilePath("app.log", reformattedDir)).isEqualTo(replaceFileSeparator("some/alt/location/app.ecs.json")); + } + + @Test + void testFileExtensionReplacement() { + assertThat(Utils.replaceFileExtensionToEcsJson("app.log")).isEqualTo("app.ecs.json"); + assertThat(Utils.replaceFileExtensionToEcsJson("app")).isEqualTo("app.ecs.json"); + assertThat(Utils.replaceFileExtensionToEcsJson("app.some.log")).isEqualTo("app.some.ecs.json"); + } + + private String replaceFileSeparator(String input) { + return input.replace("/", fileSeparator); + } + +} diff --git a/apm-agent-plugins/apm-error-logging-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/pom.xml similarity index 51% rename from apm-agent-plugins/apm-error-logging-plugin/pom.xml rename to apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/pom.xml index d1dbc2fbf0..bcb2f3c400 100644 --- a/apm-agent-plugins/apm-error-logging-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/pom.xml @@ -3,25 +3,30 @@ 4.0.0 - apm-agent-plugins + apm-logging-plugin co.elastic.apm 1.29.1-SNAPSHOT - apm-error-logging-plugin + apm-slf4j-plugin ${project.groupId}:${project.artifactId} - ${project.basedir}/../.. + ${project.basedir}/../../.. - org.apache.logging.log4j - log4j-api - ${version.log4j} + ${project.groupId} + apm-logging-plugin-common + ${project.version} + + + ${project.groupId} + apm-logging-plugin-common + ${project.version} + test-jar test - diff --git a/apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/Slf4jLoggerErrorCapturingInstrumentation.java b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/main/java/co/elastic/apm/agent/slf4j/error/Slf4jLoggerErrorCapturingInstrumentation.java similarity index 85% rename from apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/Slf4jLoggerErrorCapturingInstrumentation.java rename to apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/main/java/co/elastic/apm/agent/slf4j/error/Slf4jLoggerErrorCapturingInstrumentation.java index 655dbda2ea..adae066c66 100644 --- a/apm-agent-plugins/apm-error-logging-plugin/src/main/java/co/elastic/apm/agent/errorlogging/Slf4jLoggerErrorCapturingInstrumentation.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/main/java/co/elastic/apm/agent/slf4j/error/Slf4jLoggerErrorCapturingInstrumentation.java @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.errorlogging; +package co.elastic.apm.agent.slf4j.error; +import co.elastic.apm.agent.loginstr.error.AbstractLoggerErrorCapturingInstrumentation; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -27,14 +28,15 @@ import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.not; +/** + * Instruments {@link org.slf4j.Logger#error(String, Throwable)} + */ public class Slf4jLoggerErrorCapturingInstrumentation extends AbstractLoggerErrorCapturingInstrumentation { - static final String SLF4J_LOGGER = "org.slf4j.Logger"; - @Override public ElementMatcher getTypeMatcher() { return hasSuperType(named(SLF4J_LOGGER) - .and(not(hasSuperType(named(Log4j2LoggerErrorCapturingInstrumentation.LOG4J2_LOGGER))))); + .and(not(hasSuperType(named(LOG4J2_LOGGER))))); } @Override diff --git a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/package-info.java b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/main/java/co/elastic/apm/agent/slf4j/error/package-info.java similarity index 95% rename from apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/package-info.java rename to apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/main/java/co/elastic/apm/agent/slf4j/error/package-info.java index 2f42587cfb..7ddbcf39ad 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/apm-log-shader-plugin-common/src/main/java/co/elastic/apm/agent/log/shader/package-info.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/main/java/co/elastic/apm/agent/slf4j/error/package-info.java @@ -17,6 +17,6 @@ * under the License. */ @NonnullApi -package co.elastic.apm.agent.log.shader; +package co.elastic.apm.agent.slf4j.error; import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation new file mode 100644 index 0000000000..9ee625e2f5 --- /dev/null +++ b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation @@ -0,0 +1 @@ +co.elastic.apm.agent.slf4j.error.Slf4jLoggerErrorCapturingInstrumentation diff --git a/apm-agent-plugins/apm-error-logging-plugin/src/test/java/co/elastic/apm/agent/errorlogging/Slf4jLoggerErrorCapturingInstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/test/java/co/elastic/apm/agent/slf4j/error/Slf4jLoggerErrorCapturingInstrumentationTest.java similarity index 91% rename from apm-agent-plugins/apm-error-logging-plugin/src/test/java/co/elastic/apm/agent/errorlogging/Slf4jLoggerErrorCapturingInstrumentationTest.java rename to apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/test/java/co/elastic/apm/agent/slf4j/error/Slf4jLoggerErrorCapturingInstrumentationTest.java index 62da970a5d..5676b71b94 100644 --- a/apm-agent-plugins/apm-error-logging-plugin/src/test/java/co/elastic/apm/agent/errorlogging/Slf4jLoggerErrorCapturingInstrumentationTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/src/test/java/co/elastic/apm/agent/slf4j/error/Slf4jLoggerErrorCapturingInstrumentationTest.java @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.errorlogging; +package co.elastic.apm.agent.slf4j.error; +import co.elastic.apm.agent.loginstr.error.AbstractErrorLoggingInstrumentationTest; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/apm-agent-plugins/apm-log-shader-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/pom.xml similarity index 82% rename from apm-agent-plugins/apm-log-shader-plugin/pom.xml rename to apm-agent-plugins/apm-logging-plugin/pom.xml index 10db060eb6..f720f4d04f 100644 --- a/apm-agent-plugins/apm-log-shader-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/pom.xml @@ -8,7 +8,7 @@ 1.29.1-SNAPSHOT - apm-log-shader-plugin + apm-logging-plugin ${project.groupId}:${project.artifactId} pom @@ -18,10 +18,12 @@ - apm-log-shader-plugin-common + apm-logging-plugin-common apm-logback-plugin apm-log4j1-plugin apm-log4j2-plugin + apm-jboss-logging-plugin + apm-slf4j-plugin diff --git a/apm-agent-plugins/apm-profiling-plugin/src/main/java/co/elastic/apm/agent/profiler/ProfilingActivationListener.java b/apm-agent-plugins/apm-profiling-plugin/src/main/java/co/elastic/apm/agent/profiler/ProfilingActivationListener.java index b2d8307e02..277759536f 100644 --- a/apm-agent-plugins/apm-profiling-plugin/src/main/java/co/elastic/apm/agent/profiler/ProfilingActivationListener.java +++ b/apm-agent-plugins/apm-profiling-plugin/src/main/java/co/elastic/apm/agent/profiler/ProfilingActivationListener.java @@ -20,7 +20,6 @@ import co.elastic.apm.agent.impl.ActivationListener; import co.elastic.apm.agent.impl.ElasticApmTracer; -import co.elastic.apm.agent.impl.error.ErrorCapture; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import java.util.Objects; @@ -47,12 +46,6 @@ public void beforeActivate(AbstractSpan context) { } } - @Override - public void beforeActivate(ErrorCapture error) { - // noop - } - - @Override public void afterDeactivate(AbstractSpan deactivatedContext) { if (deactivatedContext.isSampled()) { @@ -60,9 +53,4 @@ public void afterDeactivate(AbstractSpan deactivatedContext) { profiler.onDeactivation(deactivatedContext.getTraceContext(), active != null ? active.getTraceContext() : null); } } - - @Override - public void afterDeactivate(ErrorCapture deactivatedError) { - // noop - } } diff --git a/apm-agent-plugins/pom.xml b/apm-agent-plugins/pom.xml index 522023c69e..cbcc87512a 100644 --- a/apm-agent-plugins/pom.xml +++ b/apm-agent-plugins/pom.xml @@ -30,8 +30,7 @@ apm-apache-httpclient-plugin apm-spring-resttemplate apm-httpclient-core - apm-log-correlation-plugin - apm-log-shader-plugin + apm-logging-plugin apm-log-shipper-plugin apm-es-restclient-plugin apm-okhttp-plugin @@ -45,7 +44,6 @@ apm-hibernate-search-plugin apm-redis-plugin apm-scala-concurrent-plugin - apm-error-logging-plugin apm-jmx-plugin apm-mongoclient-plugin apm-process-plugin diff --git a/apm-agent/pom.xml b/apm-agent/pom.xml index 169c4e56f6..62a3234078 100644 --- a/apm-agent/pom.xml +++ b/apm-agent/pom.xml @@ -66,11 +66,6 @@ apm-dubbo-plugin ${project.version} - - ${project.groupId} - apm-error-logging-plugin - ${project.version} - ${project.groupId} apm-es-restclient-plugin-5_6 @@ -171,11 +166,6 @@ apm-lettuce-plugin ${project.version} - - ${project.groupId} - apm-log-correlation-plugin - ${project.version} - ${project.groupId} apm-log-shipper-plugin @@ -196,6 +186,16 @@ apm-logback-plugin-impl ${project.version} + + ${project.groupId} + apm-jboss-logging-plugin + ${project.version} + + + ${project.groupId} + apm-slf4j-plugin + ${project.version} + ${project.groupId} apm-micrometer-plugin diff --git a/apm-agent/src/test/java/co/elastic/apm/agent/PackagingTest.java b/apm-agent/src/test/java/co/elastic/apm/agent/PackagingTest.java index 13cfe78b4e..ed687dd897 100644 --- a/apm-agent/src/test/java/co/elastic/apm/agent/PackagingTest.java +++ b/apm-agent/src/test/java/co/elastic/apm/agent/PackagingTest.java @@ -75,7 +75,6 @@ void checkPluginInterdependencies() { Set dependencies = plugin.getInternalDependencies().stream() .filter(d -> !d.equals("apm-agent-core")) // filter-out explicit dependencies to apm-agent-core - .filter(d -> !d.equals("apm-log-shader-plugin-common")) // TODO : remove this known case when logging refactor PR is merged. .map(modules::get) .collect(Collectors.toSet()); diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc index e1264778ee..c6a8e547ef 100644 --- a/docs/configuration.asciidoc +++ b/docs/configuration.asciidoc @@ -150,7 +150,6 @@ Click on a key to get more information. * <> ** <> ** <> -** <> ** <> ** <> ** <> @@ -762,7 +761,7 @@ you should add an additional entry to this list (make sure to also include the d ==== `enable_instrumentations` (added[1.28.0]) A list of instrumentations which should be selectively enabled. -Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. +Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. When set to non-empty value, only listed instrumentations will be enabled if they are not disabled through <> or <>. When not set or empty (default), all instrumentations enabled by default will be enabled unless they are disabled through <> or <>. @@ -790,7 +789,7 @@ NOTE: Changing this value at runtime can slow down the application temporarily. ==== `disable_instrumentations` (added[1.0.0,Changing this value at runtime is possible since version 1.15.0]) A list of instrumentations which should be disabled. -Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. +Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. For version `1.25.0` and later, use <> to enable experimental instrumentations. NOTE: Changing this value at runtime can slow down the application temporarily. @@ -1871,34 +1870,6 @@ When logging to std out, the log will be formatted as plain-text. | `elastic.apm.log_file` | `log_file` | `ELASTIC_APM_LOG_FILE` |============ -// This file is auto generated. Please make your changes in *Configuration.java (for example CoreConfiguration.java) and execute ConfigurationExporter -[float] -[[config-enable-log-correlation]] -==== `enable_log_correlation` - -A boolean specifying if the agent should integrate into SLF4J's https://www.slf4j.org/api/org/slf4j/MDC.html[MDC] to enable trace-log correlation. -If set to `true`, the agent will set the `trace.id` and `transaction.id` for the currently active spans and transactions to the MDC. -Since version 1.16.0, the agent also adds `error.id` of captured error to the MDC just before the error message is logged. -See <> for more details. - -NOTE: While it's allowed to enable this setting at runtime, you can't disable it without a restart. - -<> - - -[options="header"] -|============ -| Default | Type | Dynamic -| `false` | Boolean | true -|============ - - -[options="header"] -|============ -| Java System Properties | Property file | Environment -| `elastic.apm.enable_log_correlation` | `enable_log_correlation` | `ELASTIC_APM_ENABLE_LOG_CORRELATION` -|============ - // This file is auto generated. Please make your changes in *Configuration.java (for example CoreConfiguration.java) and execute ConfigurationExporter [float] [[config-log-ecs-reformatting]] @@ -1909,7 +1880,7 @@ NOTE: This feature is currently experimental, which means it is disabled by defa Specifying whether and how the agent should automatically reformat application logs into {ecs-logging-ref}/index.html[ECS-compatible JSON], suitable for ingestion into Elasticsearch for further Log analysis. This functionality is available for log4j1, log4j2 and Logback. -Once this option is enabled with any valid option, log correlation will be activated as well, regardless of the <> configuration. +The ECS log lines will include active trace/transaction/error IDs, if there are such. Available options: @@ -3176,7 +3147,7 @@ Example: `5ms`. # sanitize_field_names=password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,*auth*,set-cookie # A list of instrumentations which should be selectively enabled. -# Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. +# Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. # When set to non-empty value, only listed instrumentations will be enabled if they are not disabled through <> or <>. # When not set or empty (default), all instrumentations enabled by default will be enabled unless they are disabled through <> or <>. # @@ -3189,7 +3160,7 @@ Example: `5ms`. # enable_instrumentations= # A list of instrumentations which should be disabled. -# Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. +# Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. # For version `1.25.0` and later, use <> to enable experimental instrumentations. # # NOTE: Changing this value at runtime can slow down the application temporarily. @@ -3796,23 +3767,10 @@ Example: `5ms`. # # log_file=System.out -# A boolean specifying if the agent should integrate into SLF4J's https://www.slf4j.org/api/org/slf4j/MDC.html[MDC] to enable trace-log correlation. -# If set to `true`, the agent will set the `trace.id` and `transaction.id` for the currently active spans and transactions to the MDC. -# Since version 1.16.0, the agent also adds `error.id` of captured error to the MDC just before the error message is logged. -# See <> for more details. -# -# NOTE: While it's allowed to enable this setting at runtime, you can't disable it without a restart. -# -# This setting can be changed at runtime -# Type: Boolean -# Default value: false -# -# enable_log_correlation=false - # Specifying whether and how the agent should automatically reformat application logs # into {ecs-logging-ref}/index.html[ECS-compatible JSON], suitable for ingestion into Elasticsearch for # further Log analysis. This functionality is available for log4j1, log4j2 and Logback. -# Once this option is enabled with any valid option, log correlation will be activated as well, regardless of the <> configuration. +# The ECS log lines will include active trace/transaction/error IDs, if there are such. # # Available options: # diff --git a/docs/log-correlation.asciidoc b/docs/log-correlation.asciidoc index 883d39b36d..9dc2fb2128 100644 --- a/docs/log-correlation.asciidoc +++ b/docs/log-correlation.asciidoc @@ -32,20 +32,17 @@ If you’re using plain-text logs it's recommended to parse out specific parts o like the timestamp, the log level, and the message. One way to do this is by using an {ref}/ingest.html[ingest pipeline] with a {ref}/grok-processor.html[grok processor]. [float] -[[log-correlation-enable]] -=== Step 2: Enable log correlation in the agent +[[log-correlation-extract-ids]] +=== Step 2: Extract the ID fields In order to correlate logs from your application with transactions captured by the Elastic APM Java Agent, -the agent will inject the following IDs in your https://www.slf4j.org/api/org/slf4j/MDC.html[MDC] when <> is enabled: +the agent injects the following IDs into https://www.slf4j.org/api/org/slf4j/MDC.html[slf4j-MDC]-equivalents of +<>: * {ecs-ref}/ecs-tracing.html[`transaction.id`] * {ecs-ref}/ecs-tracing.html[`trace.id`] * {ecs-ref}/ecs-error.html[`error.id`] -[float] -[[log-correlation-extract-ids]] -=== Step 3: Extract the ID fields - If you are using https://github.com/elastic/java-ecs-logging[Java ECS logging], there's nothing to do in this step. The IDs in the MDC are automatically added to the right fields. diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index b2272ad8eb..7c3d0f4fcf 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -481,24 +481,18 @@ NOTE: only classes from the quartz-jobs dependency will be instrumented automati |slf4j |1.4.1+ -|When <> is set to `true`, - the agent will add a https://www.slf4j.org/api/org/slf4j/MDC.html[MDC] entry for `trace.id` and `transaction.id`. - See the <> configuration option for more details. - - Automatically <> for `logger.error("message", exception)` calls (since 1.10.0). +|Automatically <> for `logger.error("message", exception)` calls (since 1.10.0). When doing so, the ID corresponding the captured error (`error.id`) is added to the MDC as well (since 1.16.0). -|Trace correlation - 1.0.0 - -Error capturing - 1.10.0 +|1.10.0 |log4j2 |Trace correlation - 2.0+ ECS Reformatting - 2.6+ -|When <> is set to `true`, -the agent will add a https://logging.apache.org/log4j/2.x/manual/thread-context.html[ThreadContext] entry for `trace.id` and `transaction.id`. +|The agent adds https://logging.apache.org/log4j/2.x/manual/thread-context.html[ThreadContext] entries for active `trace.id`, +`transaction.id` and `error.id`. The agent sets the service name for the `EcsLayout` if not provided explicitly (since 1.29.0). @@ -516,33 +510,43 @@ ECS Service Name - 1.29.0 ECS Reformatting - 1.22.0 -|log4j -|Trace correlation - 1.x +|log4j1 +|Trace correlation and error capture - 1.x ECS Reformatting - 1.2.17 -|When <> is set to `true`, -the agent will add a https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html[MDC] entry for `trace.id` and `transaction.id`. +|The agent adds https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html[MDC] entries for active `trace.id`, +`transaction.id` and `error.id`. When <> is enabled, logs will be automatically reformatted into ECS-compatible format (since 1.22.0, experimental) + +Automatically <> for `logger.error("message", exception)` calls. +When doing so, the ID corresponding the captured error (`error.id`) is added to the MDC as well. |Trace correlation - 1.13.0 ECS Reformatting - 1.22.0 +Error capturing - 1.30.0 + |Logback |1.1.0+ -|When <> is enabled, logs will be automatically reformatted into +|The agent adds https://www.slf4j.org/api/org/slf4j/MDC.html[slf4j MDC] entries for active `trace.id`, `transaction.id` and `error.id`. + +When <> is enabled, logs will be automatically reformatted into ECS-compatible format (since 1.22.0, experimental) -|1.22.0 +|Trace correlation - 1.0.0 + +ECS Reformatting - 1.22.0 |JBoss Logging |3.0.0+ -|When <> is set to `true`, -the agent will add a http://javadox.com/org.jboss.logging/jboss-logging/3.3.0.Final/org/jboss/logging/MDC.html[MDC] -entry for `trace.id` and `transaction.id`. See the <> configuration option for more details. +|The agent adds http://javadox.com/org.jboss.logging/jboss-logging/3.3.0.Final/org/jboss/logging/MDC.html[MDC] +entries for `trace.id`, `transaction.id` and `error.id`. |1.23.0 +JBoss LogManager - 1.30.0 + |=== [float] diff --git a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/tests/ExternalPluginTestApp.java b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/tests/ExternalPluginTestApp.java index 27621fe601..a9cd1df9d1 100644 --- a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/tests/ExternalPluginTestApp.java +++ b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/tests/ExternalPluginTestApp.java @@ -65,8 +65,7 @@ public Map getAdditionalFilesToBind() { @Override public Map getAdditionalEnvVariables() { return Map.of( - "ELASTIC_APM_PLUGINS_DIR", "/plugins", - "ELASTIC_APM_ENABLE_LOG_CORRELATION", "true" + "ELASTIC_APM_PLUGINS_DIR", "/plugins" ); }