Dynatrace OneAgent in Java

Introduction to Dynatrace OneAgent

Dynatrace OneAgent provides automatic instrumentation for Java applications, delivering full-stack observability with minimal configuration. It captures metrics, traces, and logs across the entire application stack.

Installation and Setup

Maven Dependencies

<properties>
<dynatrace.version>8.277.1.1005</dynatrace.version>
</properties>
<dependencies>
<!-- Dynatrace OneAgent API -->
<dependency>
<groupId>com.dynatrace.oneagent</groupId>
<artifactId>oneagent-sdk</artifactId>
<version>${dynatrace.version}</version>
</dependency>
<!-- Dynatrace OpenTelemetry Bridge -->
<dependency>
<groupId>com.dynatrace.oneagent</groupId>
<artifactId>oneagent-opentelemetry-bridge</artifactId>
<version>${dynatrace.version}</version>
</dependency>
</dependencies>

Docker Deployment

FROM openjdk:17-jdk-slim
# Install Dynatrace OneAgent
ARG DT_ENVIRONMENT_ID
ARG DT_API_TOKEN
ARG DT_ONEAGENT_OPTIONS
# Download and install OneAgent
RUN curl -s -H "Authorization: Api-Token ${DT_API_TOKEN}" \
"https://${DT_ENVIRONMENT_ID}.live.dynatrace.com/api/v1/deployment/installer/agent/oneagent/unix/latest?arch=x86&flavor=default" \
--output /tmp/oneagent.tar.gz && \
tar -xzf /tmp/oneagent.tar.gz -C /opt && \
rm /tmp/oneagent.tar.gz
# Set OneAgent environment variables
ENV LD_PRELOAD /opt/dynatrace/oneagent/agent/lib64/liboneagentproc.so
ENV DT_ONEAGENT_OPTIONS="${DT_ONEAGENT_OPTIONS}"
COPY target/my-app.jar /app/my-app.jar
ENTRYPOINT ["java", "-jar", "/app/my-app.jar"]

Configuration

application.yml Configuration

dynatrace:
oneagent:
enabled: true
environment-id: ${DT_ENVIRONMENT_ID}
api-token: ${DT_API_TOKEN}
# OneAgent configuration options
options: >-
--set-app-log-content-access=true
--set-http-server-request-headers=X-Request-ID,X-Trace-ID
--set-http-server-response-headers=X-Request-ID
--set-log-level=INFO
# Custom service properties
service:
name: order-service
version: 1.0.0
technology: java
# Custom request attributes
request-attributes:
enabled: true
include-headers:
- "X-User-ID"
- "X-Session-ID"
- "X-Tenant-ID"

Java System Properties

@Configuration
public class DynatraceConfig {
@PostConstruct
public void configureDynatrace() {
// Set Dynatrace system properties
System.setProperty("dt_connection_environmentid", 
System.getenv().getOrDefault("DT_ENVIRONMENT_ID", "your-environment-id"));
System.setProperty("dt_connection_authtoken", 
System.getenv().getOrDefault("DT_API_TOKEN", "your-api-token"));
// Enable OneAgent features
System.setProperty("dt_feature_oneagent_custom_services", "true");
System.setProperty("dt_feature_oneagent_custom_request_attributes", "true");
System.setProperty("dt_capture_correlation_ids", "true");
// Logging configuration
System.setProperty("dt_logging_level", "INFO");
System.setProperty("dt_capture_java_method_bci", "true");
}
}

Custom Service Instrumentation

OneAgent SDK Integration

