Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
36d986e
Support excluding MDC attributes from capture-all
philsttr Jun 5, 2026
0e328af
Updated log4j-appender-1.2 test to verify excluded keys
philsttr Jun 5, 2026
4b36914
Ignore mdc/contextData exclusions unless `*` is included.
philsttr Jun 5, 2026
58c5e03
Updated jboss-logmanager-appender-1.1 test to verify excluded keys
philsttr Jun 5, 2026
25b8aed
use unmodifiableSet for mdc/contextData exclusions
philsttr Jun 5, 2026
f24c067
capture !foo literally, unless * is specified
philsttr Jun 5, 2026
ad15f97
Merge remote-tracking branch 'upstream/main' into exclude_mdc_entries
philsttr Jun 12, 2026
6971127
Use separate include/exclude properties with IncludeExcludePredicate …
philsttr Jun 12, 2026
4a55609
Merge remote-tracking branch 'upstream/main' into exclude_mdc_entries
philsttr Jun 12, 2026
85aa879
allow opentelemetry-sdk-common in muzzle
philsttr Jun 12, 2026
149aea4
update latest-dep-versions.json
philsttr Jun 12, 2026
2a97aea
Merge upstream/main into exclude_mdc_entries
trask Jul 14, 2026
033f947
Fix muzzle validation for SDK common dependency
trask Jul 14, 2026
eb3131a
Merge remote-tracking branch 'upstream/main' into exclude_mdc_entries
philsttr Jul 14, 2026
6211647
update test comment to reflect exclude-mdc-attributes config
philsttr Jul 14, 2026
34e0fc6
Fix MDC filter predicate classloader visibility
trask Jul 14, 2026
e4b64df
Update spring boot additional-spring-configuration-metadata.json for …
philsttr Jul 14, 2026
b030d27
add missing excludeMdcAttributes setting in logback appender readme
philsttr Jul 14, 2026
a82e654
Merge remote-tracking branch 'upstream/main' into exclude_mdc_entries
philsttr Jul 22, 2026
bdb4d99
Merge remote-tracking branch 'upstream/main' into exclude_mdc_entries
philsttr Jul 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions docs/instrumentation-list.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7825,12 +7825,22 @@ libraries:
- name: otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes
declarative_name: java.jboss_logmanager.capture_mdc_attributes/development
description: |
Controls which MDC attributes to capture. Use "*" to capture all MDC attributes or provide a comma-separated list of specific keys.
Controls which MDC attributes to capture. Use "*" to capture all MDC attributes or provide a comma-separated list of specific keys. Entries support glob wildcards ("*" and "?"). Use exclude-mdc-attributes to exclude specific keys.
type: list
default: ''
examples:
- custom-mdc-key
- key1,key2,key3
- '*'
- name: otel.instrumentation.jboss-logmanager.experimental.exclude-mdc-attributes
declarative_name: java.jboss_logmanager.exclude_mdc_attributes/development
description: |
Comma-separated list of MDC attribute keys to exclude from capture, supporting glob wildcards ("*" and "?"). Excludes only take effect alongside a non-empty capture-mdc-attributes list (e.g. capture-mdc-attributes="*" with exclude-mdc-attributes="secret*").
type: list
default: ''
examples:
- secret*
- key1,key2
- name: jboss-logmanager-mdc-1.1
display_name: JBoss Log Manager
description: |
Expand Down Expand Up @@ -10982,12 +10992,22 @@ libraries:
- name: otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes
declarative_name: java.log4j_appender.capture_mdc_attributes/development
description: |
Controls which MDC attributes to capture. Use "*" to capture all MDC attributes or provide a comma-separated list of specific keys.
Controls which MDC attributes to capture. Use "*" to capture all MDC attributes or provide a comma-separated list of specific keys. Entries support glob wildcards ("*" and "?"). Use exclude-mdc-attributes to exclude specific keys.
type: list
default: ''
examples:
- custom-mdc-key
- key1,key2,key3
- '*'
- name: otel.instrumentation.log4j-appender.experimental.exclude-mdc-attributes
declarative_name: java.log4j_appender.exclude_mdc_attributes/development
description: |
Comma-separated list of MDC attribute keys to exclude from capture, supporting glob wildcards ("*" and "?"). Excludes only take effect alongside a non-empty capture-mdc-attributes list (e.g. capture-mdc-attributes="*" with exclude-mdc-attributes="secret*").
type: list
default: ''
examples:
- secret*
- key1,key2
- name: otel.instrumentation.log4j-appender.experimental.capture-code-attributes
declarative_name: java.log4j_appender.capture_code_attributes/development
description: |
Expand Down Expand Up @@ -11036,12 +11056,21 @@ libraries:
- name: otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes
declarative_name: java.log4j_appender.capture_mdc_attributes/development
description: |
Controls which MDC attributes to capture. Use "*" to capture all MDC attributes or provide a comma-separated list of specific keys.
Controls which MDC attributes to capture. Use "*" to capture all MDC attributes or provide a comma-separated list of specific keys. Entries support glob wildcards ("*" and "?"). Use exclude-mdc-attributes to exclude specific keys.
type: list
default: ''
examples:
- '*'
- key1,key2
- name: otel.instrumentation.log4j-appender.experimental.exclude-mdc-attributes
declarative_name: java.log4j_appender.exclude_mdc_attributes/development
description: |
Comma-separated list of MDC attribute keys to exclude from capture, supporting glob wildcards ("*" and "?"). Excludes only take effect alongside a non-empty capture-mdc-attributes list (e.g. capture-mdc-attributes="*" with exclude-mdc-attributes="secret*").
type: list
default: ''
examples:
- secret*
- key1,key2
- name: log4j-context-data-2.7
display_name: Log4j
description: |
Expand Down Expand Up @@ -11234,12 +11263,22 @@ libraries:
- name: otel.instrumentation.logback-appender.experimental.capture-mdc-attributes
declarative_name: java.logback_appender.capture_mdc_attributes/development
description: |
Controls which MDC attributes to capture. Use "*" to capture all MDC attributes or provide a comma-separated list of specific keys.
Controls which MDC attributes to capture. Use "*" to capture all MDC attributes or provide a comma-separated list of specific keys. Entries support glob wildcards ("*" and "?"). Use exclude-mdc-attributes to exclude specific keys.
type: list
default: ''
examples:
- custom-mdc-key
- key1,key2,key3
- '*'
- name: otel.instrumentation.logback-appender.experimental.exclude-mdc-attributes
declarative_name: java.logback_appender.exclude_mdc_attributes/development
description: |
Comma-separated list of MDC attribute keys to exclude from capture, supporting glob wildcards ("*" and "?"). Excludes only take effect alongside a non-empty capture-mdc-attributes list (e.g. capture-mdc-attributes="*" with exclude-mdc-attributes="secret*").
type: list
default: ''
examples:
- secret*
- key1,key2
- name: logback-mdc-1.0
display_name: Logback
description: |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.api.internal;

