google-site-verification: google61fe8ba583a51912.html
Titliel – Datadog APM Integration in Java

Article: Comprehensive Datadog APM Implementation for Java Applications

Datadog Application Performance Monitoring (APM) provides distributed tracing, performance metrics, and error tracking for modern applications. This article covers comprehensive integration strategies for Java applications.

Core Datadog APM Configuration

package com.titliel.apm; import datadog.trace.api.DDTags; import datadog.trace.api.GlobalTracer; import datadog.trace.api.Trace; import datadog.trace.api.interceptor.MutableSpan; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; import io.opentracing.propagation.TextMapAdapter; import io.opentracing.util.GlobalTracer; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Titliel Datadog APM Manager * Advanced APM integration with custom metrics and tracing */ public class TitlielDatadogApm { private final Tracer tracer; private final ApmConfig config; private final Map<String, ServiceMetrics> serviceMetrics; public TitlielDatadogApm() { this.tracer = GlobalTracer.get(); this.config = ApmConfig.getInstance(); this.serviceMetrics = new ConcurrentHashMap<>(); } /** * Start a new trace for business transaction */ public Span startBusinessTrace(String operationName, String serviceName) { Tracer.SpanBuilder spanBuilder = tracer.buildSpan(operationName) .withTag(DDTags.SERVICE_NAME, serviceName) .withTag(DDTags.SPAN_TYPE, "web") .withTag(DDTags.RESOURCE_NAME, operationName); Span span = spanBuilder.start(); // Add custom tags span.setTag("environment", config.getEnvironment()); span.setTag("app.version", config.getAppVersion()); span.setTag("deployment.id", config.getDeploymentId()); return span; } /** * Trace method execution with custom metrics */ @Trace(operationName = "custom.operation", resourceName = "${1:class.method}") public <T> T traceMethod(String methodName, String resourceName, ApmOperation<T> operation) { Span span = tracer.activeSpan(); long startTime = System.currentTimeMillis(); try { if (span != null) { span.setTag("method.name", methodName); span.setTag("resource.name", resourceName); } T result = operation.execute(); // Record success metrics recordSuccess(methodName, System.currentTimeMillis() - startTime); return result; } catch (Exception e) { // Record error metrics recordError(methodName, e, System.currentTimeMillis() - startTime); if (span != null) { span.setTag(DDTags.ERROR, true); span.log(Map.of( "event", "error", "error.object", e.getClass().getName(), "message", e.getMessage(), "stack", getStackTrace(e) )); } throw e; } } /** * Create database query span */ public Span startDBSpan(String query, String dbType, String dbInstance) { Span span = tracer.buildSpan("database.query") .withTag(DDTags.SERVICE_NAME, dbType + "-db") .withTag(DDTags.SPAN_TYPE, "sql") .withTag(DDTags.RESOURCE_NAME, query) .withTag("db.type", dbType) .withTag("db.instance", dbInstance) .withTag("db.query", maskSensitiveQuery(query)) .start(); return span; } /** * Create HTTP client span */ public Span startHttpClientSpan(String method, String url, String service) { Span span = tracer.buildSpan("http.request") .withTag(DDTags.SERVICE_NAME, service) .withTag(DDTags.SPAN_TYPE, "http") .withTag(DDTags.RESOURCE_NAME, method + " " + url) .withTag("http.method", method) .withTag("http.url", url) .withTag("http.service", service) .start(); return span; } /** * Extract context from incoming HTTP request */ public SpanContext extractContext(Map<String, String> headers) { try { return tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(headers)); } catch (Exception e) { // Context extraction failed, start new trace return null; } } /** * Inject context for outgoing HTTP request */ public void injectContext(Span span, Map<String, String> headers) { tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMapAdapter(headers)); } /** * Add custom business metrics to span */ public void addBusinessMetrics(Span span, String metricName, Number value) { if (span instanceof MutableSpan) { MutableSpan mutableSpan = (MutableSpan) span; mutableSpan.setMetric(metricName, value.doubleValue()); } // Also record in service metrics serviceMetrics.computeIfAbsent(metricName, k -> new ServiceMetrics()) .recordValue(value.doubleValue()); } /** * Record custom event for business transaction */ public void recordBusinessEvent(String eventType, Map<String, Object> eventData) { Span span = tracer.activeSpan(); if (span != null) { span.log(Map.of( "event.type", eventType, "event.timestamp", System.currentTimeMillis(), "event.data", eventData )); } // Also send as custom metric addBusinessMetrics(span, "business.event." + eventType, 1); } private void recordSuccess(String methodName, long duration) { serviceMetrics.computeIfAbsent(methodName, k -> new ServiceMetrics()) .recordSuccess(duration); } private void recordError(String methodName, Exception error, long duration) { serviceMetrics.computeIfAbsent(methodName, k -> new ServiceMetrics()) .recordError(error.getClass().getSimpleName(), duration); } private String maskSensitiveQuery(String query) { // Mask sensitive data in SQL queries return query.replaceAll("(password|pwd|token|secret)\\s*=\\s*'[^']*'", "$1 = '***'"); } private String getStackTrace(Exception e) { StringBuilder sb = new StringBuilder(); for (StackTraceElement element : e.getStackTrace()) { sb.append(element.toString()).append("\n"); } return sb.toString(); } /** * Get performance metrics summary */ public Map<String, ServiceMetrics> getServiceMetrics() { return new HashMap<>(serviceMetrics); } } /** * APM operation interface */ @FunctionalInterface public interface ApmOperation<T> { T execute() throws Exception; } /** * Service metrics tracking */ public class ServiceMetrics { private long requestCount = 0; private long errorCount = 0; private long totalDuration = 0; private long maxDuration = 0; private long minDuration = Long.MAX_VALUE; private final Map<String, Long> errorTypes = new ConcurrentHashMap<>(); private final List<Double> recentDurations = new ArrayList<>(); private static final int MAX_RECENT_DURATIONS = 100; public void recordSuccess(long duration) { requestCount++; totalDuration += duration; maxDuration = Math.max(maxDuration, duration); minDuration = Math.min(minDuration, duration); // Maintain recent durations for percentile calculation synchronized (recentDurations) { recentDurations.add((double) duration); if (recentDurations.size() > MAX_RECENT_DURATIONS) { recentDurations.remove(0); } } } public void recordError(String errorType, long duration) { errorCount++; requestCount++; totalDuration += duration; errorTypes.merge(errorType, 1L, Long::sum); } public void recordValue(double value) { // For custom metrics synchronized (recentDurations) { recentDurations.add(value); if (recentDurations.size() > MAX_RECENT_DURATIONS) { recentDurations.remove(0); } } } // Getters public long getRequestCount() { return requestCount; } public long getErrorCount() { return errorCount; } public double getErrorRate() { return requestCount > 0 ? (double) errorCount / requestCount : 0.0; } public double getAverageDuration() { return requestCount > 0 ? (double) totalDuration / requestCount : 0.0; } public long getMaxDuration() { return maxDuration; } public long getMinDuration() { return minDuration == Long.MAX_VALUE ? 0 : minDuration; } public double getPercentile(double percentile) { synchronized (recentDurations) { if (recentDurations.isEmpty()) return 0.0; List<Double> sorted = new ArrayList<>(recentDurations); Collections.sort(sorted); int index = (int) Math.ceil(percentile * sorted.size()) - 1; return sorted.get(Math.max(0, index)); } } }

Spring Boot Integration

package com.titliel.apm.spring; import com.titliel.apm.TitlielDatadogApm; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; /** * Spring Boot integration for Datadog APM */ @Component public class ApmSpringIntegration implements HandlerInterceptor, ApplicationListener<ApplicationReadyEvent> { @Autowired private TitlielDatadogApm apm; private static final ThreadLocal<Long> requestStartTime = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { requestStartTime.set(System.currentTimeMillis()); // Extract tracing context from incoming request Map<String, String> headers = extractHeaders(request); apm.extractContext(headers); // Start span for HTTP request io.opentracing.Span span = apm.startHttpClientSpan( request.getMethod(), request.getRequestURI(), "spring-web" ); // Add request details to span span.setTag("http.host", request.getServerName()); span.setTag("http.port", request.getServerPort()); span.setTag("http.query_string", request.getQueryString()); span.setTag("http.user_agent", request.getHeader("User-Agent")); span.setTag("http.client_ip", getClientIp(request)); // Store span in request for later use request.setAttribute("datadog.span", span); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { Long startTime = requestStartTime.get(); if (startTime != null) { long duration = System.currentTimeMillis() - startTime; io.opentracing.Span span = (io.opentracing.Span) request.getAttribute("datadog.span"); if (span != null) { span.setTag("http.status_code", response.getStatus()); span.setTag("http.response_size", response.getBufferSize()); if (ex != null) { span.setTag("error", true); span.log(Map.of( "event", "error", "error.object", ex.getClass().getName(), "message", ex.getMessage() )); } // Add performance metrics apm.addBusinessMetrics(span, "http.request.duration", duration); apm.addBusinessMetrics(span, "http.request.count", 1); span.finish(); } requestStartTime.remove(); } } @Override public void onApplicationEvent(ApplicationReadyEvent event) { // Application startup metrics apm.recordBusinessEvent("application.startup", Map.of( "version", apm.getConfig().getAppVersion(), "environment", apm.getConfig().getEnvironment(), "timestamp", System.currentTimeMillis() )); } private Map<String, String> extractHeaders(HttpServletRequest request) { Map<String, String> headers = new HashMap<>(); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); headers.put(headerName, request.getHeader(headerName)); } return headers; } private String getClientIp(HttpServletRequest request) { String xForwardedFor = request.getHeader("X-Forwarded-For"); if (xForwardedFor != null && !xForwardedFor.isEmpty()) { return xForwardedFor.split(",")[0].trim(); } return request.getRemoteAddr(); } } /** * AOP aspect for method-level tracing */ package com.titliel.apm.spring.aspect; import com.titliel.apm.TitlielDatadogApm; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Aspect @Component public class ApmTracingAspect { @Autowired private TitlielDatadogApm apm; @Around("@annotation(com.titliel.apm.annotation.TraceMethod)") public Object traceMethod(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String methodName = signature.getDeclaringType().getSimpleName() + "." + signature.getName(); String resourceName = signature.getDeclaringType().getSimpleName() + "." + signature.getName(); return apm.traceMethod(methodName, resourceName, () -> { try { return joinPoint.proceed(); } catch (Throwable throwable) { if (throwable instanceof Exception) { throw (Exception) throwable; } throw new RuntimeException(throwable); } }); } @Around("execution(* com.titliel..*Repository.*(..))") public Object traceRepository(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String methodName = "repository." + signature.getName(); String resourceName = signature.getDeclaringType().getSimpleName() + "." + signature.getName(); return apm.traceMethod(methodName, resourceName, () -> { try { return joinPoint.proceed(); } catch (Throwable throwable) { if (throwable instanceof Exception) { throw (Exception) throwable; } throw new RuntimeException(throwable); } }); } @Around("execution(* com.titliel..*Service.*(..))") public Object traceService(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String methodName = "service." + signature.getName(); String resourceName = signature.getDeclaringType().getSimpleName() + "." + signature.getName(); return apm.traceMethod(methodName, resourceName, () -> { try { return joinPoint.proceed(); } catch (Throwable throwable) { if (throwable instanceof Exception) { throw (Exception) throwable; } throw new RuntimeException(throwable); } }); } }

Database Integration

package com.titliel.apm.database; import com.titliel.apm.TitlielDatadogApm; import io.opentracing.Span; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Component; import java.sql.*; import java.util.List; import java.util.Map; /** * Datadog APM integration for database operations */ @Component public class ApmJdbcTemplate extends JdbcTemplate { private final TitlielDatadogApm apm; public ApmJdbcTemplate(TitlielDatadogApm apm) { this.apm = apm; } @Override public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) { Span span = apm.startDBSpan(sql, "mysql", getDatabaseName()); try { span.setTag("db.operation", "queryForObject"); span.setTag("db.args_count", args.length); T result = super.queryForObject(sql, rowMapper, args); span.setTag("db.result_found", true); return result; } catch (Exception e) { span.setTag("db.error", true); span.log(Map.of("error", e.getMessage())); throw e; } finally { span.finish(); } } @Override public List<Map<String, Object>> queryForList(String sql, Object... args) { Span span = apm.startDBSpan(sql, "mysql", getDatabaseName()); try { span.setTag("db.operation", "queryForList"); span.setTag("db.args_count", args.length); List<Map<String, Object>> result = super.queryForList(sql, args); span.setTag("db.rows_returned", result.size()); return result; } catch (Exception e) { span.setTag("db.error", true); span.log(Map.of("error", e.getMessage())); throw e; } finally { span.finish(); } } @Override public int update(String sql, Object... args) { Span span = apm.startDBSpan(sql, "mysql", getDatabaseName()); try { span.setTag("db.operation", "update"); span.setTag("db.args_count", args.length); int rowsAffected = super.update(sql, args); span.setTag("db.rows_affected", rowsAffected); return rowsAffected; } catch (Exception e) { span.setTag("db.error", true); span.log(Map.of("error", e.getMessage())); throw e; } finally { span.finish(); } } @Override public void execute(String sql) { Span span = apm.startDBSpan(sql, "mysql", getDatabaseName()); try { span.setTag("db.operation", "execute"); super.execute(sql); } catch (Exception e) { span.setTag("db.error", true); span.log(Map.of("error", e.getMessage())); throw e; } finally { span.finish(); } } private String getDatabaseName() { // Extract database name from datasource try { return getDataSource().getConnection().getCatalog(); } catch (SQLException e) { return "unknown"; } } } /** * Prepared statement wrapper for tracing */ class TracedPreparedStatement implements PreparedStatement { private final PreparedStatement delegate; private final Span span; private final String sql; public TracedPreparedStatement(PreparedStatement delegate, Span span, String sql) { this.delegate = delegate; this.span = span; this.sql = sql; } @Override public ResultSet executeQuery() throws SQLException { long startTime = System.currentTimeMillis(); try { ResultSet resultSet = delegate.executeQuery(); long duration = System.currentTimeMillis() - startTime; span.setTag("db.execution_time", duration); return resultSet; } catch (SQLException e) { span.setTag("db.error", true); span.log(Map.of("error", e.getMessage())); throw e; } } @Override public int executeUpdate() throws SQLException { long startTime = System.currentTimeMillis(); try { int result = delegate.executeUpdate(); long duration = System.currentTimeMillis() - startTime; span.setTag("db.execution_time", duration); span.setTag("db.rows_affected", result); return result; } catch (SQLException e) { span.setTag("db.error", true); span.log(Map.of("error", e.getMessage())); throw e; } } @Override public boolean execute() throws SQLException { long startTime = System.currentTimeMillis(); try { boolean result = delegate.execute(); long duration = System.currentTimeMillis() - startTime; span.setTag("db.execution_time", duration); return result; } catch (SQLException e) { span.setTag("db.error", true); span.log(Map.of("error", e.getMessage())); throw e; } } // Delegate all other methods... @Override public void close() throws SQLException { delegate.close(); span.finish(); } // Implement all other PreparedStatement methods by delegating to the delegate @Override public ResultSetMetaData getMetaData() throws SQLException { return delegate.getMetaData(); } @Override public ParameterMetaData getParameterMetaData() throws SQLException { return delegate.getParameterMetaData(); } // ... implement all other methods }

HTTP Client Integration

package com.titliel.apm.http; import com.titliel.apm.TitlielDatadogApm; import io.opentracing.Span; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * HTTP client interceptor for Datadog tracing */ @Component public class ApmHttpClientInterceptor implements ClientHttpRequestInterceptor { private final TitlielDatadogApm apm; public ApmHttpClientInterceptor(TitlielDatadogApm apm) { this.apm = apm; } @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { Span span = apm.startHttpClientSpan( request.getMethod().name(), request.getURI().toString(), "http-client" ); // Inject tracing headers Map<String, String> headers = new HashMap<>(); apm.injectContext(span, headers); headers.forEach((key, value) -> { request.getHeaders().add(key, value); }); long startTime = System.currentTimeMillis(); try { ClientHttpResponse response = execution.execute(request, body); long duration = System.currentTimeMillis() - startTime; span.setTag("http.status_code", response.getStatusCode().value()); span.setTag("http.response_size", response.getHeaders().getContentLength()); span.setTag("http.duration", duration); apm.addBusinessMetrics(span, "http.client.duration", duration); return response; } catch (IOException e) { span.setTag("http.error", true); span.log(Map.of( "event", "error", "error.object", e.getClass().getName(), "message", e.getMessage() )); throw e; } finally { span.finish(); } } } /** * REST Template configuration with APM */ package com.titliel.apm.config; import com.titliel.apm.http.ApmHttpClientInterceptor; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; import java.util.List; @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(RestTemplateBuilder builder, ApmHttpClientInterceptor apmInterceptor) { return builder .additionalInterceptors(apmInterceptor) .build(); } }

Custom Metrics and Monitoring

package com.titliel.apm.metrics; import com.titliel.apm.TitlielDatadogApm; import io.opentracing.Span; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.lang.management.*; import java.util.HashMap; import java.util.Map; /** * Custom metrics collector for Datadog */ @Component public class ApmMetricsCollector { private final TitlielDatadogApm apm; private final MemoryMXBean memoryMXBean; private final ThreadMXBean threadMXBean; private final OperatingSystemMXBean osMXBean; public ApmMetricsCollector(TitlielDatadogApm apm) { this.apm = apm; this.memoryMXBean = ManagementFactory.getMemoryMXBean(); this.threadMXBean = ManagementFactory.getThreadMXBean(); this.osMXBean = ManagementFactory.getOperatingSystemMXBean(); } @Scheduled(fixedRate = 60000) // Collect every minute public void collectSystemMetrics() { Span span = apm.startBusinessTrace("system.metrics.collection", "metrics-collector"); try { // Memory metrics MemoryUsage heapMemory = memoryMXBean.getHeapMemoryUsage(); MemoryUsage nonHeapMemory = memoryMXBean.getNonHeapMemoryUsage(); apm.addBusinessMetrics(span, "jvm.memory.heap.used", heapMemory.getUsed()); apm.addBusinessMetrics(span, "jvm.memory.heap.max", heapMemory.getMax()); apm.addBusinessMetrics(span, "jvm.memory.heap.committed", heapMemory.getCommitted()); apm.addBusinessMetrics(span, "jvm.memory.nonheap.used", nonHeapMemory.getUsed()); apm.addBusinessMetrics(span, "jvm.memory.nonheap.committed", nonHeapMemory.getCommitted()); // Thread metrics apm.addBusinessMetrics(span, "jvm.threads.count", threadMXBean.getThreadCount()); apm.addBusinessMetrics(span, "jvm.threads.daemon", threadMXBean.getDaemonThreadCount()); apm.addBusinessMetrics(span, "jvm.threads.peak", threadMXBean.getPeakThreadCount()); // GC metrics for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) { String gcName = gc.getName().replace(" ", "_").toLowerCase(); apm.addBusinessMetrics(span, "jvm.gc." + gcName + ".count", gc.getCollectionCount()); apm.addBusinessMetrics(span, "jvm.gc." + gcName + ".time", gc.getCollectionTime()); } // System metrics apm.addBusinessMetrics(span, "system.cpu.load", osMXBean.getSystemLoadAverage()); apm.addBusinessMetrics(span, "system.cpu.cores", osMXBean.getAvailableProcessors()); } finally { span.finish(); } } @Scheduled(fixedRate = 30000) // Collect every 30 seconds public void collectBusinessMetrics() { Map<String, ServiceMetrics> metrics = apm.getServiceMetrics(); Span span = apm.startBusinessTrace("business.metrics.collection", "metrics-collector"); try { for (Map.Entry<String, ServiceMetrics> entry : metrics.entrySet()) { String serviceName = entry.getKey(); ServiceMetrics serviceMetrics = entry.getValue(); apm.addBusinessMetrics(span, "service." + serviceName + ".request_count", serviceMetrics.getRequestCount()); apm.addBusinessMetrics(span, "service." + serviceName + ".error_count", serviceMetrics.getErrorCount()); apm.addBusinessMetrics(span, "service." + serviceName + ".error_rate", serviceMetrics.getErrorRate()); apm.addBusinessMetrics(span, "service." + serviceName + ".avg_duration", serviceMetrics.getAverageDuration()); apm.addBusinessMetrics(span, "service." + serviceName + ".p95_duration", serviceMetrics.getPercentile(0.95)); } } finally { span.finish(); } } /** * Record custom business metric */ public void recordBusinessMetric(String metricName, double value, Map<String, String> tags) { Span span = apm.startBusinessTrace("business.metric.record", "metrics-collector"); try { apm.addBusinessMetrics(span, "business." + metricName, value); // Add tags as span tags if (tags != null) { tags.forEach(span::setTag); } } finally { span.finish(); } } }

Configuration Classes

package com.titliel.apm; import java.io.InputStream; import java.util.Properties; /** * APM configuration manager */ public class ApmConfig { private static ApmConfig instance; private final Properties properties; private ApmConfig() { this.properties = loadProperties(); } public static synchronized ApmConfig getInstance() { if (instance == null) { instance = new ApmConfig(); } return instance; } private Properties loadProperties() { Properties props = new Properties(); try (InputStream input = getClass().getClassLoader() .getResourceAsStream("datadog.properties")) { if (input != null) { props.load(input); } else { setDefaults(props); } } catch (Exception e) { setDefaults(props); } return props; } private void setDefaults(Properties props) { props.setProperty("dd.service", "my-java-app"); props.setProperty("dd.env", "development"); props.setProperty("dd.version", "1.0.0"); props.setProperty("dd.trace.analytics.enabled", "true"); props.setProperty("dd.trace.methods", "com.titliel.service.*"); props.setProperty("dd.trace.db.client.enabled", "true"); props.setProperty("dd.trace.http.client.enabled", "true"); props.setProperty("dd.trace.servlet.enabled", "true"); } // Getters public String getServiceName() { return properties.getProperty("dd.service", "my-java-app"); } public String getEnvironment() { return properties.getProperty("dd.env", "development"); } public String getAppVersion() { return properties.getProperty("dd.version", "1.0.0"); } public String getDeploymentId() { return properties.getProperty("dd.deployment.id", System.getenv("DEPLOYMENT_ID")); } public boolean isAnalyticsEnabled() { return Boolean.parseBoolean(properties.getProperty("dd.trace.analytics.enabled", "true")); } }

Usage Examples

package com.titliel.service; import com.titliel.apm.TitlielDatadogApm; import com.titliel.apm.annotation.TraceMethod; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Map; @Service public class UserService { @Autowired private TitlielDatadogApm apm; @Autowired private UserRepository userRepository; @TraceMethod public User createUser(User user) { return apm.traceMethod("UserService.createUser", "user.create", () -> { // Validate user validateUser(user); // Record business event apm.recordBusinessEvent("user.created", Map.of( "userId", user.getId(), "email", user.getEmail(), "source", "api" )); // Save user User savedUser = userRepository.save(user); // Add custom metrics apm.addBusinessMetrics(apm.getActiveSpan(), "user.created.count", 1); return savedUser; }); } @TraceMethod public User getUser(String userId) { return apm.traceMethod("UserService.getUser", "user.get", () -> { User user = userRepository.findById(userId); if (user == null) { // Record not found as business event apm.recordBusinessEvent("user.not_found", Map.of( "userId", userId )); throw new UserNotFoundException("User not found: " + userId); } return user; }); } public void processUserBatch(List<User> users) { apm.traceMethod("UserService.processUserBatch", "user.batch.process", () -> { int successCount = 0; int errorCount = 0; for (User user : users) { try { processUser(user); successCount++; } catch (Exception e) { errorCount++; apm.recordBusinessEvent("user.process.error", Map.of( "userId", user.getId(), "error", e.getMessage() )); } } // Record batch metrics apm.addBusinessMetrics(apm.getActiveSpan(), "user.batch.success", successCount); apm.addBusinessMetrics(apm.getActiveSpan(), "user.batch.errors", errorCount); apm.addBusinessMetrics(apm.getActiveSpan(), "user.batch.total", users.size()); return null; }); } private void validateUser(User user) { // Validation logic if (user.getEmail() == null || !user.getEmail().contains("@")) { throw new ValidationException("Invalid email format"); } } private void processUser(User user) { // Processing logic } }

Maven Dependencies

<dependencies> <!-- Datadog APM --> <dependency> <groupId>com.datadoghq</groupId> <artifactId>dd-trace-api</artifactId> <version>1.10.0</version> </dependency> <!-- Datadog Java Agent (runtime) --> <dependency> <groupId>com.datadoghq</groupId> <artifactId>dd-java-agent</artifactId> <version>1.10.0</version> <scope>provided</scope> </dependency> <!-- OpenTracing --> <dependency> <groupId>io.opentracing</groupId> <artifactId>opentracing-api</artifactId> <version>0.33.0</version> </dependency> <dependency> <groupId>io.opentracing</groupId> <artifactId>opentracing-util</artifactId> <version>0.33.0</version> </dependency> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> </dependencies>

Application Properties

# Datadog Configuration dd.service=my-java-app dd.env=production dd.version=1.0.0 dd.trace.analytics.enabled=true # Logging dd.logs.injection=true # Sampling dd.trace.sample.rate=1.0 dd.trace.span.sampling.rules=[{"service": "my-java-app", "name": "web.request", "sample_rate": 1.0}] # Integration specific dd.trace.db.client.split-by-instance=true dd.trace.http.client.split-by-domain=true dd.integration.spring.enabled=true

Best Practices

1. Proper Service Naming

  • Use meaningful service names
  • Follow naming conventions
  • Include environment in service name

2. Effective Tagging

  • Use consistent tag names
  • Include business context in tags
  • Avoid high-cardinality tags

3. Performance Optimization

  • Use async tracing for high-throughput
  • Implement sampling for high-volume services
  • Monitor APM overhead

4. Error Tracking

  • Include meaningful error messages
  • Add stack traces for errors
  • Track error rates and patterns

5. Distributed Tracing

  • Propagate context across services
  • Use consistent operation names
  • Include correlation IDs

This Titliel Datadog APM integration provides comprehensive monitoring, tracing, and metrics collection for Java applications, enabling full observability in production environments.

Leave a Reply

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


Macro Nepal Helper