@Component
public class DynatraceOneAgentService {
private final OneAgentSDK oneAgentSDK;
private static final Logger logger = LoggerFactory.getLogger(DynatraceOneAgentService.class);
public DynatraceOneAgentService() {
this.oneAgentSDK = OneAgentSDKFactory.createInstance();
// Verify SDK state
SDKStateCallback sdkStateCallback = new SDKStateCallback() {
@Override
public void onSDKStateChanged(SDKState sdkState) {
logger.info("Dynatrace OneAgent SDK state changed: {}", sdkState);
}
};
oneAgentSDK.addSDKStateCallback(sdkStateCallback);
if (oneAgentSDK.getCurrentState() == SDKState.ACTIVE) {
logger.info("Dynatrace OneAgent SDK initialized successfully");
} else {
logger.warn("Dynatrace OneAgent SDK not active. Current state: {}", 
oneAgentSDK.getCurrentState());
}
}
public OneAgentSDK getOneAgentSDK() {
return oneAgentSDK;
}
}

Custom Service Instrumentation

@Service
public class OrderProcessingService {
private final OneAgentSDK oneAgentSDK;
private final Tracer tracer;
private static final Logger logger = LoggerFactory.getLogger(OrderProcessingService.class);
public OrderProcessingService(DynatraceOneAgentService dynatraceService) {
this.oneAgentSDK = dynatraceService.getOneAgentSDK();
this.tracer = oneAgentSDK.traceCustomService("OrderProcessingService", "OrderProcessing");
}
@DynatraceTraced(serviceName = "OrderProcessing", methodName = "processOrder")
public Order processOrder(OrderRequest request) {
// This method will be automatically traced by Dynatrace
return executeOrderProcessing(request);
}
public Order processOrderWithCustomTrace(OrderRequest request) {
InProcessLink inProcessLink = oneAgentSDK.createInProcessLink();
String traceId = oneAgentSDK.getDynatraceStringRepresentation();
try {
// Start custom service
Tracer.ServerlessCallback serverlessCallback = tracer.enterServerless("processOrder", null);
// Add custom request attributes
oneAgentSDK.addCustomRequestAttribute("order.amount", request.getAmount());
oneAgentSDK.addCustomRequestAttribute("order.currency", request.getCurrency());
oneAgentSDK.addCustomRequestAttribute("order.customer", request.getCustomerId());
// Set trace context
MDC.put("dt_trace_id", traceId);
MDC.put("dt_span_id", serverlessCallback.getTraceContext().getSpanId());
logger.info("Processing order with Dynatrace trace: {}", traceId);
Order order = executeOrderProcessing(request);
// Add result as custom attribute
oneAgentSDK.addCustomRequestAttribute("order.result", "success");
oneAgentSDK.addCustomRequestAttribute("order.id", order.getId());
return order;
} catch (Exception e) {
// Report error to Dynatrace
oneAgentSDK.addCustomRequestAttribute("order.result", "error");
oneAgentSDK.addCustomRequestAttribute("order.error", e.getMessage());
oneAgentSDK.reportError(e);
throw e;
} finally {
tracer.leave();
MDC.remove("dt_trace_id");
MDC.remove("dt_span_id");
}
}
public CompletableFuture<Order> processOrderAsync(OrderRequest request) {
// Capture context for async operations
InProcessLink inProcessLink = oneAgentSDK.createInProcessLink();
return CompletableFuture.supplyAsync(() -> {
try {
inProcessLink.link();
return processOrderWithCustomTrace(request);
} finally {
inProcessLink.unlink();
}
});
}
private Order executeOrderProcessing(OrderRequest request) {
// Simulate processing time
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Order processing interrupted", e);
}
return new Order(UUID.randomUUID().toString(), request.getCustomerId(), request.getAmount());
}
}
// Custom annotation for Dynatrace tracing
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DynatraceTraced {
String serviceName();
String methodName() default "";
boolean captureParameters() default false;
}

Database Instrumentation

Custom Database Monitoring

