Skip to content

Commit 8bd08ba

Browse files
Pass MDC tags as Sentry tags (#1954)
* Pass MDC tags as Sentry tags in Logback integration. * Pass MDC tags as Sentry tags in Log4j2 integration. * Configure MDC tags through external configuration. * Polish. * Pass MDC tags as Sentry tags in JUL integration. * Changelog. * Update logback sample to include multiple tags. * Update log4j2 sample to include multiple tags. * Rename `mdcTags` to `contextTags`.
1 parent 3bbbcfc commit 8bd08ba

File tree

18 files changed

+181
-14
lines changed

18 files changed

+181
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 6.0.0-alpha.4
44

55
* Ref: Remove not needed interface abstractions on Android (#1953)
6+
* Feat: Pass MDC tags as Sentry tags (#1954)
67

78
## 6.0.0-alpha.3
89

sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class SentryHandler extends Handler {
4242

4343
private @NotNull Level minimumBreadcrumbLevel = Level.INFO;
4444
private @NotNull Level minimumEventLevel = Level.SEVERE;
45+
private @NotNull SentryOptions options;
4546

4647
/** Creates an instance of SentryHandler. */
4748
public SentryHandler() {
@@ -60,6 +61,7 @@ public SentryHandler(final @NotNull SentryOptions options) {
6061
/** Creates an instance of SentryHandler. */
6162
@TestOnly
6263
SentryHandler(final @NotNull SentryOptions options, final boolean configureFromLogManager) {
64+
this.options = options;
6365
setFilter(new DropSentryFilter());
6466
if (configureFromLogManager) {
6567
retrieveProperties();
@@ -200,7 +202,20 @@ SentryEvent createEvent(final @NotNull LogRecord record) {
200202
mdcProperties =
201203
CollectionUtils.filterMapEntries(mdcProperties, entry -> entry.getValue() != null);
202204
if (!mdcProperties.isEmpty()) {
203-
event.getContexts().put("MDC", mdcProperties);
205+
if (!options.getContextTags().isEmpty()) {
206+
for (final String contextTag : options.getContextTags()) {
207+
// if mdc tag is listed in SentryOptions, apply as event tag
208+
if (mdcProperties.containsKey(contextTag)) {
209+
event.setTag(contextTag, mdcProperties.get(contextTag));
210+
// remove from all tags applied to logging event
211+
mdcProperties.remove(contextTag);
212+
}
213+
}
214+
}
215+
// put the rest of mdc tags in contexts
216+
if (!mdcProperties.isEmpty()) {
217+
event.getContexts().put("MDC", mdcProperties);
218+
}
204219
}
205220
}
206221
event.setExtra(THREAD_ID, record.getThreadID());

sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ import kotlin.test.assertNull
2424
import kotlin.test.assertTrue
2525

2626
class SentryHandlerTest {
27-
private class Fixture(minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, val configureWithLogManager: Boolean = false, val transport: ITransport = mock()) {
27+
private class Fixture(minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, val configureWithLogManager: Boolean = false, val transport: ITransport = mock(), contextTags: List<String>? = null) {
2828
var logger: Logger
2929
var handler: SentryHandler
3030

3131
init {
3232
val options = SentryOptions()
3333
options.dsn = "http://key@localhost/proj"
3434
options.setTransportFactory { _, _ -> transport }
35+
contextTags?.forEach { options.addContextTag(it) }
3536
logger = Logger.getLogger("jul.SentryHandlerTest")
3637
handler = SentryHandler(options, configureWithLogManager)
3738
handler.setMinimumBreadcrumbLevel(minimumBreadcrumbLevel)
@@ -312,6 +313,22 @@ class SentryHandlerTest {
312313
)
313314
}
314315

316+
@Test
317+
fun `sets tags as Sentry tags from MDC`() {
318+
fixture = Fixture(minimumEventLevel = Level.WARNING, contextTags = listOf("contextTag1"))
319+
MDC.put("key", "value")
320+
MDC.put("contextTag1", "contextTag1Value")
321+
fixture.logger.warning("testing MDC tags")
322+
323+
verify(fixture.transport).send(
324+
checkEvent { event ->
325+
assertEquals(mapOf("key" to "value"), event.contexts["MDC"])
326+
assertEquals(mapOf("contextTag1" to "contextTag1Value"), event.tags)
327+
},
328+
anyOrNull()
329+
)
330+
}
331+
315332
@Test
316333
fun `ignore set tags with null values from MDC`() {
317334
fixture = Fixture(minimumEventLevel = Level.WARNING)

sentry-log4j2/api/sentry-log4j2.api

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ public final class io/sentry/log4j2/BuildConfig {
44
}
55

66
public class io/sentry/log4j2/SentryAppender : org/apache/logging/log4j/core/appender/AbstractAppender {
7-
public fun <init> (Ljava/lang/String;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/Boolean;Lio/sentry/ITransportFactory;Lio/sentry/IHub;)V
7+
public fun <init> (Ljava/lang/String;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/Boolean;Lio/sentry/ITransportFactory;Lio/sentry/IHub;[Ljava/lang/String;)V
88
public fun append (Lorg/apache/logging/log4j/core/LogEvent;)V
9-
public static fun createAppender (Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/String;Ljava/lang/Boolean;Lorg/apache/logging/log4j/core/Filter;)Lio/sentry/log4j2/SentryAppender;
9+
public static fun createAppender (Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/String;Ljava/lang/Boolean;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;)Lio/sentry/log4j2/SentryAppender;
1010
protected fun createBreadcrumb (Lorg/apache/logging/log4j/core/LogEvent;)Lio/sentry/Breadcrumb;
1111
protected fun createEvent (Lorg/apache/logging/log4j/core/LogEvent;)Lio/sentry/SentryEvent;
1212
public fun start ()V

sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class SentryAppender extends AbstractAppender {
4646
private @NotNull Level minimumEventLevel = Level.ERROR;
4747
private final @Nullable Boolean debug;
4848
private final @NotNull IHub hub;
49+
private final @Nullable List<String> contextTags;
4950

5051
public SentryAppender(
5152
final @NotNull String name,
@@ -55,7 +56,8 @@ public SentryAppender(
5556
final @Nullable Level minimumEventLevel,
5657
final @Nullable Boolean debug,
5758
final @Nullable ITransportFactory transportFactory,
58-
final @NotNull IHub hub) {
59+
final @NotNull IHub hub,
60+
final @Nullable String[] contextTags) {
5961
super(name, filter, null, true, null);
6062
this.dsn = dsn;
6163
if (minimumBreadcrumbLevel != null) {
@@ -67,6 +69,7 @@ public SentryAppender(
6769
this.debug = debug;
6870
this.transportFactory = transportFactory;
6971
this.hub = hub;
72+
this.contextTags = contextTags != null ? Arrays.asList(contextTags) : null;
7073
}
7174

7275
/**
@@ -87,7 +90,8 @@ public SentryAppender(
8790
@Nullable @PluginAttribute("minimumEventLevel") final Level minimumEventLevel,
8891
@Nullable @PluginAttribute("dsn") final String dsn,
8992
@Nullable @PluginAttribute("debug") final Boolean debug,
90-
@Nullable @PluginElement("filter") final Filter filter) {
93+
@Nullable @PluginElement("filter") final Filter filter,
94+
@Nullable @PluginAttribute("contextTags") final String contextTags) {
9195

9296
if (name == null) {
9397
LOGGER.error("No name provided for SentryAppender");
@@ -101,7 +105,8 @@ public SentryAppender(
101105
minimumEventLevel,
102106
debug,
103107
null,
104-
HubAdapter.getInstance());
108+
HubAdapter.getInstance(),
109+
contextTags != null ? contextTags.split(",") : null);
105110
}
106111

107112
@Override
@@ -117,6 +122,11 @@ public void start() {
117122
}
118123
options.setSentryClientName(BuildConfig.SENTRY_LOG4J2_SDK_NAME);
119124
options.setSdkVersion(createSdkVersion(options));
125+
if (contextTags != null) {
126+
for (final String contextTag : contextTags) {
127+
options.addContextTag(contextTag);
128+
}
129+
}
120130
Optional.ofNullable(transportFactory).ifPresent(options::setTransportFactory);
121131
});
122132
} catch (IllegalArgumentException e) {
@@ -177,7 +187,20 @@ public void append(final @NotNull LogEvent eventObject) {
177187
CollectionUtils.filterMapEntries(
178188
loggingEvent.getContextData().toMap(), entry -> entry.getValue() != null);
179189
if (!contextData.isEmpty()) {
180-
event.getContexts().put("Context Data", contextData);
190+
if (contextTags != null && !contextTags.isEmpty()) {
191+
for (final String contextTag : contextTags) {
192+
// if mdc tag is listed in SentryOptions, apply as event tag
193+
if (contextData.containsKey(contextTag)) {
194+
event.setTag(contextTag, contextData.get(contextTag));
195+
// remove from all tags applied to logging event
196+
contextData.remove(contextTag);
197+
}
198+
}
199+
}
200+
// put the rest of mdc tags in contexts
201+
if (!contextData.isEmpty()) {
202+
event.getContexts().put("Context Data", contextData);
203+
}
181204
}
182205

183206
return event;

sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ class SentryAppenderTest {
4343
whenever(transportFactory.create(any(), any())).thenReturn(transport)
4444
}
4545

46-
fun getSut(transportFactory: ITransportFactory? = null, minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, debug: Boolean? = null): ExtendedLogger {
46+
fun getSut(transportFactory: ITransportFactory? = null, minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, debug: Boolean? = null, contextTags: List<String>? = null): ExtendedLogger {
4747
if (transportFactory != null) {
4848
this.transportFactory = transportFactory
4949
}
5050
loggerContext.start()
5151
val config: Configuration = loggerContext.configuration
52-
val appender = SentryAppender("sentry", null, "http://key@localhost/proj", minimumBreadcrumbLevel, minimumEventLevel, debug, this.transportFactory, HubAdapter.getInstance())
52+
val appender = SentryAppender("sentry", null, "http://key@localhost/proj", minimumBreadcrumbLevel, minimumEventLevel, debug, this.transportFactory, HubAdapter.getInstance(), contextTags?.toTypedArray())
5353
config.addAppender(appender)
5454

5555
val ref = AppenderRef.createAppenderRef("sentry", null, null)
@@ -241,6 +241,22 @@ class SentryAppenderTest {
241241
)
242242
}
243243

244+
@Test
245+
fun `sets tags from ThreadContext as Sentry tags`() {
246+
val logger = fixture.getSut(minimumEventLevel = Level.WARN, contextTags = listOf("contextTag1"))
247+
ThreadContext.put("key", "value")
248+
ThreadContext.put("contextTag1", "contextTag1Value")
249+
logger.warn("testing MDC tags")
250+
251+
verify(fixture.transport).send(
252+
checkEvent { event ->
253+
assertEquals(mapOf("key" to "value"), event.contexts["Context Data"])
254+
assertEquals(mapOf("contextTag1" to "contextTag1Value"), event.tags)
255+
},
256+
anyOrNull()
257+
)
258+
}
259+
244260
@Test
245261
fun `ignore set tags with null values from ThreadContext`() {
246262
val logger = fixture.getSut(minimumEventLevel = Level.WARN)

sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,20 @@ protected void append(@NotNull ILoggingEvent eventObject) {
112112
CollectionUtils.filterMapEntries(
113113
loggingEvent.getMDCPropertyMap(), entry -> entry.getValue() != null);
114114
if (!mdcProperties.isEmpty()) {
115-
event.getContexts().put("MDC", mdcProperties);
115+
if (!options.getContextTags().isEmpty()) {
116+
for (final String contextTag : options.getContextTags()) {
117+
// if mdc tag is listed in SentryOptions, apply as event tag
118+
if (mdcProperties.containsKey(contextTag)) {
119+
event.setTag(contextTag, mdcProperties.get(contextTag));
120+
// remove from all tags applied to logging event
121+
mdcProperties.remove(contextTag);
122+
}
123+
}
124+
}
125+
// put the rest of mdc tags in contexts
126+
if (!mdcProperties.isEmpty()) {
127+
event.getContexts().put("MDC", mdcProperties);
128+
}
116129
}
117130

118131
return event;

sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import kotlin.test.assertNull
3131
import kotlin.test.assertTrue
3232

3333
class SentryAppenderTest {
34-
private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null) {
34+
private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, contextTags: List<String>? = null) {
3535
val logger: Logger = LoggerFactory.getLogger(SentryAppenderTest::class.java)
3636
val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext
3737
val transportFactory = mock<ITransportFactory>()
@@ -43,6 +43,7 @@ class SentryAppenderTest {
4343
val appender = SentryAppender()
4444
val options = SentryOptions()
4545
options.dsn = dsn
46+
contextTags?.forEach { options.addContextTag(it) }
4647
appender.setOptions(options)
4748
appender.setMinimumBreadcrumbLevel(minimumBreadcrumbLevel)
4849
appender.setMinimumEventLevel(minimumEventLevel)
@@ -217,6 +218,22 @@ class SentryAppenderTest {
217218
)
218219
}
219220

221+
@Test
222+
fun `sets tags as sentry tags from MDC`() {
223+
fixture = Fixture(minimumEventLevel = Level.WARN, contextTags = listOf("contextTag1"))
224+
MDC.put("key", "value")
225+
MDC.put("contextTag1", "contextTag1Value")
226+
fixture.logger.warn("testing MDC tags")
227+
228+
verify(fixture.transport).send(
229+
checkEvent { event ->
230+
assertEquals(mapOf("key" to "value"), event.contexts["MDC"])
231+
assertEquals(mapOf("contextTag1" to "contextTag1Value"), event.tags)
232+
},
233+
anyOrNull()
234+
)
235+
}
236+
220237
@Test
221238
fun `ignore set tags with null values from MDC`() {
222239
fixture = Fixture(minimumEventLevel = Level.WARN)

sentry-samples/sentry-samples-jul/src/main/resources/sentry.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ dsn=https://[email protected]/5428563
33
debug=true
44
environment=staging
55
in-app-includes=io.sentry.samples
6+
context-tags=userId,requestId

sentry-samples/sentry-samples-log4j2/src/main/java/io/sentry/samples/log4j2/Main.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ public static void main(String[] args) {
1313
// Update the DSN in log4j2.xml to see these events in your Sentry dashboard.
1414
LOGGER.debug("Hello Sentry!");
1515

16-
// ThreadContext parameters are converted to Sentry Event tags
16+
// ThreadContext tags listed in log4j2.xml are converted to Sentry Event tags
1717
ThreadContext.put("userId", UUID.randomUUID().toString());
18+
ThreadContext.put("requestId", UUID.randomUUID().toString());
19+
// ThreadContext tag not listed in log4j2.xml
20+
ThreadContext.put("context-tag", "context-tag-value");
1821

1922
// logging arguments are converted to Sentry Event parameters
2023
LOGGER.info("User has made a purchase of product: {}", 445);

0 commit comments

Comments
 (0)