-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Nr 59596 jboss logging
- Loading branch information
Showing
13 changed files
with
362 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# JBoss Logging | ||
|
||
JBoss Logging is a "logging bridge" that integrates with various logging frameworks. | ||
|
||
JBoss Logging bridges to the following backends: | ||
- JBoss LogManager (usually only used with WildFly app server) | ||
- Log4j 2 | ||
- Log4j 1 | ||
- Slf4j | ||
- JDK logging (aka JUL) | ||
|
||
This means that when using the `org.jboss.logging:jboss-logging` API, this instrumentation will only apply if the | ||
app is also configured to use the JBoss Logging subsystem (default in JBoss/Wildfly servers) which utilizes the JBoss LogManager. | ||
If the backend is any other logging framework (e.g. log4j) then the instrumentation for that framework will apply instead. | ||
|
||
This instrumentation weaves `org.jboss.logmanager.Logger` (which extends `java.util.logging.Logger`) | ||
to generate logging metrics and forward log events. However, this instrumentation does not do local log | ||
decorating as that functionality is already handled by the JUL instrumentation module. | ||
|
||
## Testing | ||
|
||
Because this instrumentation requires a fully configured JBoss logging subsystem it is not feasible to test it in instrumentation tests. | ||
Instead, the tests can be found in the logging.py AIT which stands up a Wildfly server. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
dependencies { | ||
implementation(project(":agent-bridge")) | ||
implementation 'org.jboss.logmanager:jboss-logmanager:2.1.19.Final' | ||
} | ||
|
||
jar { | ||
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.jboss.logging' } | ||
} | ||
|
||
verifyInstrumentation { | ||
passesOnly('org.jboss.logmanager:jboss-logmanager:[1.3.0.Final,)') | ||
} | ||
|
||
site { | ||
title 'JBoss Logging' | ||
type 'Other' | ||
versionOverride '[1.3.0,)' | ||
} | ||
|
||
compileJava { | ||
options.fork = true | ||
options.bootstrapClasspath = null | ||
} |
124 changes: 124 additions & 0 deletions
124
instrumentation/jboss-logging/src/main/java/com/nr/instrumentation/jboss/AgentUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
* | ||
* * Copyright 2023 New Relic Corporation. All rights reserved. | ||
* * SPDX-License-Identifier: Apache-2.0 | ||
* | ||
*/ | ||
|
||
package com.nr.instrumentation.jboss; | ||
|
||
import com.newrelic.agent.bridge.AgentBridge; | ||
import com.newrelic.agent.bridge.logging.AppLoggingUtils; | ||
import com.newrelic.agent.bridge.logging.LogAttributeKey; | ||
import com.newrelic.agent.bridge.logging.LogAttributeType; | ||
import org.jboss.logmanager.ExtLogRecord; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.logging.Level; | ||
|
||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.DEFAULT_NUM_OF_LOG_EVENT_ATTRIBUTES; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.ERROR_CLASS; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.ERROR_MESSAGE; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.ERROR_STACK; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.INSTRUMENTATION; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.LEVEL; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.LOGGER_FQCN; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.LOGGER_NAME; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.MESSAGE; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.THREAD_ID; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.THREAD_NAME; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.TIMESTAMP; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.UNKNOWN; | ||
|
||
public class AgentUtil { | ||
/** | ||
* Record a LogEvent to be sent to New Relic. | ||
* | ||
* @param record to parse | ||
*/ | ||
public static void recordNewRelicLogEvent(ExtLogRecord record) { | ||
if (record != null) { | ||
String message = record.getMessage(); | ||
Throwable throwable = record.getThrown(); | ||
|
||
if (shouldCreateLogEvent(message, throwable)) { | ||
Map<String, String> mdcCopy = record.getMdcCopy(); | ||
Map<LogAttributeKey, Object> logEventMap = new HashMap<>(calculateInitialMapSize(mdcCopy)); | ||
logEventMap.put(INSTRUMENTATION, "jboss.logging"); | ||
logEventMap.put(MESSAGE, message); | ||
logEventMap.put(TIMESTAMP, record.getMillis()); | ||
|
||
if (AppLoggingUtils.isAppLoggingContextDataEnabled() && mdcCopy != null) { | ||
for (Map.Entry<String, String> entry : mdcCopy.entrySet()) { | ||
String key = entry.getKey(); | ||
String value = entry.getValue(); | ||
LogAttributeKey logAttrKey = new LogAttributeKey(key, LogAttributeType.CONTEXT); | ||
logEventMap.put(logAttrKey, value); | ||
} | ||
} | ||
|
||
Level level = record.getLevel(); | ||
if (level != null) { | ||
String levelName = level.getName(); | ||
if (levelName.isEmpty()) { | ||
logEventMap.put(LEVEL, UNKNOWN); | ||
} else { | ||
logEventMap.put(LEVEL, levelName); | ||
} | ||
} | ||
|
||
String errorStack = ExceptionUtil.getErrorStack(throwable); | ||
if (errorStack != null) { | ||
logEventMap.put(ERROR_STACK, errorStack); | ||
} | ||
|
||
String errorMessage = ExceptionUtil.getErrorMessage(throwable); | ||
if (errorMessage != null) { | ||
logEventMap.put(ERROR_MESSAGE, errorMessage); | ||
} | ||
|
||
String errorClass = ExceptionUtil.getErrorClass(throwable); | ||
if (errorClass != null) { | ||
logEventMap.put(ERROR_CLASS, errorClass); | ||
} | ||
|
||
String threadName = Thread.currentThread().getName(); | ||
if (threadName != null) { | ||
logEventMap.put(THREAD_NAME, threadName); | ||
} | ||
|
||
logEventMap.put(THREAD_ID, record.getThreadID()); | ||
|
||
String loggerName = record.getLoggerName(); | ||
if (loggerName != null) { | ||
logEventMap.put(LOGGER_NAME, loggerName); | ||
} | ||
|
||
String loggerFqcn = record.getSourceClassName(); | ||
if (loggerFqcn != null) { | ||
logEventMap.put(LOGGER_FQCN, loggerFqcn); | ||
} | ||
|
||
AgentBridge.getAgent().getLogSender().recordLogEvent(logEventMap); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* A LogEvent MUST NOT be reported if neither a log message nor an error is logged. If either is present report the LogEvent. | ||
* | ||
* @param message Message to validate | ||
* @param throwable Throwable to validate | ||
* @return true if a LogEvent should be created, otherwise false | ||
*/ | ||
private static boolean shouldCreateLogEvent(String message, Throwable throwable) { | ||
return (message != null) || !ExceptionUtil.isThrowableNull(throwable); | ||
} | ||
|
||
private static int calculateInitialMapSize(Map<String, String> mdcPropertyMap) { | ||
return AppLoggingUtils.isAppLoggingContextDataEnabled() && mdcPropertyMap != null | ||
? mdcPropertyMap.size() + DEFAULT_NUM_OF_LOG_EVENT_ATTRIBUTES | ||
: DEFAULT_NUM_OF_LOG_EVENT_ATTRIBUTES; | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
instrumentation/jboss-logging/src/main/java/com/nr/instrumentation/jboss/ExceptionUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* | ||
* * Copyright 2023 New Relic Corporation. All rights reserved. | ||
* * SPDX-License-Identifier: Apache-2.0 | ||
* | ||
*/ | ||
|
||
package com.nr.instrumentation.jboss; | ||
|
||
public class ExceptionUtil { | ||
public static final int MAX_STACK_SIZE = 300; | ||
|
||
public static boolean isThrowableNull(Throwable throwable) { | ||
return throwable == null; | ||
} | ||
|
||
public static String getErrorStack(Throwable throwable) { | ||
if (isThrowableNull(throwable)) { | ||
return null; | ||
} | ||
|
||
StackTraceElement[] stack = throwable.getStackTrace(); | ||
return getErrorStack(stack); | ||
} | ||
|
||
public static String getErrorStack(StackTraceElement[] stack) { | ||
return getErrorStack(stack, MAX_STACK_SIZE); | ||
} | ||
|
||
public static String getErrorStack(StackTraceElement[] stack, Integer maxStackSize) { | ||
if (stack == null || stack.length == 0) { | ||
return null; | ||
} | ||
|
||
StringBuilder stackBuilder = new StringBuilder(); | ||
int stackSizeLimit = Math.min(maxStackSize, stack.length); | ||
for (int i = 0; i < stackSizeLimit; i++) { | ||
stackBuilder.append(" at ").append(stack[i].toString()).append("\n"); | ||
} | ||
return stackBuilder.toString(); | ||
} | ||
|
||
public static String getErrorMessage(Throwable throwable) { | ||
if (isThrowableNull(throwable)) { | ||
return null; | ||
} | ||
return throwable.getMessage(); | ||
} | ||
|
||
public static String getErrorClass(Throwable throwable) { | ||
if (isThrowableNull(throwable)) { | ||
return null; | ||
} | ||
return throwable.getClass().getName(); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
instrumentation/jboss-logging/src/main/java/org/jboss/logmanager/Logger_Instrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* | ||
* * Copyright 2023 New Relic Corporation. All rights reserved. | ||
* * SPDX-License-Identifier: Apache-2.0 | ||
* | ||
*/ | ||
|
||
package org.jboss.logmanager; | ||
|
||
import com.newrelic.api.agent.NewRelic; | ||
import com.newrelic.api.agent.weaver.NewField; | ||
import com.newrelic.api.agent.weaver.Weave; | ||
import com.newrelic.api.agent.weaver.WeaveAllConstructors; | ||
import com.newrelic.api.agent.weaver.Weaver; | ||
|
||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.isApplicationLoggingEnabled; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.isApplicationLoggingForwardingEnabled; | ||
import static com.newrelic.agent.bridge.logging.AppLoggingUtils.isApplicationLoggingMetricsEnabled; | ||
import static com.nr.instrumentation.jboss.AgentUtil.recordNewRelicLogEvent; | ||
|
||
@Weave(originalName = "org.jboss.logmanager.Logger") | ||
public class Logger_Instrumentation { | ||
|
||
@NewField | ||
public static AtomicBoolean instrumented = new AtomicBoolean(false); | ||
|
||
@WeaveAllConstructors | ||
Logger_Instrumentation() { | ||
// Generate the instrumentation module supportability metric only once | ||
if (!instrumented.getAndSet(true)) { | ||
NewRelic.incrementCounter("Supportability/Logging/Java/JBossLogging/enabled"); | ||
} | ||
} | ||
|
||
public void logRaw(final ExtLogRecord record) { | ||
Weaver.callOriginal(); | ||
|
||
if (isApplicationLoggingEnabled()) { | ||
if (isApplicationLoggingMetricsEnabled()) { | ||
// Generate log level metrics | ||
NewRelic.incrementCounter("Logging/lines"); | ||
NewRelic.incrementCounter("Logging/lines/" + record.getLevel().toString()); | ||
} | ||
if (isApplicationLoggingForwardingEnabled()) { | ||
// Record and send LogEvent to New Relic | ||
recordNewRelicLogEvent(record); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.