Skip to content

Commit

Permalink
Support Websphere JMX admin metrics (#7235)
Browse files Browse the repository at this point in the history
Co-authored-by: Stuart McCulloch <[email protected]>
  • Loading branch information
amarziali and mcculls authored Aug 2, 2024
1 parent 56022b2 commit 0a5a92f
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package datadog.trace.bootstrap.instrumentation.jmx;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.management.MBeanServer;

public class MBeanServerRegistry {
private static final ConcurrentMap<String, MBeanServer> serverMap = new ConcurrentHashMap<>();

public static MBeanServer getServer(final String serverClass) {
return serverMap.get(serverClass);
}

public static void putServer(final String serverClass, final MBeanServer server) {
serverMap.putIfAbsent(serverClass, server);
}
}
3 changes: 2 additions & 1 deletion dd-java-agent/agent-jmxfetch/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ plugins {
apply from: "$rootDir/gradle/java.gradle"

dependencies {
api('com.datadoghq:jmxfetch:0.49.2') {
api('com.datadoghq:jmxfetch:0.49.3') {
exclude group: 'org.slf4j', module: 'slf4j-api'
exclude group: 'org.slf4j', module: 'slf4j-jdk14'
exclude group: 'com.beust', module: 'jcommander'
Expand All @@ -20,6 +20,7 @@ dependencies {
}
api libs.slf4j
api project(':internal-api')
api project(':dd-java-agent:agent-bootstrap')
}

shadowJar {
Expand Down
2 changes: 1 addition & 1 deletion dd-java-agent/agent-jmxfetch/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ch.qos.logback:logback-core:1.2.3=testCompileClasspath,testRuntimeClasspath
com.beust:jcommander:1.78=testRuntimeClasspath
com.datadoghq:dd-javac-plugin-client:0.1.7=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.datadoghq:java-dogstatsd-client:2.10.5=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.datadoghq:jmxfetch:0.49.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.datadoghq:jmxfetch:0.49.3=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.javaparser:javaparser-core:3.25.1=testCompileClasspath,testRuntimeClasspath
com.github.jnr:jffi:1.2.23=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.jnr:jnr-a64asm:1.0.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package datadog.trace.agent.jmxfetch;

import datadog.trace.bootstrap.instrumentation.jmx.MBeanServerRegistry;
import java.io.IOException;
import java.util.Map;
import javax.management.MBeanServer;
import org.datadog.jmxfetch.Connection;
import org.datadog.jmxfetch.ConnectionFactory;
import org.datadog.jmxfetch.DefaultConnectionFactory;
import org.datadog.jmxfetch.JvmDirectConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AgentConnectionFactory implements ConnectionFactory {
private static final Logger log = LoggerFactory.getLogger(AgentConnectionFactory.class);

private final ConnectionFactory defaultConnectionFactory = new DefaultConnectionFactory();

public AgentConnectionFactory() {}

@Override
public Connection createConnection(Map<String, Object> connectionParams) throws IOException {
Object mbeanServerClass = connectionParams.get("mbean_server_class");
if (mbeanServerClass != null) {
MBeanServer mbeanServer = MBeanServerRegistry.getServer(mbeanServerClass.toString());
if (mbeanServer != null) {
return new InitialMBeanServerConnection(mbeanServer);
}
log.warn(
"Unable to provide a MBean server instance of {}. Falling back to platform default",
mbeanServerClass);
return new JvmDirectConnection();
}
return defaultConnectionFactory.createConnection(connectionParams);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package datadog.trace.agent.jmxfetch;

import java.io.IOException;
import javax.annotation.Nonnull;
import javax.management.MBeanServerConnection;
import org.datadog.jmxfetch.JvmDirectConnection;

public class InitialMBeanServerConnection extends JvmDirectConnection {

public InitialMBeanServerConnection(@Nonnull final MBeanServerConnection mbs) throws IOException {
this.mbs = mbs;
}

@Override
protected void createConnection() {
// already connected
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public class JMXFetch {

private static final Logger log = LoggerFactory.getLogger(JMXFetch.class);

public static final List<String> DEFAULT_CONFIGS =
Collections.singletonList("jmxfetch-config.yaml");
private static final String DEFAULT_CONFIG = "jmxfetch-config.yaml";
private static final String WEBSPHERE_CONFIG = "jmxfetch-websphere-config.yaml";

private static final int DELAY_BETWEEN_RUN_ATTEMPTS = 5000;

Expand Down Expand Up @@ -92,6 +92,11 @@ private static void run(final StatsDClientManager statsDClientManager, final Con
final AgentStatsdReporter reporter = new AgentStatsdReporter(statsd);

TracerFlare.addReporter(reporter);
final List<String> defaultConfigs = new ArrayList<>();
defaultConfigs.add(DEFAULT_CONFIG);
if (config.isJmxFetchIntegrationEnabled(Collections.singletonList("websphere"), false)) {
defaultConfigs.add(WEBSPHERE_CONFIG);
}

final AppConfig.AppConfigBuilder configBuilder =
AppConfig.builder()
Expand All @@ -102,13 +107,14 @@ private static void run(final StatsDClientManager statsDClientManager, final Con
.confdDirectory(jmxFetchConfigDir)
.yamlFileList(jmxFetchConfigs)
.targetDirectInstances(true)
.instanceConfigResources(DEFAULT_CONFIGS)
.instanceConfigResources(defaultConfigs)
.metricConfigResources(internalMetricsConfigs)
.metricConfigFiles(metricsConfigs)
.initialRefreshBeansPeriod(initialRefreshBeansPeriod)
.refreshBeansPeriod(refreshBeansPeriod)
.globalTags(globalTags)
.reporter(reporter);
.reporter(reporter)
.connectionFactory(new AgentConnectionFactory());

if (config.isJmxFetchMultipleRuntimeServicesEnabled()) {
ServiceNameCollectingTraceInterceptor serviceNameProvider =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
init_config:
is_jmx: true

instances:
- jvm_direct: true
mbean_server_class: com.ibm.ws.management.PlatformMBeanServer
name: dd-java-agent for websphere
collect_default_jvm_metrics: false
conf:
# The metrics are included for now. When we'll have a dedicate core integration we can
# remove that, but we'll need to keep shipping the connection factory
- include:
domain: WebSphere
type: ThreadPool
attribute:
stats.PoolSize.lowerBound:
alias: websphere.thread_pool.lower_bound
stats.PoolSize.upperBound:
alias: websphere.thread_pool.upper_bound
stats.PoolSize.highWaterMark:
alias: websphere.thread_pool.high_water_mark
stats.PoolSize.lowWaterMark:
alias: websphere.thread_pool.low_water_mark
stats.PoolSize.current:
alias: websphere.thread_pool.current
stats.ActiveCount.lowerBound:
alias: websphere.thread_pool.active.lower_bound
stats.ActiveCount.upperBound:
alias: websphere.thread_pool.active.upper_bound
stats.ActiveCount.highWaterMark:
alias: websphere.thread_pool.active.high_water_mark
stats.ActiveCount.lowWaterMark:
alias: websphere.thread_pool.active.low_water_mark
stats.ActiveCount.current:
alias: websphere.thread_pool.active.current
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package datadog.trace.instrumentation.java.lang.management;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.returns;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;
import datadog.trace.bootstrap.instrumentation.jmx.MBeanServerRegistry;
import javax.management.MBeanServer;
import net.bytebuddy.asm.Advice;

@AutoService(InstrumenterModule.class)
public class CustomMBeanBuilderInstrumentation extends InstrumenterModule.Tracing
implements Instrumenter.ForConfiguredType {

private final String customBuilder;

public CustomMBeanBuilderInstrumentation() {
super("java-lang-management");

customBuilder = System.getProperty("javax.management.builder.initial");
}

@Override
public boolean isEnabled() {
return super.isEnabled() && customBuilder != null && !customBuilder.isEmpty();
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
isMethod().and(named("newMBeanServer")).and(returns(named("javax.management.MBeanServer"))),
this.getClass().getName() + "$StoreMBeanServerAdvice");
}

@Override
public String configuredMatchingType() {
return customBuilder;
}

public static class StoreMBeanServerAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterCreation(@Advice.Return final MBeanServer mbeanServer) {
MBeanServerRegistry.putServer(mbeanServer.getClass().getName(), mbeanServer);
}
}
}
1 change: 1 addition & 0 deletions dd-java-agent/instrumentation/websphere-jmx/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apply from: "$rootDir/gradle/java.gradle"
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package datadog.trace.instrumentation.websphere_jmx;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.returns;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;
import datadog.trace.bootstrap.config.provider.ConfigProvider;
import datadog.trace.util.AgentThreadFactory;
import java.util.Collections;
import net.bytebuddy.asm.Advice;

/**
* Grant JMXFetch access to the WebSphere Admin MBean without changing the server security config.
*/
@AutoService(InstrumenterModule.class)
public class WebsphereSecurityInstrumentation extends InstrumenterModule.Tracing
implements Instrumenter.ForSingleType {

private final String customBuilder;

public WebsphereSecurityInstrumentation() {
super("websphere-jmx");

customBuilder = System.getProperty("javax.management.builder.initial");
}

@Override
public String instrumentedType() {
return "com.ibm.ws.management.util.SecurityHelper";
}

@Override
public boolean isEnabled() {
return super.isEnabled()
&& "com.ibm.ws.management.PlatformMBeanServerBuilder".equals(customBuilder)
// we must avoid loading the global Config while setting up instrumentation, so use the same
// underlying provider call as Config.get().isJmxFetchIntegrationEnabled("websphere", false)
&& ConfigProvider.getInstance()
.isEnabled(Collections.singletonList("websphere"), "jmxfetch.", ".enabled", false);
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
isMethod().and(named("isSecurityEnabled")).and(returns(boolean.class)),
this.getClass().getName() + "$DisableSecurityAdvice");
}

public static class DisableSecurityAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void after(@Advice.Return(readOnly = false) boolean securityEnabled) {
// only grant access when we know the call is coming from one of our agent threads
if (AgentThreadFactory.AGENT_THREAD_GROUP == Thread.currentThread().getThreadGroup()) {
securityEnabled = false;
}
}
}
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ include ':dd-java-agent:instrumentation:vertx-web-3.5'
include ':dd-java-agent:instrumentation:vertx-web-3.9'
include ':dd-java-agent:instrumentation:vertx-web-4.0'
include ':dd-java-agent:instrumentation:redisson-2.0.0'
include ':dd-java-agent:instrumentation:websphere-jmx'
include ':dd-java-agent:instrumentation:zio'
include ':dd-java-agent:instrumentation:zio:zio-2.0'

Expand Down

0 comments on commit 0a5a92f

Please sign in to comment.