@Repository
public class OrderRepository {
private final OneAgentSDK oneAgentSDK;
private final JdbcTemplate jdbcTemplate;
private static final Logger logger = LoggerFactory.getLogger(OrderRepository.class);
public OrderRepository(DynatraceOneAgentService dynatraceService, JdbcTemplate jdbcTemplate) {
this.oneAgentSDK = dynatraceService.getOneAgentSDK();
this.jdbcTemplate = jdbcTemplate;
}
public Order findById(String orderId) {
DatabaseRequestTracer tracer = oneAgentSDK.traceSQLDatabaseRequest(
"OrderRepository", 
"findById", 
"orders", 
"SELECT * FROM orders WHERE id = ?"
);
try {
tracer.start();
tracer.setParameterData("orderId", orderId);
// Execute query
List<Order> orders = jdbcTemplate.query(
"SELECT * FROM orders WHERE id = ?",
new Object[]{orderId},
new OrderRowMapper()
);
tracer.setRowsReturned(orders.size());
if (orders.isEmpty()) {
tracer.error("Order not found: " + orderId);
return null;
}
return orders.get(0);
} catch (Exception e) {
tracer.error(e);
logger.error("Failed to find order: {}", orderId, e);
throw e;
} finally {
tracer.end();
}
}
public void saveOrder(Order order) {
DatabaseRequestTracer tracer = oneAgentSDK.traceSQLDatabaseRequest(
"OrderRepository", 
"saveOrder", 
"orders", 
"INSERT INTO orders (id, customer_id, amount, status) VALUES (?, ?, ?, ?)"
);
try {
tracer.start();
tracer.setParameterData("orderId", order.getId());
tracer.setParameterData("customerId", order.getCustomerId());
tracer.setParameterData("amount", order.getAmount().toString());
int rowsAffected = jdbcTemplate.update(
"INSERT INTO orders (id, customer_id, amount, status) VALUES (?, ?, ?, ?)",
order.getId(),
order.getCustomerId(),
order.getAmount(),
"CREATED"
);
tracer.setRowsAffected(rowsAffected);
} catch (Exception e) {
tracer.error(e);
logger.error("Failed to save order: {}", order.getId(), e);
throw e;
} finally {
tracer.end();
}
}
}
// Custom DataSource wrapper for Dynatrace monitoring
@Component
public class DynatraceDataSource extends AbstractDataSource {
private final DataSource delegate;
private final OneAgentSDK oneAgentSDK;
public DynatraceDataSource(DataSource delegate, DynatraceOneAgentService dynatraceService) {
this.delegate = delegate;
this.oneAgentSDK = dynatraceService.getOneAgentSDK();
}
@Override
public Connection getConnection() throws SQLException {
return new DynatraceConnection(delegate.getConnection(), oneAgentSDK);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return new DynatraceConnection(delegate.getConnection(username, password), oneAgentSDK);
}
private static class DynatraceConnection implements Connection {
private final Connection delegate;
private final OneAgentSDK oneAgentSDK;
public DynatraceConnection(Connection delegate, OneAgentSDK oneAgentSDK) {
this.delegate = delegate;
this.oneAgentSDK = oneAgentSDK;
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return new DynatracePreparedStatement(delegate.prepareStatement(sql), sql, oneAgentSDK);
}
// Implement all other Connection methods by delegating to the underlying connection
@Override
public void close() throws SQLException { delegate.close(); }
@Override
public boolean isClosed() throws SQLException { return delegate.isClosed(); }
// ... implement all other Connection methods
}
private static class DynatracePreparedStatement implements PreparedStatement {
private final PreparedStatement delegate;
private final String sql;
private final OneAgentSDK oneAgentSDK;
private DatabaseRequestTracer tracer;
public DynatracePreparedStatement(PreparedStatement delegate, String sql, OneAgentSDK oneAgentSDK) {
this.delegate = delegate;
this.sql = sql;
this.oneAgentSDK = oneAgentSDK;
}
@Override
public ResultSet executeQuery() throws SQLException {
startTracer();
try {
ResultSet resultSet = delegate.executeQuery();
tracer.setRowsReturned(getRowCount(resultSet));
return resultSet;
} catch (SQLException e) {
tracer.error(e);
throw e;
} finally {
endTracer();
}
}
@Override
public int executeUpdate() throws SQLException {
startTracer();
try {
int rowsAffected = delegate.executeUpdate();
tracer.setRowsAffected(rowsAffected);
return rowsAffected;
} catch (SQLException e) {
tracer.error(e);
throw e;
} finally {
endTracer();
}
}
private void startTracer() {
tracer = oneAgentSDK.traceSQLDatabaseRequest("CustomDataSource", "execute", "database", sql);
tracer.start();
}
private void endTracer() {
if (tracer != null) {
tracer.end();
tracer = null;
}
}
private int getRowCount(ResultSet resultSet) throws SQLException {
int rowCount = 0;
if (resultSet != null) {
resultSet.last();
rowCount = resultSet.getRow();
resultSet.beforeFirst();
}
return rowCount;
}
// Delegate all other methods
@Override
public void close() throws SQLException { delegate.close(); }
// ... implement all other PreparedStatement methods
}
}

