Skip to content

Commit fb5c1cf

Browse files
committed
Added support for setting the service name on Log4j2's EcsLayout (elastic#1626)
1 parent 721d892 commit fb5c1cf

File tree

10 files changed

+235
-1
lines changed

10 files changed

+235
-1
lines changed

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ public void overrideServiceNameForClassLoader(@Nullable ClassLoader classLoader,
762762
}
763763

764764
@Nullable
765-
private String getServiceName(@Nullable ClassLoader initiatingClassLoader) {
765+
public String getServiceName(@Nullable ClassLoader initiatingClassLoader) {
766766
if (initiatingClassLoader == null) {
767767
return null;
768768
}

apm-agent-core/src/main/java/co/elastic/apm/agent/logging/LoggingConfiguration.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ public void assertValid(Boolean value) {
162162
})
163163
.buildWithDefault(false);
164164

165+
private final ConfigurationOption<Boolean> logEcsServiceName = ConfigurationOption.booleanOption()
166+
.key("log_ecs_service_name")
167+
.configurationCategory(LOGGING_CATEGORY)
168+
.tags("added[1.28.0]")
169+
.description("Specifies if the agent should set the service name for an ECS compatible logging implementation if it is not explicitly set.\n\n" +
170+
"NOTE: Only log4j2-ecs-layout is currently supported")
171+
.dynamic(false)
172+
.buildWithDefault(false);
173+
165174
private final ConfigurationOption<LogEcsReformatting> logEcsReformatting = ConfigurationOption.enumOption(LogEcsReformatting.class)
166175
.key("log_ecs_reformatting")
167176
.configurationCategory(LOGGING_CATEGORY)
@@ -349,6 +358,10 @@ public boolean isLogCorrelationEnabled() {
349358
return logCorrelationEnabled.get() || getLogEcsReformatting() != LogEcsReformatting.OFF;
350359
}
351360

361+
public boolean getLogEcsServiceName() {
362+
return logEcsServiceName.get();
363+
}
364+
352365
public LogEcsReformatting getLogEcsReformatting() {
353366
return logEcsReformatting.get();
354367
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<artifactId>apm-agent-plugins</artifactId>
8+
<groupId>co.elastic.apm</groupId>
9+
<version>1.27.1-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>apm-ecs-logging-plugin</artifactId>
13+
<name>${project.groupId}:${project.artifactId}</name>
14+
15+
<properties>
16+
<apm-agent-parent.base.dir>${project.basedir}/../..</apm-agent-parent.base.dir>
17+
</properties>
18+
19+
<dependencies>
20+
<dependency>
21+
<groupId>co.elastic.logging</groupId>
22+
<artifactId>log4j-ecs-layout</artifactId>
23+
<version>1.2.0</version>
24+
<scope>provided</scope>
25+
</dependency>
26+
<dependency>
27+
<groupId>co.elastic.logging</groupId>
28+
<artifactId>log4j2-ecs-layout</artifactId>
29+
<version>1.2.0</version>
30+
<scope>provided</scope>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.apache.logging.log4j</groupId>
34+
<artifactId>log4j-core</artifactId>
35+
<version>2.14.1</version>
36+
<scope>provided</scope>
37+
</dependency>
38+
</dependencies>
39+
40+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.ecs_logging;
20+
21+
import co.elastic.apm.agent.bci.TracerAwareInstrumentation;
22+
import co.elastic.apm.agent.configuration.CoreConfiguration;
23+
import co.elastic.apm.agent.impl.ElasticApmTracer;
24+
import co.elastic.apm.agent.impl.GlobalTracer;
25+
import co.elastic.apm.agent.logging.LoggingConfiguration;
26+
import co.elastic.logging.log4j2.EcsLayout;
27+
import net.bytebuddy.asm.Advice;
28+
import net.bytebuddy.description.method.MethodDescription;
29+
import net.bytebuddy.description.type.TypeDescription;
30+
import net.bytebuddy.matcher.ElementMatcher;
31+
32+
import java.util.Arrays;
33+
import java.util.Collection;
34+
35+
import static net.bytebuddy.matcher.ElementMatchers.named;
36+
37+
public class Log4j2ServiceNameInstrumentation extends TracerAwareInstrumentation {
38+
39+
@Override
40+
public ElementMatcher<? super TypeDescription> getTypeMatcher() {
41+
return named("co.elastic.logging.log4j2.EcsLayout$Builder");
42+
}
43+
44+
@Override
45+
public ElementMatcher<? super MethodDescription> getMethodMatcher() {
46+
return named("build");
47+
}
48+
49+
@Override
50+
public Collection<String> getInstrumentationGroupNames() {
51+
return Arrays.asList("logging", "log4j2-ecs");
52+
}
53+
54+
public static class AdviceClass {
55+
56+
private static final ElasticApmTracer tracer = GlobalTracer.requireTracerImpl();
57+
58+
private static final String defaultServiceName = tracer.getConfig(CoreConfiguration.class).getServiceName();
59+
60+
private static final boolean logEcsServiceName = tracer.getConfig(LoggingConfiguration.class).getLogEcsServiceName();
61+
62+
@Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
63+
public static void onEnter(@Advice.This EcsLayout.Builder builder) {
64+
if (logEcsServiceName && (builder.getServiceName() == null || builder.getServiceName().isEmpty())) {
65+
String serviceName = tracer.getServiceName(Thread.currentThread().getContextClassLoader());
66+
builder.setServiceName(serviceName != null ? serviceName : defaultServiceName);
67+
}
68+
}
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
co.elastic.apm.agent.ecs_logging.Log4j2ServiceNameInstrumentation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.ecs_logging;
20+
21+
import co.elastic.apm.agent.AbstractInstrumentationTest;
22+
import co.elastic.apm.agent.configuration.CoreConfiguration;
23+
import co.elastic.apm.agent.logging.LoggingConfiguration;
24+
import co.elastic.logging.log4j2.EcsLayout;
25+
import com.fasterxml.jackson.core.JsonProcessingException;
26+
import com.fasterxml.jackson.databind.ObjectMapper;
27+
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
28+
import org.apache.logging.log4j.message.SimpleMessage;
29+
import org.junit.jupiter.api.BeforeAll;
30+
import org.junit.jupiter.api.Test;
31+
32+
import java.util.Map;
33+
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
import static org.mockito.Mockito.when;
36+
37+
class Log4j2ServiceNameInstrumentationTest extends AbstractInstrumentationTest {
38+
39+
@BeforeAll
40+
static void setUp() {
41+
when(tracer.getConfig(CoreConfiguration.class).getServiceName()).thenReturn("foo");
42+
when(tracer.getConfig(LoggingConfiguration.class).getLogEcsServiceName()).thenReturn(true);
43+
}
44+
45+
@Test
46+
void testBuildWithNoServiceNameSet() throws JsonProcessingException {
47+
EcsLayout ecsLayout = EcsLayout.newBuilder().build();
48+
assertThat(getServiceName(ecsLayout.toSerializable(createLogEvent()))).isEqualTo("foo");
49+
}
50+
51+
@Test
52+
void testBuildWithServiceNameSet() throws JsonProcessingException {
53+
EcsLayout ecsLayout = EcsLayout.newBuilder().setServiceName("bar").build();
54+
assertThat(getServiceName(ecsLayout.toSerializable(createLogEvent()))).isEqualTo("bar");
55+
}
56+
57+
private static Log4jLogEvent createLogEvent() {
58+
return new Log4jLogEvent("", null, "", null, new SimpleMessage(), null, null);
59+
}
60+
61+
private static String getServiceName(String json) throws JsonProcessingException {
62+
return (String) new ObjectMapper().readValue(json, Map.class).get("service.name");
63+
}
64+
}

apm-agent-plugins/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
<module>apm-jaxws-plugin-jakartaee-test</module>
6969
<module>apm-jaxrs-plugin-jakartaee-test</module>
7070
<module>apm-scheduled-annotation-plugin-jakartaee-test</module>
71+
<module>apm-ecs-logging-plugin</module>
7172
</modules>
7273

7374
<dependencies>

apm-agent/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,11 @@
316316
<artifactId>apm-vertx4-plugin</artifactId>
317317
<version>${project.version}</version>
318318
</dependency>
319+
<dependency>
320+
<groupId>${project.groupId}</groupId>
321+
<artifactId>apm-ecs-logging-plugin</artifactId>
322+
<version>${project.version}</version>
323+
</dependency>
319324

320325
<!-- For auto-generating configuration docs -->
321326
<dependency>

docs/configuration.asciidoc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ Click on a key to get more information.
142142
** <<config-log-level>>
143143
** <<config-log-file>>
144144
** <<config-enable-log-correlation>>
145+
** <<config-log-ecs-service-name>>
145146
** <<config-log-ecs-reformatting>>
146147
** <<config-log-ecs-reformatting-additional-fields>>
147148
** <<config-log-ecs-formatter-allow-list>>
@@ -1720,6 +1721,31 @@ NOTE: While it's allowed to enable this setting at runtime, you can't disable it
17201721
| `elastic.apm.enable_log_correlation` | `enable_log_correlation` | `ELASTIC_APM_ENABLE_LOG_CORRELATION`
17211722
|============
17221723

1724+
// This file is auto generated. Please make your changes in *Configuration.java (for example CoreConfiguration.java) and execute ConfigurationExporter
1725+
[float]
1726+
[[config-log-ecs-service-name]]
1727+
==== `log_ecs_service_name` (added[1.28.0])
1728+
1729+
Specifies if the agent should set the service name for an ECS compatible logging implementation if it is not explicitly set.
1730+
1731+
NOTE: Only log4j2-ecs-layout is currently supported
1732+
1733+
1734+
1735+
1736+
[options="header"]
1737+
|============
1738+
| Default | Type | Dynamic
1739+
| `false` | Boolean | false
1740+
|============
1741+
1742+
1743+
[options="header"]
1744+
|============
1745+
| Java System Properties | Property file | Environment
1746+
| `elastic.apm.log_ecs_service_name` | `log_ecs_service_name` | `ELASTIC_APM_LOG_ECS_SERVICE_NAME`
1747+
|============
1748+
17231749
// This file is auto generated. Please make your changes in *Configuration.java (for example CoreConfiguration.java) and execute ConfigurationExporter
17241750
[float]
17251751
[[config-log-ecs-reformatting]]
@@ -3492,6 +3518,16 @@ The default unit for this option is `ms`.
34923518
#
34933519
# enable_log_correlation=false
34943520
3521+
# Specifies if the agent should set the service name for an ECS compatible logging implementation if it is not explicitly set.
3522+
#
3523+
# NOTE: Only log4j2-ecs-layout is currently supported
3524+
#
3525+
# This setting can not be changed at runtime. Changes require a restart of the application.
3526+
# Type: Boolean
3527+
# Default value: false
3528+
#
3529+
# log_ecs_service_name=false
3530+
34953531
# Specifying whether and how the agent should automatically reformat application logs
34963532
# into {ecs-logging-ref}/index.html[ECS-compatible JSON], suitable for ingestion into Elasticsearch for
34973533
# further Log analysis. This functionality is available for log4j1, log4j2 and Logback.

docs/supported-technologies.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,8 @@ ECS Reformatting - 2.6+
493493
|When <<config-enable-log-correlation>> is set to `true`,
494494
the agent will add a https://logging.apache.org/log4j/2.x/manual/thread-context.html[ThreadContext] entry for `trace.id` and `transaction.id`.
495495

496+
When <<config-log-ecs-service-name, `log_ecs_service_name`>> is enabled, the agent sets the service name for the `EcsLayout` (since 1.28.0).
497+
496498
When <<config-log-ecs-reformatting, `log_ecs_reformatting`>> is enabled, logs will be automatically reformatted into
497499
ECS-compatible format (since 1.22.0, experimental)
498500

@@ -502,6 +504,8 @@ When doing so, the ID corresponding the captured error (`error.id`) is added to
502504

503505
Error capturing - 1.10.0
504506

507+
ECS Service Name - 1.28.0
508+
505509
ECS Reformatting - 1.22.0
506510

507511

0 commit comments

Comments
 (0)