import static java.util.Collections.emptyList;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import javax.annotation.Nullable;

/**
* A predicate that evaluates whether a string matches configurable include and exclude glob
* patterns.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class IncludeExcludePredicate implements Predicate<String> {

private final List<Predicate<String>> included;
private final List<Predicate<String>> excluded;

/**
* Creates a case-sensitive pattern matching include/exclude predicate. Excludes take precedence
* over includes. {@code *} matches zero or more characters and {@code ?} matches one character.
*/
public static Predicate<String> createPatternMatching(
@Nullable Collection<String> included, @Nullable Collection<String> excluded) {
return new IncludeExcludePredicate(included, excluded);
}

private IncludeExcludePredicate(
@Nullable Collection<String> included, @Nullable Collection<String> excluded) {
this.included = createGlobPredicates(included);
this.excluded = createGlobPredicates(excluded);
}

@Override
public boolean test(String value) {
return (included.isEmpty() || matchesAny(included, value)) && !matchesAny(excluded, value);
}

private static List<Predicate<String>> createGlobPredicates(
@Nullable Collection<String> patterns) {
if (patterns == null || patterns.isEmpty()) {
return emptyList();
}

List<Predicate<String>> predicates = new ArrayList<>(patterns.size());
for (String pattern : patterns) {
predicates.add(createGlobPredicate(pattern));
}
return predicates;
}