HTTP Client Instrumentation

RestTemplate with Dynatrace

@Component
public class DynatraceRestTemplateInterceptor implements ClientHttpRequestInterceptor {
private final OneAgentSDK oneAgentSDK;
private static final Logger logger = LoggerFactory.getLogger(DynatraceRestTemplateInterceptor.class);
public DynatraceRestTemplateInterceptor(DynatraceOneAgentService dynatraceService) {
this.oneAgentSDK = dynatraceService.getOneAgentSDK();
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, 
ClientHttpRequestExecution execution) throws IOException {
String url = request.getURI().toString();
String method = request.getMethod().name();
String host = request.getURI().getHost();
// Create web application tracer
WebApplicationTracer tracer = oneAgentSDK.traceWebApplicationRequest(
"OutboundHTTP", url, method, host
);
try {
tracer.start();
// Add Dynatrace headers for distributed tracing
tracer.injectTracingHeaders((name, value) -> {
request.getHeaders().add(name, value);
});
// Add custom attributes
tracer.setRemoteAddress(host);
tracer.setRemotePort(request.getURI().getPort());
ClientHttpResponse response = execution.execute(request, body);
// Set response information
tracer.setStatusCode(response.getRawStatusCode());
return response;
} catch (IOException e) {
tracer.error(e);
logger.error("HTTP request failed: {} {}", method, url, e);
throw e;
} finally {
tracer.end();
}
}
}
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(DynatraceRestTemplateInterceptor interceptor) {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = 
new ArrayList<>(restTemplate.getInterceptors());
interceptors.add(interceptor);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}

WebClient Instrumentation

@Component
public class DynatraceWebClientFilter {
private final OneAgentSDK oneAgentSDK;
public DynatraceWebClientFilter(DynatraceOneAgentService dynatraceService) {
this.oneAgentSDK = dynatraceService.getOneAgentSDK();
}
public ExchangeFilterFunction dynatraceTracingFilter() {
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
String url = clientRequest.url().toString();
String method = clientRequest.method().name();
String host = clientRequest.url().getHost();
WebApplicationTracer tracer = oneAgentSDK.traceWebApplicationRequest(
"OutboundHTTP", url, method, host
);
try {
tracer.start();
// Inject Dynatrace headers
ClientRequest.Builder requestBuilder = ClientRequest.from(clientRequest);
tracer.injectTracingHeaders((name, value) -> {
requestBuilder.header(name, value);
});
return Mono.just(requestBuilder.build());
} catch (Exception e) {
tracer.error(e);
return Mono.error(e);
} finally {
tracer.end();
}
});
}
}

Custom Business Metrics

Custom Metrics and Events

