Azure Monitor in Java: Complete Integration Guide


Article

Azure Monitor provides comprehensive monitoring solutions for applications running on Azure, on-premises, or other cloud platforms. This guide covers how to integrate Azure Monitor into Java applications using the Azure SDK, custom metrics, logs, distributed tracing, and best practices.

Azure Monitor Components for Java

  • Application Insights: APM solution for performance monitoring
  • Azure Monitor Metrics: Time-series data for numerical values
  • Azure Monitor Logs: Log analytics with Kusto Query Language (KQL)
  • Distributed Tracing: End-to-end transaction tracking
  • Custom Telemetry: Business-specific monitoring data

Project Setup and Dependencies

Maven Dependencies:

<properties> <azure.version>1.41.0</azure.version> <applicationinsights.version>3.4.18</applicationinsights.version> <opentelemetry.version>1.32.0</opentelemetry.version> </properties> <dependencies> <!-- Azure Monitor Core --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-core</artifactId> <version>${azure.version}</version> </dependency> <!-- Azure Monitor OpenTelemetry Distro --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-monitor-opentelemetry-autoconfigure</artifactId> <version>1.0.0-beta.17</version> </dependency> <!-- Application Insights --> <dependency> <groupId>com.microsoft.azure</groupId> <artifactId>applicationinsights-core</artifactId> <version>${applicationinsights.version}</version> </dependency> <!-- Application Insights Web (for HTTP monitoring) --> <dependency> <groupId>com.microsoft.azure</groupId> <artifactId>applicationinsights-web</artifactId> <version>${applicationinsights.version}</version> </dependency> <!-- Azure Identity --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-identity</artifactId> <version>1.10.0</version> </dependency> <!-- OpenTelemetry API --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- Micrometer Azure Monitor --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-azure-monitor</artifactId> <version>1.11.5</version> </dependency> <!-- Spring Boot Starter (if using Spring) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>

1. Application Insights Integration

ApplicationInsights Configuration:

package com.example.azuremonitor.config; import com.microsoft.applicationinsights.TelemetryConfiguration; import com.microsoft.applicationinsights.web.internal.WebRequestTrackingFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; @Configuration public class ApplicationInsightsConfig { @PostConstruct public void initializeApplicationInsights() { String instrumentationKey = System.getenv("APPINSIGHTS_INSTRUMENTATIONKEY"); if (instrumentationKey != null && !instrumentationKey.isEmpty()) { TelemetryConfiguration.getActive().setInstrumentationKey(instrumentationKey); } } @Bean public FilterRegistrationBean<WebRequestTrackingFilter> webRequestTrackingFilter() { FilterRegistrationBean<WebRequestTrackingFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new WebRequestTrackingFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setOrder(1); return registrationBean; } }

Custom Telemetry Service:

package com.example.azuremonitor.service; import com.microsoft.applicationinsights.TelemetryClient; import com.microsoft.applicationinsights.telemetry.*; import org.springframework.stereotype.Service; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @Service public class ApplicationInsightsService { private final TelemetryClient telemetryClient; public ApplicationInsightsService() { this.telemetryClient = new TelemetryClient(); } // Track custom events public void trackEvent(String eventName, Map<String, String> properties, Map<String, Double> metrics) { EventTelemetry event = new EventTelemetry(eventName); if (properties != null) { properties.forEach(event.getProperties()::put); } if (metrics != null) { metrics.forEach(event.getMetrics()::put); } telemetryClient.trackEvent(event); } // Track custom metrics public void trackMetric(String metricName, double value, Map<String, String> properties) { MetricTelemetry metric = new MetricTelemetry(metricName, value); if (properties != null) { properties.forEach(metric.getProperties()::put); } telemetryClient.trackMetric(metric); } // Track dependencies (external service calls) public void trackDependency(String dependencyName, String commandName, long duration, boolean success) { DependencyTelemetry dependency = new DependencyTelemetry(); dependency.setName(dependencyName); dependency.setData(commandName); dependency.setDuration(duration); dependency.setSuccess(success); dependency.setTimestamp(new Date()); telemetryClient.trackDependency(dependency); } // Track exceptions public void trackException(Exception exception, Map<String, String> properties, Map<String, Double> metrics) { ExceptionTelemetry exceptionTelemetry = new ExceptionTelemetry(exception); if (properties != null) { properties.forEach(exceptionTelemetry.getProperties()::put); } if (metrics != null) { metrics.forEach(exceptionTelemetry.getMetrics()::put); } telemetryClient.trackException(exceptionTelemetry); } // Track business transactions public void trackBusinessTransaction(String transactionName, String userId, double amount, String status) { Map<String, String> properties = new HashMap<>(); properties.put("TransactionName", transactionName); properties.put("UserId", userId); properties.put("Status", status); Map<String, Double> metrics = new HashMap<>(); metrics.put("Amount", amount); trackEvent("BusinessTransaction", properties, metrics); } // Track page views public void trackPageView(String pageName, long duration) { PageViewTelemetry pageView = new PageViewTelemetry(); pageView.setName(pageName); pageView.setDuration(duration); telemetryClient.trackPageView(pageView); } // Track requests public void trackRequest(String requestName, long duration, boolean success) { RequestTelemetry request = new RequestTelemetry(); request.setName(requestName); request.setDuration(duration); request.setSuccess(success); request.setTimestamp(new Date()); telemetryClient.trackRequest(request); } // Flush telemetry asynchronously public CompletableFuture<Void> flushAsync() { return CompletableFuture.runAsync(() -> { telemetryClient.flush(); try { // Wait for flush to complete Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } // Track availability test public void trackAvailability(String testName, String runLocation, long duration, boolean success, String message) { AvailabilityTelemetry availability = new AvailabilityTelemetry(); availability.setName(testName); availability.setRunLocation(runLocation); availability.setDuration(duration); availability.setSuccess(success); availability.setMessage(message); telemetryClient.trackAvailability(availability); } }

2. Azure Monitor Metrics with Micrometer

Micrometer Azure Monitor Configuration:

package com.example.azuremonitor.config; import com.azure.monitor.opentelemetry.exporter.AzureMonitorExporterBuilder; import com.azure.monitor.opentelemetry.exporter.AzureMonitorTraceExporter; import io.micrometer.azuremonitor.AzureMonitorConfig; import io.micrometer.azuremonitor.AzureMonitorMeterRegistry; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.ResourceAttributes; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.time.Duration; @Configuration public class AzureMonitorMetricsConfig { @Bean public AzureMonitorConfig azureMonitorConfig() { return new AzureMonitorConfig() { @Override public String get(String key) { return null; } @Override public String instrumentationKey() { return System.getenv("APPINSIGHTS_INSTRUMENTATIONKEY"); } @Override public Duration step() { return Duration.ofSeconds(30); } }; } @Bean public MeterRegistry azureMonitorMeterRegistry(AzureMonitorConfig config) { AzureMonitorMeterRegistry registry = new AzureMonitorMeterRegistry(config, Clock.SYSTEM); // Add common tags registry.config().commonTags( "application", "order-service", "environment", System.getenv("ENVIRONMENT") != null ? System.getenv("ENVIRONMENT") : "development", "region", System.getenv("REGION") != null ? System.getenv("REGION") : "eastus" ); return registry; } @Bean public Tracer openTelemetryTracer() { String connectionString = System.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING"); if (connectionString == null || connectionString.isEmpty()) { // Return a no-op tracer if no connection string return io.opentelemetry.api.trace.Tracer.noop(); } AzureMonitorTraceExporter exporter = new AzureMonitorExporterBuilder() .connectionString(connectionString) .buildTraceExporter(); Resource resource = Resource.getDefault() .merge(Resource.create(Attributes.builder() .put(ResourceAttributes.SERVICE_NAME, "order-service") .put(ResourceAttributes.SERVICE_VERSION, "1.0.0") .put(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, System.getenv("ENVIRONMENT") != null ? System.getenv("ENVIRONMENT") : "development") .build())); OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() .setResource(resource) .build(); GlobalOpenTelemetry.set(openTelemetrySdk); return openTelemetrySdk.getTracer("com.example.orders"); } }

Custom Metrics Service:

package com.example.azuremonitor.service; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Service; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @Service public class AzureMetricsService { private final MeterRegistry meterRegistry; private final Counter httpRequests; private final Counter businessEvents; private final Timer httpRequestDuration; private final AtomicInteger activeUsers; private final AtomicLong cacheHits; private final AtomicLong cacheMisses; private final ConcurrentHashMap<String, Counter> customCounters; public AzureMetricsService(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; this.customCounters = new ConcurrentHashMap<>(); // Initialize core metrics this.httpRequests = Counter.builder("http.requests.total") .description("Total HTTP requests") .tag("application", "order-service") .register(meterRegistry); this.businessEvents = Counter.builder("business.events.total") .description("Total business events") .register(meterRegistry); this.httpRequestDuration = Timer.builder("http.request.duration") .description("HTTP request duration") .publishPercentiles(0.5, 0.95, 0.99) .register(meterRegistry); this.activeUsers = new AtomicInteger(0); Gauge.builder("application.active.users", activeUsers, AtomicInteger::get) .description("Number of active users") .register(meterRegistry); this.cacheHits = new AtomicLong(0); this.cacheMisses = new AtomicLong(0); // Cache effectiveness gauge Gauge.builder("cache.hit.ratio", this, AzureMetricsService::calculateCacheHitRatio) .description("Cache hit ratio") .register(meterRegistry); } // HTTP metrics public void recordHttpRequest(String method, String path, int status, long durationMs) { httpRequests.increment(); httpRequestDuration.record(durationMs, TimeUnit.MILLISECONDS); // Record detailed HTTP metrics Counter.builder("http.requests.detailed") .tag("method", method) .tag("path", path) .tag("status", String.valueOf(status)) .register(meterRegistry) .increment(); } // Business metrics public void recordBusinessEvent(String eventType, String entityType) { businessEvents.increment(); Counter.builder("business.events.detailed") .tag("event.type", eventType) .tag("entity.type", entityType) .register(meterRegistry) .increment(); } public void recordOrderCreated(String customerId, double amount, String currency) { businessEvents.increment(); Counter.builder("orders.created.total") .tag("currency", currency) .register(meterRegistry) .increment(); // Record order value meterRegistry.summary("orders.amount") .record(amount); } // User activity metrics public void userLoggedIn() { activeUsers.incrementAndGet(); } public void userLoggedOut() { activeUsers.decrementAndGet(); } // Cache metrics public void recordCacheHit() { cacheHits.incrementAndGet(); } public void recordCacheMiss() { cacheMisses.incrementAndGet(); } private double calculateCacheHitRatio() { long hits = cacheHits.get(); long misses = cacheMisses.get(); long total = hits + misses; return total > 0 ? (double) hits / total * 100 : 0; } // Database metrics public void recordDatabaseQuery(String queryType, long durationMs, boolean success) { Timer.builder("database.query.duration") .tag("query.type", queryType) .tag("success", String.valueOf(success)) .register(meterRegistry) .record(durationMs, TimeUnit.MILLISECONDS); Counter.builder("database.queries.total") .tag("query.type", queryType) .tag("success", String.valueOf(success)) .register(meterRegistry) .increment(); } // Custom counter with dynamic tags public void incrementCounter(String name, String... tags) { String counterKey = name + String.join("", tags); Counter counter = customCounters.computeIfAbsent(counterKey, k -> Counter.builder(name) .tags(tags) .register(meterRegistry) ); counter.increment(); } // Gauge for business metrics public void registerBusinessGauge(String name, AtomicLong value, String... tags) { Gauge.builder(name, value, AtomicLong::get) .tags(tags) .register(meterRegistry); } // Performance metrics public Timer.Sample startTimer() { return Timer.start(meterRegistry); } public void stopTimer(Timer.Sample sample, String timerName, String... tags) { sample.stop(Timer.builder(timerName) .tags(tags) .register(meterRegistry)); } }

3. OpenTelemetry with Azure Monitor

Distributed Tracing Configuration:

package com.example.azuremonitor.tracing; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; @Component public class AzureDistributedTracing { private final Tracer tracer; public AzureDistributedTracing() { this.tracer = GlobalOpenTelemetry.getTracer("order-service"); } // Create span for method execution public <T> T trace(String spanName, Map<String, Object> attributes, Supplier<T> operation) { Span span = tracer.spanBuilder(spanName).startSpan(); // Set attributes if (attributes != null) { attributes.forEach((key, value) -> span.setAttribute(key, value.toString())); } try (Scope scope = span.makeCurrent()) { T result = operation.get(); span.setStatus(io.opentelemetry.api.trace.StatusCode.OK); return result; } catch (Exception e) { span.setStatus(io.opentelemetry.api.trace.StatusCode.ERROR); span.recordException(e); throw e; } finally { span.end(); } } // Trace async operations public <T> CompletableFuture<T> traceAsync(String spanName, Map<String, Object> attributes, Supplier<CompletableFuture<T>> operation) { Span span = tracer.spanBuilder(spanName).startSpan(); if (attributes != null) { attributes.forEach((key, value) -> span.setAttribute(key, value.toString())); } try (Scope scope = span.makeCurrent()) { return operation.get() .whenComplete((result, throwable) -> { if (throwable != null) { span.setStatus(io.opentelemetry.api.trace.StatusCode.ERROR); span.recordException(throwable); } else { span.setStatus(io.opentelemetry.api.trace.StatusCode.OK); } span.end(); }); } } // Add custom attributes to current span public void addSpanAttributes(Map<String, Object> attributes) { Span currentSpan = Span.current(); if (currentSpan != null && attributes != null) { attributes.forEach((key, value) -> currentSpan.setAttribute(key, value.toString())); } } // Record business events in traces public void recordBusinessEvent(String eventName, Map<String, Object> properties) { Span currentSpan = Span.current(); if (currentSpan != null) { currentSpan.addEvent(eventName); if (properties != null) { properties.forEach((key, value) -> currentSpan.setAttribute("event." + key, value.toString())); } } } // Create span for HTTP requests public Span startHttpSpan(String method, String path, Map<String, String> headers) { return tracer.spanBuilder(String.format("%s %s", method, path)) .setAttribute("http.method", method) .setAttribute("http.path", path) .setAttribute("http.url", path) .startSpan(); } }

4. Spring Boot Integration

Azure Monitor Auto-Configuration:

package com.example.azuremonitor.config; import com.azure.core.credential.TokenCredential; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.monitor.ingestion.LogsIngestionClient; import com.azure.monitor.ingestion.LogsIngestionClientBuilder; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.autoconfigure.metrics.export.azuremonitor.AzureMonitorProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnableConfigurationProperties(AzureMonitorProperties.class) public class AzureMonitorAutoConfig { @Value("${azure.monitor.data-collection-endpoint:}") private String dataCollectionEndpoint; @Value("${azure.monitor.data-collection-rule-id:}") private String dataCollectionRuleId; @Bean public TokenCredential azureCredential() { return new DefaultAzureCredentialBuilder().build(); } @Bean public LogsIngestionClient logsIngestionClient(TokenCredential credential) { if (dataCollectionEndpoint == null || dataCollectionEndpoint.isEmpty()) { return null; } return new LogsIngestionClientBuilder() .endpoint(dataCollectionEndpoint) .credential(credential) .buildClient(); } @Bean public AzureLogsService azureLogsService(LogsIngestionClient logsClient) { return new AzureLogsService(logsClient, dataCollectionRuleId); } }

Custom Logs Service for Azure Monitor Logs:

package com.example.azuremonitor.service; import com.azure.monitor.ingestion.LogsIngestionClient; import com.azure.monitor.ingestion.models.UploadLogsResult; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Service; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class AzureLogsService { private final LogsIngestionClient logsClient; private final String dataCollectionRuleId; private final ObjectMapper objectMapper; private final String customTableName = "CustomAppLogs_CL"; public AzureLogsService(LogsIngestionClient logsClient, String dataCollectionRuleId) { this.logsClient = logsClient; this.dataCollectionRuleId = dataCollectionRuleId; this.objectMapper = new ObjectMapper(); } public void sendCustomLog(Map<String, Object> logData) { if (logsClient == null || dataCollectionRuleId == null) { return; // Skip if not configured } try { // Add common fields logData.putIfAbsent("TimeGenerated", OffsetDateTime.now()); logData.putIfAbsent("Application", "order-service"); logData.putIfAbsent("Environment", System.getenv("ENVIRONMENT") != null ? System.getenv("ENVIRONMENT") : "development"); List<Object> logs = new ArrayList<>(); logs.add(logData); UploadLogsResult result = logsClient.upload( dataCollectionRuleId, customTableName, logs ); // Log upload result if needed if (result.getStatus() != UploadLogsResult.Status.SUCCESS) { System.err.println("Failed to upload logs: " + result.getStatus()); } } catch (Exception e) { System.err.println("Error sending logs to Azure Monitor: " + e.getMessage()); } } public void sendBusinessLog(String operation, String entityId, String status, Map<String, Object> additionalData) { Map<String, Object> logEntry = new HashMap<>(); logEntry.put("Operation", operation); logEntry.put("EntityId", entityId); logEntry.put("Status", status); logEntry.put("LogType", "BusinessOperation"); if (additionalData != null) { logEntry.putAll(additionalData); } sendCustomLog(logEntry); } public void sendPerformanceLog(String operation, long durationMs, boolean success, Map<String, Object> context) { Map<String, Object> logEntry = new HashMap<>(); logEntry.put("Operation", operation); logEntry.put("DurationMs", durationMs); logEntry.put("Success", success); logEntry.put("LogType", "Performance"); if (context != null) { logEntry.putAll(context); } sendCustomLog(logEntry); } public void sendSecurityLog(String eventType, String userId, String resource, String action, Map<String, Object> details) { Map<String, Object> logEntry = new HashMap<>(); logEntry.put("EventType", eventType); logEntry.put("UserId", userId); logEntry.put("Resource", resource); logEntry.put("Action", action); logEntry.put("LogType", "Security"); if (details != null) { logEntry.putAll(details); } sendCustomLog(logEntry); } }

5. Spring Boot Web Integration

Request/Response Monitoring:

package com.example.azuremonitor.web; import com.example.azuremonitor.service.AzureMetricsService; import com.example.azuremonitor.service.ApplicationInsightsService; import com.example.azuremonitor.tracing.AzureDistributedTracing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; @Component public class AzureMonitorInterceptor implements HandlerInterceptor { private static final Logger logger = LoggerFactory.getLogger(AzureMonitorInterceptor.class); private final AzureMetricsService metricsService; private final ApplicationInsightsService appInsightsService; private final AzureDistributedTracing tracing; public AzureMonitorInterceptor(AzureMetricsService metricsService, ApplicationInsightsService appInsightsService, AzureDistributedTracing tracing) { this.metricsService = metricsService; this.appInsightsService = appInsightsService; this.tracing = tracing; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { long startTime = System.currentTimeMillis(); request.setAttribute("startTime", startTime); // Start tracing span Map<String, Object> spanAttributes = new HashMap<>(); spanAttributes.put("http.method", request.getMethod()); spanAttributes.put("http.path", request.getRequestURI()); spanAttributes.put("http.user_agent", request.getHeader("User-Agent")); spanAttributes.put("http.client_ip", getClientIp(request)); tracing.addSpanAttributes(spanAttributes); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { Long startTime = (Long) request.getAttribute("startTime"); long duration = System.currentTimeMillis() - startTime; int status = response.getStatus(); // Record metrics metricsService.recordHttpRequest( request.getMethod(), request.getRequestURI(), status, duration ); // Record in Application Insights appInsightsService.trackRequest( request.getMethod() + " " + request.getRequestURI(), duration, status < 400 ); // Record errors if (ex != null) { Map<String, String> properties = new HashMap<>(); properties.put("Method", request.getMethod()); properties.put("Path", request.getRequestURI()); appInsightsService.trackException(ex, properties, null); } // Record business event for significant operations if (isBusinessOperation(request)) { tracing.recordBusinessEvent("HTTP Request Completed", Map.of( "method", request.getMethod(), "path", request.getRequestURI(), "status", status, "duration", duration )); } } 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(); } private boolean isBusinessOperation(HttpServletRequest request) { String path = request.getRequestURI(); return path.contains("/api/") && (path.contains("/orders") || path.contains("/customers")); } }

6. Business Service Integration

Order Service with Azure Monitor:

package com.example.azuremonitor.service; import com.example.azuremonitor.tracing.AzureDistributedTracing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.Map; import java.util.concurrent.CompletableFuture; @Service public class OrderService { private static final Logger logger = LoggerFactory.getLogger(OrderService.class); private final AzureMetricsService metricsService; private final ApplicationInsightsService appInsightsService; private final AzureDistributedTracing tracing; private final AzureLogsService logsService; public OrderService(AzureMetricsService metricsService, ApplicationInsightsService appInsightsService, AzureDistributedTracing tracing, AzureLogsService logsService) { this.metricsService = metricsService; this.appInsightsService = appInsightsService; this.tracing = tracing; this.logsService = logsService; } public Order createOrder(CreateOrderRequest request) { return tracing.trace("OrderService.createOrder", Map.of( "order.customerId", request.getCustomerId(), "order.items.count", request.getItems().size(), "order.total.amount", request.getTotalAmount() ), () -> { try { // Record business event metricsService.recordBusinessEvent("order_creation", "order"); // Process order logic Order order = processOrder(request); // Track success in Application Insights appInsightsService.trackBusinessTransaction( "OrderCreated", request.getCustomerId(), request.getTotalAmount(), "SUCCESS" ); // Send custom log logsService.sendBusinessLog( "CreateOrder", order.getId(), "SUCCESS", Map.of( "CustomerId", request.getCustomerId(), "OrderAmount", request.getTotalAmount(), "ItemsCount", request.getItems().size() ) ); // Record metrics metricsService.recordOrderCreated( request.getCustomerId(), request.getTotalAmount(), request.getCurrency() ); return order; } catch (Exception e) { // Track failure appInsightsService.trackBusinessTransaction( "OrderCreated", request.getCustomerId(), request.getTotalAmount(), "FAILED" ); Map<String, String> properties = Map.of( "CustomerId", request.getCustomerId(), "Operation", "CreateOrder" ); appInsightsService.trackException(e, properties, null); logsService.sendBusinessLog( "CreateOrder", null, "FAILED", Map.of( "CustomerId", request.getCustomerId(), "Error", e.getMessage() ) ); throw e; } }); } public CompletableFuture<Order> processOrderAsync(Order order) { return tracing.traceAsync("OrderService.processOrderAsync", Map.of("order.id", order.getId()), () -> CompletableFuture.supplyAsync(() -> { long startTime = System.currentTimeMillis(); try { // Simulate processing Thread.sleep(100); metricsService.recordBusinessEvent("order_processed", "order"); long duration = System.currentTimeMillis() - startTime; logsService.sendPerformanceLog( "ProcessOrder", duration, true, Map.of("OrderId", order.getId()) ); return order; } catch (Exception e) { long duration = System.currentTimeMillis() - startTime; logsService.sendPerformanceLog( "ProcessOrder", duration, false, Map.of( "OrderId", order.getId(), "Error", e.getMessage() ) ); throw new RuntimeException(e); } })); } private Order processOrder(CreateOrderRequest request) { // Order processing logic return new Order( java.util.UUID.randomUUID().toString(), request.getCustomerId(), request.getItems(), request.getTotalAmount(), "CREATED" ); } }

7. Configuration Files

application.yml:

azure: monitor: data-collection-endpoint: ${AZURE_MONITOR_DCE:} data-collection-rule-id: ${AZURE_MONITOR_DCR_ID:} management: endpoints: web: exposure: include: health,metrics,info endpoint: health: show-details: always metrics: export: azuremonitor: enabled: true instrumentation-key: ${APPINSIGHTS_INSTRUMENTATIONKEY:} step: 30s logging: level: com.example.azuremonitor: DEBUG app: name: order-service version: 1.0.0

8. Docker Deployment

Dockerfile:

FROM openjdk:17-jre-slim # Install necessary tools RUN apt-get update && apt-get install -y \ curl \ && rm -rf /var/lib/apt/lists/* # Copy application COPY target/order-service.jar /app/ # Create non-root user RUN groupadd -r appuser && useradd -r -g appuser appuser USER appuser WORKDIR /app # Set Azure Monitor environment variables ENV APPLICATIONINSIGHTS_CONNECTION_STRING="" ENV APPINSIGHTS_INSTRUMENTATIONKEY="" ENV AZURE_MONITOR_DCE="" ENV AZURE_MONITOR_DCR_ID="" ENV ENVIRONMENT="production" EXPOSE 8080 CMD ["java", "-jar", "order-service.jar"]

docker-compose.yml:

version: '3.8' services: order-service: build: . ports: - "8080:8080" environment: - APPLICATIONINSIGHTS_CONNECTION_STRING=${APPLICATIONINSIGHTS_CONNECTION_STRING} - APPINSIGHTS_INSTRUMENTATIONKEY=${APPINSIGHTS_INSTRUMENTATIONKEY} - AZURE_MONITOR_DCE=${AZURE_MONITOR_DCE} - AZURE_MONITOR_DCR_ID=${AZURE_MONITOR_DCR_ID} - ENVIRONMENT=production - JAVA_OPTS=-Xms512m -Xmx1024m restart: unless-stopped

9. Kubernetes Deployment

ConfigMap:

apiVersion: v1 kind: ConfigMap metadata: name: order-service-config data: application.yml: | management: endpoints: web: exposure: include: health,metrics,info metrics: export: azuremonitor: enabled: true step: 30s logging: level: com.example.azuremonitor: INFO

Deployment:

apiVersion: apps/v1 kind: Deployment metadata: name: order-service spec: replicas: 3 selector: matchLabels: app: order-service template: metadata: labels: app: order-service annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" prometheus.io/path: "/actuator/prometheus" spec: containers: - name: order-service image: your-registry/order-service:latest ports: - containerPort: 8080 env: - name: APPLICATIONINSIGHTS_CONNECTION_STRING valueFrom: secretKeyRef: name: azure-secrets key: applicationinsights-connection-string - name: APPINSIGHTS_INSTRUMENTATIONKEY valueFrom: secretKeyRef: name: azure-secrets key: appinsights-instrumentationkey - name: ENVIRONMENT value: "production" - name: JAVA_OPTS value: "-Xms512m -Xmx1024m -javaagent:/app/applicationinsights-agent.jar" volumeMounts: - name: config mountPath: /app/config livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 20 periodSeconds: 5 resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" volumes: - name: config configMap: name: order-service-config

10. Best Practices

Performance Optimization:

  • Use async operations for telemetry submission
  • Implement batching for high-volume metrics
  • Configure appropriate sampling rates
  • Use connection pooling for HTTP clients

Error Handling:

@Component public class AzureMonitorErrorHandler { private final Counter telemetryErrors; public AzureMonitorErrorHandler(MeterRegistry registry) { this.telemetryErrors = Counter.builder("azure.monitor.errors") .register(registry); } public void handleError(String operation, Exception e) { telemetryErrors.increment(); logger.error("Azure Monitor operation failed: {}", operation, e); // Implement fallback logging or alerting if (e instanceof com.azure.core.exception.HttpResponseException) { // Handle specific Azure errors handleAzureError((com.azure.core.exception.HttpResponseException) e); } } private void handleAzureError(com.azure.core.exception.HttpResponseException e) { // Implement specific error handling for Azure services logger.warn("Azure service error: {} - {}", e.getStatusCode(), e.getMessage()); } }

Security:

  • Use Managed Identity when running on Azure
  • Secure connection strings and keys
  • Implement proper RBAC for Azure resources
  • Encrypt sensitive data in logs

Conclusion

Azure Monitor provides a comprehensive observability solution for Java applications. Key benefits include:

  1. Unified Monitoring: Combine metrics, logs, and traces in one platform
  2. Powerful Analytics: Kusto Query Language for advanced log analysis
  3. Smart Alerts: AI-driven anomaly detection and alerting
  4. Integration: Seamless integration with Azure ecosystem
  5. Cost-Effective: Flexible pricing based on data volume

By implementing the patterns shown above, you can achieve full-stack observability for your Java applications, from infrastructure metrics to business-level KPIs, all within the Azure Monitor ecosystem.

Leave a Reply

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


Macro Nepal Helper