Skip to content

Commit

Permalink
Merge pull request #866 from StanlieK/feature/mdc-in-log-forwarding
Browse files Browse the repository at this point in the history
NR-23881 - Give users possibility to add MDC to logging
  • Loading branch information
meiao authored Oct 19, 2022
2 parents 2ab186e + 66bbdec commit 8769ab2
Show file tree
Hide file tree
Showing 43 changed files with 907 additions and 511 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package com.newrelic.agent.bridge;

import com.newrelic.agent.bridge.logging.LogAttributeKey;
import com.newrelic.api.agent.Logs;

import java.util.Map;
Expand All @@ -18,7 +19,7 @@ private NoOpLogs() {
}

@Override
public void recordLogEvent(Map<String, ?> attributes) {
}
public void recordLogEvent(Map<LogAttributeKey, ?> attributes) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,47 @@
* * SPDX-License-Identifier: Apache-2.0
*
*/
package com.newrelic.agent.bridge.logging;

package com.nr.agent.instrumentation.log4j1;

import com.newrelic.agent.bridge.logging.LogAttributeKey;
import com.newrelic.agent.bridge.logging.LogAttributeType;
import com.newrelic.api.agent.NewRelic;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.logging.Level;

public class AgentUtil {
public static final int DEFAULT_NUM_OF_LOG_EVENT_ATTRIBUTES = 3;
public class AppLoggingUtils {
public static final int DEFAULT_NUM_OF_LOG_EVENT_ATTRIBUTES = 10;
// Log message attributes
public static final String MESSAGE = "message";
public static final String TIMESTAMP = "timestamp";
public static final String LEVEL = "level";
public static final LogAttributeKey MESSAGE = new LogAttributeKey("message", LogAttributeType.AGENT);
public static final LogAttributeKey TIMESTAMP = new LogAttributeKey("timestamp", LogAttributeType.AGENT);
public static final LogAttributeKey LEVEL = new LogAttributeKey("level", LogAttributeType.AGENT);
public static final LogAttributeKey ERROR_MESSAGE = new LogAttributeKey("error.message", LogAttributeType.AGENT);
public static final LogAttributeKey ERROR_CLASS = new LogAttributeKey("error.class", LogAttributeType.AGENT);
public static final LogAttributeKey ERROR_STACK = new LogAttributeKey("error.stack", LogAttributeType.AGENT);
public static final LogAttributeKey THREAD_NAME = new LogAttributeKey("thread.name", LogAttributeType.AGENT);
public static final LogAttributeKey THREAD_ID = new LogAttributeKey("thread.id", LogAttributeType.AGENT);
public static final LogAttributeKey LOGGER_NAME = new LogAttributeKey("logger.name", LogAttributeType.AGENT);
public static final LogAttributeKey LOGGER_FQCN = new LogAttributeKey("logger.fqcn", LogAttributeType.AGENT);
public static final String UNKNOWN = "UNKNOWN";
// Linking metadata attributes used in blob
private static final String BLOB_PREFIX = "NR-LINKING";
private static final String BLOB_DELIMITER = "|";
private static final String TRACE_ID = "trace.id";
private static final String HOSTNAME = "hostname";
private static final String ENTITY_GUID = "entity.guid";
private static final String ENTITY_NAME = "entity.name";
private static final String SPAN_ID = "span.id";
public static final String BLOB_PREFIX = "NR-LINKING";
public static final String BLOB_DELIMITER = "|";
public static final String TRACE_ID = "trace.id";
public static final String HOSTNAME = "hostname";
public static final String ENTITY_GUID = "entity.guid";
public static final String ENTITY_NAME = "entity.name";
public static final String SPAN_ID = "span.id";
// Log attribute prefixes
public static final String CONTEXT_DATA_ATTRIBUTE_PREFIX = "context.";
// Enabled defaults
private static final boolean APP_LOGGING_DEFAULT_ENABLED = true;
private static final boolean APP_LOGGING_METRICS_DEFAULT_ENABLED = true;
private static final boolean APP_LOGGING_FORWARDING_DEFAULT_ENABLED = true;
private static final boolean APP_LOGGING_LOCAL_DECORATING_DEFAULT_ENABLED = false;
private static final boolean APP_LOGGING_FORWARDING_INCLUDE_CONTEXT_DATA_DEFAULT_ENABLED = false;

/**
* Gets a String representing the agent linking metadata in blob format:
Expand Down Expand Up @@ -70,13 +80,15 @@ private static void appendAttributeToBlob(String attribute, StringBuilder blob)
* @param value String to encode
* @return URL encoded String
*/
static String urlEncode(String value) {
public static String urlEncode(String value) {
try {
if (value != null) {
value = URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
}
} catch (UnsupportedEncodingException e) {
NewRelic.getAgent().getLogger().log(Level.WARNING, "Unable to URL encode entity.name for application_logging.local_decorating", e);
NewRelic.getAgent()
.getLogger()
.log(java.util.logging.Level.WARNING, "Unable to URL encode entity.name for application_logging.local_decorating", e);
}
return value;
}
Expand Down Expand Up @@ -116,4 +128,14 @@ public static boolean isApplicationLoggingForwardingEnabled() {
public static boolean isApplicationLoggingLocalDecoratingEnabled() {
return NewRelic.getAgent().getConfig().getValue("application_logging.local_decorating.enabled", APP_LOGGING_LOCAL_DECORATING_DEFAULT_ENABLED);
}

/**
* Check if the application_logging forwarding include_context_data feature is enabled.
*
* @return true if enabled, else false
*/
public static boolean isAppLoggingContextDataEnabled() {
return NewRelic.getAgent().getConfig().getValue("application_logging.forwarding.context_data.enabled",
APP_LOGGING_FORWARDING_INCLUDE_CONTEXT_DATA_DEFAULT_ENABLED);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.newrelic.agent.bridge.logging;

import java.util.Objects;

public class LogAttributeKey {
public final String key;
public final LogAttributeType type;

public LogAttributeKey(String key, LogAttributeType type) {
this.key = key;
this.type = type;
}

public String getKey() {
return key;
}

public String getPrefixedKey() {
if (key == null || type == null) {
return key;
}
return type.applyPrefix(key);
}

public LogAttributeType getType() {
return type;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LogAttributeKey that = (LogAttributeKey) o;
return Objects.equals(key, that.key) && type == that.type;
}

@Override
public int hashCode() {
return Objects.hash(key, type);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.newrelic.agent.bridge.logging;

public enum LogAttributeType {
AGENT(null) {
@Override
public String applyPrefix(String key) {
return key;
}
},
CONTEXT(AppLoggingUtils.CONTEXT_DATA_ATTRIBUTE_PREFIX);

private final String prefix;

LogAttributeType(String prefix) {
this.prefix = prefix;
}

public String applyPrefix(String key) {
return prefix.concat(key);
}
}
4 changes: 3 additions & 1 deletion agent-bridge/src/main/java/com/newrelic/api/agent/Logs.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

package com.newrelic.api.agent;

import com.newrelic.agent.bridge.logging.LogAttributeKey;

import java.util.Map;

/**
Expand All @@ -22,5 +24,5 @@ public interface Logs {
* For map values that are not String, Number, or Boolean object types the toString value will be used.
* @since 7.6.0
*/
void recordLogEvent(Map<String, ?> attributes);
void recordLogEvent(Map<LogAttributeKey, ?> attributes);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package com.nr.agent.instrumentation.log4j1;

/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/
package com.newrelic.agent.bridge.logging;

import com.newrelic.agent.bridge.logging.AppLoggingUtils;
import org.junit.Assert;
import org.junit.Test;

public class AgentUtilTest {
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;

public class AppLoggingUtilsTest {

@Test
public void testUrlEncoding() {
Expand All @@ -15,9 +24,8 @@ public void testUrlEncoding() {
final String valueToEncode = "|My Application|";
final String expectedEncodedValue = ENCODED_PIPE + "My" + ENCODED_SPACE + "Application" + ENCODED_PIPE;

String encodedValue = AgentUtil.urlEncode(valueToEncode);
String encodedValue = AppLoggingUtils.urlEncode(valueToEncode);

Assert.assertEquals(expectedEncodedValue, encodedValue);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.newrelic.agent.introspec.internal;

import com.newrelic.agent.Agent;
import com.newrelic.agent.bridge.logging.LogAttributeKey;
import com.newrelic.agent.config.AgentConfig;
import com.newrelic.agent.deps.com.google.common.collect.LinkedListMultimap;
import com.newrelic.agent.deps.com.google.common.collect.ListMultimap;
Expand All @@ -23,6 +24,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;

import static com.newrelic.agent.model.LogEvent.LOG_EVENT_TYPE;

Expand Down Expand Up @@ -75,9 +77,10 @@ public boolean isStoppedOrStopping() {
}

@Override
public void recordLogEvent(Map<String, ?> attributes) {
public void recordLogEvent(Map<LogAttributeKey, ?> attributes) {
if (AnalyticsEvent.isValidType(LOG_EVENT_TYPE)) {
Map<String, Object> atts = Maps.newHashMap(attributes);
Map<String, Object> atts = attributes.entrySet().stream()
.collect(Collectors.toMap(entry -> entry.getKey().getPrefixedKey(), Map.Entry::getValue));
LogEvent event = new LogEvent(atts, DistributedTraceServiceImpl.nextTruncatedFloat());
storeEvent("TestApp", event);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;

import static com.nr.agent.instrumentation.log4j1.AgentUtil.isApplicationLoggingEnabled;
import static com.nr.agent.instrumentation.log4j1.AgentUtil.isApplicationLoggingMetricsEnabled;
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.isApplicationLoggingEnabled;
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.isApplicationLoggingMetricsEnabled;

@Weave(originalName = "org.apache.log4j.Category")
public class Category_Instrumentation {
Expand Down
Loading

0 comments on commit 8769ab2

Please sign in to comment.