@Service
public class BusinessMetricsService {
private final OneAgentSDK oneAgentSDK;
private static final Logger logger = LoggerFactory.getLogger(BusinessMetricsService.class);
// Custom metrics
private static final String ORDERS_PROCESSED_METRIC = "orders_processed_total";
private static final String ORDER_VALUE_METRIC = "order_value";
private static final String PROCESSING_TIME_METRIC = "order_processing_time";
public BusinessMetricsService(DynatraceOneAgentService dynatraceService) {
this.oneAgentSDK = dynatraceService.getOneAgentSDK();
}
public void recordOrderProcessed(String customerId, BigDecimal amount, long processingTimeMs) {
try {
// Record counter metric
oneAgentSDK.recordMetric(ORDERS_PROCESSED_METRIC, 1.0);
// Record value metric
oneAgentSDK.recordMetric(ORDER_VALUE_METRIC, amount.doubleValue());
// Record timing metric
oneAgentSDK.recordMetric(PROCESSING_TIME_METRIC, processingTimeMs);
// Add custom request attributes for business analysis
oneAgentSDK.addCustomRequestAttribute("order.customer", customerId);
oneAgentSDK.addCustomRequestAttribute("order.amount", amount.toString());
oneAgentSDK.addCustomRequestAttribute("order.processing_time_ms", processingTimeMs);
} catch (Exception e) {
logger.warn("Failed to record business metrics", e);
}
}
public void recordError(String operation, String errorType, String errorMessage) {
try {
oneAgentSDK.addCustomRequestAttribute("error.operation", operation);
oneAgentSDK.addCustomRequestAttribute("error.type", errorType);
oneAgentSDK.addCustomRequestAttribute("error.message", errorMessage);
} catch (Exception e) {
logger.warn("Failed to record error metrics", e);
}
}
public void recordCustomEvent(String eventType, Map<String, String> eventData) {
try {
CustomEvent event = new CustomEvent(eventType);
eventData.forEach(event::put);
oneAgentSDK.recordEvent(event);
} catch (Exception e) {
logger.warn("Failed to record custom event", e);
}
}
public void recordUserAction(String action, String userId, Map<String, String> context) {
try {
CustomEvent userAction = new CustomEvent("user_action");
userAction.put("action", action);
userAction.put("user_id", userId);
context.forEach(userAction::put);
oneAgentSDK.recordEvent(userAction);
} catch (Exception e) {
logger.warn("Failed to record user action", e);
}
}
}

Spring Boot Integration

Auto-Configuration

@Configuration
@EnableConfigurationProperties(DynatraceProperties.class)
@ConditionalOnProperty(name = "dynatrace.oneagent.enabled", havingValue = "true")
public class DynatraceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DynatraceOneAgentService dynatraceOneAgentService() {
return new DynatraceOneAgentService();
}
@Bean
public DynatraceRestTemplateInterceptor dynatraceRestTemplateInterceptor(
DynatraceOneAgentService dynatraceService) {
return new DynatraceRestTemplateInterceptor(dynatraceService);
}
@Bean
public BusinessMetricsService businessMetricsService(
DynatraceOneAgentService dynatraceService) {
return new BusinessMetricsService(dynatraceService);
}
@Bean
public DynatraceWebClientFilter dynatraceWebClientFilter(
DynatraceOneAgentService dynatraceService) {
return new DynatraceWebClientFilter(dynatraceService);
}
@Bean
public DynatraceAspect dynatraceAspect(DynatraceOneAgentService dynatraceService) {
return new DynatraceAspect(dynatraceService);
}
}
@ConfigurationProperties(prefix = "dynatrace.oneagent")
public class DynatraceProperties {
private boolean enabled = true;
private String environmentId;
private String apiToken;
private String options;
private Service service = new Service();
private RequestAttributes requestAttributes = new RequestAttributes();
public static class Service {
private String name;
private String version;
private String technology = "java";
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public String getTechnology() { return technology; }
public void setTechnology(String technology) { this.technology = technology; }
}
public static class RequestAttributes {
private boolean enabled = true;
private List<String> includeHeaders = new ArrayList<>();
// Getters and setters
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public List<String> getIncludeHeaders() { return includeHeaders; }
public void setIncludeHeaders(List<String> includeHeaders) { this.includeHeaders = includeHeaders; }
}
// Getters and setters for main class
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getEnvironmentId() { return environmentId; }
public void setEnvironmentId(String environmentId) { this.environmentId = environmentId; }
public String getApiToken() { return apiToken; }
public void setApiToken(String apiToken) { this.apiToken = apiToken; }
public String getOptions() { return options; }
public void setOptions(String options) { this.options = options; }
public Service getService() { return service; }
public void setService(Service service) { this.service = service; }
public RequestAttributes getRequestAttributes() { return requestAttributes; }
public void setRequestAttributes(RequestAttributes requestAttributes) { this.requestAttributes = requestAttributes; }
}

