Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NR-23881 - Give users possibility to add MDC to logging #866

Merged
merged 2 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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