private static Predicate<String> createGlobPredicate(String globPattern) {
if (globPattern.equals("*")) {
return value -> true;
}
if (globPattern.indexOf('*') == -1 && globPattern.indexOf('?') == -1) {
return globPattern::equals;
}

Pattern pattern = toRegexPattern(globPattern);
return value -> pattern.matcher(value).matches();
}

private static Pattern toRegexPattern(String globPattern) {
StringBuilder regex = new StringBuilder();
int tokenStart = 0;
for (int i = 0; i < globPattern.length(); i++) {
char character = globPattern.charAt(i);
if (character != '*' && character != '?') {
continue;
}

if (tokenStart < i) {
regex.append(Pattern.quote(globPattern.substring(tokenStart, i)));
}
regex.append(character == '*' ? ".*" : ".");
tokenStart = i + 1;
}
if (tokenStart < globPattern.length()) {
regex.append(Pattern.quote(globPattern.substring(tokenStart)));
}
return Pattern.compile(regex.toString());
}

private static boolean matchesAny(List<Predicate<String>> predicates, String value) {
for (Predicate<String> predicate : predicates) {
if (predicate.test(value)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.api.internal;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.argumentSet;

import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

class IncludeExcludePredicateTest {

@ParameterizedTest
@MethodSource("patterns")
void patternMatching(
Collection<String> included, Collection<String> excluded, String value, boolean expected) {
Predicate<String> predicate = IncludeExcludePredicate.createPatternMatching(included, excluded);

assertThat(predicate.test(value)).isEqualTo(expected);
}

private static Stream<Arguments> patterns() {
return Stream.of(
argumentSet("empty patterns include all", emptyList(), emptyList(), "foo", true),
argumentSet("exact include matches", singletonList("foo"), emptyList(), "foo", true),
argumentSet("exact include rejects", singletonList("foo"), emptyList(), "bar", false),
argumentSet(
"star matches zero characters", singletonList("foo*"), emptyList(), "foo", true),
argumentSet(
"star matches many characters", singletonList("foo*"), emptyList(), "foobar", true),
argumentSet(
"question matches one character", singletonList("f?o"), emptyList(), "foo", true),
argumentSet(
"question rejects zero characters", singletonList("f?o"), emptyList(), "fo", false),
argumentSet(
"regex characters are literal",
singletonList("f()[]$^.{}|*"),
emptyList(),
"f()[]$^.{}|oo",
true),
argumentSet(
"matching exclude wins", singletonList("*"), singletonList("foo*"), "foobar", false),
argumentSet(
"nonmatching exclude allows", singletonList("*"), singletonList("foo*"), "bar", true),
argumentSet("any include can match", asList("foo", "bar"), emptyList(), "bar", true));
}
}
9 changes: 5 additions & 4 deletions instrumentation/jboss-logmanager/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Settings for the JBoss Log Manager instrumentation

| System property | Type | Default | Description |
| --------------------------------------------------------------------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `otel.instrumentation.jboss-logmanager.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. |
| `otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. |
| System property | Type | Default | Description |
| --------------------------------------------------------------------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `otel.instrumentation.jboss-logmanager.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. |
| `otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes; glob wildcards (`*` and `?`) are supported. Use `exclude-mdc-attributes` to exclude keys. |
| `otel.instrumentation.jboss-logmanager.experimental.exclude-mdc-attributes` | String | | Comma separated list of MDC attributes to exclude from capture (glob wildcards `*` and `?` supported). Only takes effect alongside a non-empty `capture-mdc-attributes` list. |

The `otel.event.name` key is supported in MDC entries. When present, its value is used as the log event name and is not emitted as an attribute.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ tasks {
withType<Test>().configureEach {
// TODO run tests both with and without experimental log attributes
jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes=*")
jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental.exclude-mdc-attributes=excludedKey")
jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental-log-attributes=true")
jvmArgs("-Dotel.instrumentation.java-util-logging.experimental-log-attributes=true")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.incubator.config.internal.DeclarativeConfigUtil;
import io.opentelemetry.instrumentation.api.internal.IncludeExcludePredicate;
import io.opentelemetry.instrumentation.api.internal.cache.Cache;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import org.jboss.logmanager.ExtLogRecord;
import org.jboss.logmanager.Level;
import org.jboss.logmanager.Logger;
Expand Down Expand Up @@ -54,29 +56,23 @@ public class LoggingEventMapper {
captureArguments = config.getBoolean("capture_arguments/development", false);
}

private final List<AttributeKey<String>> captureMdcAttributeKeys;

// cached as an optimization
private final boolean captureAllMdcAttributes;
@Nullable private final Predicate<String> mdcAttributeFilter;

private LoggingEventMapper() {
List<String> captureMdcAttributes =
DeclarativeConfigUtil.getInstrumentationConfig(
GlobalOpenTelemetry.get(), "jboss_logmanager")
.getScalarList("capture_mdc_attributes/development", String.class, emptyList());
this.captureAllMdcAttributes =
captureMdcAttributes.size() == 1 && captureMdcAttributes.get(0).equals("*");
if (captureAllMdcAttributes) {
this.captureMdcAttributeKeys = emptyList();
} else {
List<AttributeKey<String>> keys = new ArrayList<>(captureMdcAttributes.size());
for (String key : captureMdcAttributes) {
if (!OTEL_EVENT_NAME.getKey().equals(key)) {
keys.add(getMdcAttributeKey(key));
}
}
this.captureMdcAttributeKeys = keys;
}
List<String> excludeMdcAttributes =
DeclarativeConfigUtil.getInstrumentationConfig(
GlobalOpenTelemetry.get(), "jboss_logmanager")
.getScalarList("exclude_mdc_attributes/development", String.class, emptyList());
// an empty include list captures nothing; excludes only take effect alongside includes
this.mdcAttributeFilter =
captureMdcAttributes.isEmpty()
? null
: IncludeExcludePredicate.createPatternMatching(
captureMdcAttributes, excludeMdcAttributes);
Comment thread
philsttr marked this conversation as resolved.
}

public void capture(Logger logger, ExtLogRecord record) {
Expand Down Expand Up @@ -144,19 +140,15 @@ private void captureMdcAttributes(LogRecordBuilder builder) {
builder.setEventName(otelEventName);
}

if (captureAllMdcAttributes) {
for (Map.Entry<String, String> entry : context.entrySet()) {
String key = entry.getKey();
if (!OTEL_EVENT_NAME.getKey().equals(key)) {
builder.setAttribute(getMdcAttributeKey(key), entry.getValue());
}
}
if (mdcAttributeFilter == null) {
return;
}

for (AttributeKey<String> attributeKey : captureMdcAttributeKeys) {
String value = context.get(attributeKey.getKey());
builder.setAttribute(attributeKey, value);
for (Map.Entry<String, String> entry : context.entrySet()) {
String key = entry.getKey();
if (!OTEL_EVENT_NAME.getKey().equals(key) && mdcAttributeFilter.test(key)) {
builder.setAttribute(getMdcAttributeKey(key), entry.getValue());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,16 @@ private static void performLogging(
void testMdc() {
MDC.put("key1", "val1");
MDC.put("key2", "val2");
// excludedKey is filtered out by the exclude-mdc-attributes config (see build.gradle.kts);
// the exact attribute assertions below verify it is not captured
MDC.put("excludedKey", "excludedValue");
MDC.put("otel.event.name", "MyEventName");
try {
logger.info("xyz");
} finally {
MDC.remove("key1");
MDC.remove("key2");
MDC.remove("excludedKey");
MDC.remove("otel.event.name");
}

Expand Down
Loading
Loading