Aspect-Oriented Instrumentation

@Aspect
@Component
public class DynatraceAspect {
private final DynatraceOneAgentService dynatraceService;
private static final Logger logger = LoggerFactory.getLogger(DynatraceAspect.class);
public DynatraceAspect(DynatraceOneAgentService dynatraceService) {
this.dynatraceService = dynatraceService;
}
@Around("@annotation(dynatraceTraced)")
public Object traceMethod(ProceedingJoinPoint joinPoint, DynatraceTraced dynatraceTraced) throws Throwable {
String serviceName = dynatraceTraced.serviceName();
String methodName = !dynatraceTraced.methodName().isEmpty() ? 
dynatraceTraced.methodName() : joinPoint.getSignature().getName();
Tracer tracer = dynatraceService.getOneAgentSDK()
.traceCustomService(serviceName, methodName);
long startTime = System.currentTimeMillis();
try {
tracer.enter();
// Capture method parameters if enabled
if (dynatraceTraced.captureParameters()) {
captureMethodParameters(joinPoint);
}
Object result = joinPoint.proceed();
// Record success metrics
recordMethodSuccess(serviceName, methodName, System.currentTimeMillis() - startTime);
return result;
} catch (Exception e) {
// Record error metrics
recordMethodError(serviceName, methodName, e, System.currentTimeMillis() - startTime);
dynatraceService.getOneAgentSDK().reportError(e);
throw e;
} finally {
tracer.leave();
}
}
@Around("execution(* com.myapp.service.*.*(..))")
public Object traceServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Tracer tracer = dynatraceService.getOneAgentSDK()
.traceCustomService(className, methodName);
try {
tracer.enter();
return joinPoint.proceed();
} finally {
tracer.leave();
}
}
private void captureMethodParameters(ProceedingJoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
dynatraceService.getOneAgentSDK().addCustomRequestAttribute(
"param." + i, args[i].toString()
);
}
}
}
private void recordMethodSuccess(String serviceName, String methodName, long duration) {
try {
dynatraceService.getOneAgentSDK().addCustomRequestAttribute(
serviceName + "." + methodName + ".duration", duration
);
dynatraceService.getOneAgentSDK().addCustomRequestAttribute(
serviceName + "." + methodName + ".result", "success"
);
} catch (Exception e) {
logger.warn("Failed to record method success metrics", e);
}
}
private void recordMethodError(String serviceName, String methodName, Exception error, long duration) {
try {
dynatraceService.getOneAgentSDK().addCustomRequestAttribute(
serviceName + "." + methodName + ".duration", duration
);
dynatraceService.getOneAgentSDK().addCustomRequestAttribute(
serviceName + "." + methodName + ".result", "error"
);
dynatraceService.getOneAgentSDK().addCustomRequestAttribute(
serviceName + "." + methodName + ".error", error.getClass().getSimpleName()
);
} catch (Exception e) {
logger.warn("Failed to record method error metrics", e);
}
}
}

Log Integration

MDC Integration with Dynatrace

@Component
public class DynatraceMDCIntegration {
private final DynatraceOneAgentService dynatraceService;
public DynatraceMDCIntegration(DynatraceOneAgentService dynatraceService) {
this.dynatraceService = dynatraceService;
}
public void updateMDCWithDynatraceContext() {
try {
OneAgentSDK oneAgentSDK = dynatraceService.getOneAgentSDK();
// Get Dynatrace trace context
String traceId = oneAgentSDK.getDynatraceStringRepresentation();
// Update MDC with Dynatrace context
MDC.put("dt.trace_id", traceId);
// Add custom request attributes to MDC for logging
Map<String, Object> customAttributes = getCustomAttributes();
customAttributes.forEach((key, value) -> {
if (value != null) {
MDC.put("dt." + key, value.toString());
}
});
} catch (Exception e) {
// Log warning but don't break application
LoggerFactory.getLogger(DynatraceMDCIntegration.class)
.warn("Failed to update MDC with Dynatrace context", e);
}
}
public void clearDynatraceMDC() {
// Remove all Dynatrace-related MDC entries
MDC.getCopyOfContextMap().keySet().stream()
.filter(key -> key.startsWith("dt."))
.forEach(MDC::remove);
}
private Map<String, Object> getCustomAttributes() {
// This would typically retrieve current custom request attributes
// For simplicity, return empty map
return Collections.emptyMap();
}
}

Testing and Validation

Test Configuration

@SpringBootTest
@TestPropertySource(properties = {
"dynatrace.oneagent.enabled=true",
"dynatrace.oneagent.service.name=test-service",
"dynatrace.oneagent.service.version=1.0.0"
})
public class DynatraceIntegrationTest {
@Autowired
private DynatraceOneAgentService dynatraceService;
@Autowired
private BusinessMetricsService businessMetricsService;
@Test
public void testOneAgentSDKInitialization() {
OneAgentSDK oneAgentSDK = dynatraceService.getOneAgentSDK();
assertNotNull(oneAgentSDK);
SDKState state = oneAgentSDK.getCurrentState();
assertTrue(state == SDKState.ACTIVE || state == SDKState.TEMPORARY_INACTIVE);
}
@Test
public void testCustomServiceTracing() {
OneAgentSDK oneAgentSDK = dynatraceService.getOneAgentSDK();
Tracer tracer = oneAgentSDK.traceCustomService("TestService", "testMethod");
try {
tracer.enter();
// Simulate work
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
tracer.leave();
}
}
@Test
public void testBusinessMetricsRecording() {
businessMetricsService.recordOrderProcessed("customer123", 
new BigDecimal("99.99"), 150L);
// Verify metrics were recorded (this would typically be verified in Dynatrace UI)
}
@Test
public void testErrorReporting() {
OneAgentSDK oneAgentSDK = dynatraceService.getOneAgentSDK();
try {
throw new RuntimeException("Test error for Dynatrace");
} catch (RuntimeException e) {
oneAgentSDK.reportError(e);
}
}
}

Kubernetes Deployment

Deployment Configuration

apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
labels:
app: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
annotations:
dynatrace.com/inject: "true"
spec:
containers:
- name: order-service
image: my-registry/order-service:latest
env:
- name: DT_ENVIRONMENT_ID
valueFrom:
secretKeyRef:
name: dynatrace-secrets
key: environmentId
- name: DT_API_TOKEN
valueFrom:
secretKeyRef:
name: dynatrace-secrets
key: apiToken
- name: DT_ONEAGENT_OPTIONS
value: "--set-app-log-content-access=true --set-log-level=INFO"
ports:
- containerPort: 8080
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
---
apiVersion: v1
kind: Secret
metadata:
name: dynatrace-secrets
type: Opaque
data:
environmentId: <base64-encoded-environment-id>
apiToken: <base64-encoded-api-token>

Conclusion

Dynatrace OneAgent provides comprehensive observability for Java applications:

  1. Automatic Instrumentation - Zero-code monitoring for common frameworks
  2. Custom Service Monitoring - Fine-grained control over business logic tracing
  3. Database Monitoring - SQL query performance and error tracking
  4. HTTP Client Monitoring - Outbound request tracing and performance
  5. Business Metrics - Custom metrics and events for business analysis
  6. Distributed Tracing - End-to-end transaction tracking across services
  7. Error Reporting - Automatic error detection and reporting

The integration enables full-stack observability with minimal code changes while providing the flexibility to add custom instrumentation for specific business requirements.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper