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. For Java applications, it offers robust telemetry collection, application performance monitoring, and log analytics capabilities.

This guide covers everything from basic setup to advanced custom monitoring scenarios with Azure Monitor in Java.

Azure Monitor Components for Java

  • Application Insights: APM solution for performance monitoring and exception tracking
  • Log Analytics: Centralized log collection and analysis
  • Azure Metrics: Infrastructure and custom metrics
  • Azure Dashboards: Visualization and alerting
  • Distributed Tracing: End-to-end transaction tracking

1. Project Setup and Dependencies

Maven Dependencies:

<properties>
<applicationinsights.version>3.4.18</applicationinsights.version>
<azure.monitor.version>1.0.0-beta.17</azure.monitor.version>
<opentelemetry.version>1.32.0</opentelemetry.version>
</properties>
<dependencies>
<!-- Application Insights Core -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>applicationinsights-core</artifactId>
<version>${applicationinsights.version}</version>
</dependency>
<!-- Application Insights Web -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>applicationinsights-web</artifactId>
<version>${applicationinsights.version}</version>
</dependency>
<!-- Application Insights Logging -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>applicationinsights-logging-logback</artifactId>
<version>${applicationinsights.version}</version>
</dependency>
<!-- Azure Monitor OpenTelemetry Distro -->
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-monitor-opentelemetry-autoconfigure</artifactId>
<version>${azure.monitor.version}</version>
</dependency>
<!-- OpenTelemetry API -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>${opentelemetry.version}</version>
</dependency>
</dependencies>

2. Basic Application Insights Setup

ApplicationInsights.xml Configuration:

<?xml version="1.0" encoding="utf-8"?>
<ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings" 
schemaVersion="2014-05-30">
<!-- The instrumentation key from Azure Portal -->
<InstrumentationKey>your-instrumentation-key-here</InstrumentationKey>
<!-- Connection string for newer Azure regions -->
<ConnectionString>InstrumentationKey=your-instrumentation-key;IngestionEndpoint=https://your-region-1.in.applicationinsights.azure.com/</Connectionistry>
<!-- Telemetry channels -->
<Channel type="com.microsoft.applicationinsights.channel.concrete.inprocess.InProcessTelemetryChannel">
<EndpointAddress>https://your-region-1.in.applicationinsights.azure.com/v2/track</EndpointAddress>
<MaxTelemetryBufferCapacity>1000</MaxTelemetryBufferCapacity>
<FlushIntervalInSeconds>5</FlushIntervalInSeconds>
</Channel>
<!-- Sampling settings -->
<Sampling percentage="100">
<ExcludedTypes>Event;Exception;Message;Metric;PageView;Request;Trace</ExcludedTypes>
</Sampling>
<!-- Performance counters -->
<PerformanceCounters>
<UseBuiltIn>true</UseBuiltIn>
</PerformanceCounters>
<!-- JMX metrics -->
<JMX metricsConfigFile="jmxmetrics.json"/>
</ApplicationInsights>

Programmatic Configuration:

package com.example.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 initApplicationInsights() {
TelemetryConfiguration config = TelemetryConfiguration.getActive();
// Set connection string (recommended) or instrumentation key
config.setConnectionString("InstrumentationKey=your-instrumentation-key;IngestionEndpoint=https://your-region-1.in.applicationinsights.azure.com/");
// Configure sampling
config.setSamplingPercentage(100.0); // 100% of telemetry
// Enable performance counters
config.setPerformanceCounterCollectionEnabled(true);
config.setJmxCollectionEnabled(true);
}
@Bean
public FilterRegistrationBean<WebRequestTrackingFilter> webRequestTrackingFilter() {
WebRequestTrackingFilter filter = new WebRequestTrackingFilter();
FilterRegistrationBean<WebRequestTrackingFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(filter);
registration.addUrlPatterns("/*");
registration.setOrder(1);
return registration;
}
}

3. Custom Telemetry Tracking

Telemetry Service:

package com.example.monitoring;
import com.microsoft.applicationinsights.TelemetryClient;
import com.microsoft.applicationinsights.telemetry.*;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@Component
public class AzureMonitorService {
private final TelemetryClient telemetryClient;
public AzureMonitorService() {
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 custom traces
public void trackTrace(String message, SeverityLevel severity, Map<String, String> properties) {
TraceTelemetry trace = new TraceTelemetry(message, severity);
if (properties != null) {
properties.forEach(trace.getProperties()::put);
}
telemetryClient.trackTrace(trace);
}
// 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, double amount, 
String currency, Map<String, String> context) {
Map<String, String> properties = new HashMap<>();
properties.put("TransactionName", transactionName);
properties.put("Currency", currency);
properties.put("Timestamp", new Date().toString());
if (context != null) {
properties.putAll(context);
}
Map<String, Double> metrics = new HashMap<>();
metrics.put("Amount", amount);
metrics.put("TransactionCount", 1.0);
trackEvent("BusinessTransaction", properties, metrics);
}
// Track page views
public void trackPageView(String pageName, long duration, Map<String, String> properties) {
PageViewTelemetry pageView = new PageViewTelemetry(pageName);
pageView.setDuration(duration);
if (properties != null) {
properties.forEach(pageView.getProperties()::put);
}
telemetryClient.trackPageView(pageView);
}
// Flush telemetry immediately
public void flush() {
telemetryClient.flush();
}
// Async telemetry tracking
public CompletableFuture<Void> trackEventAsync(String eventName, Map<String, String> properties) {
return CompletableFuture.runAsync(() -> {
trackEvent(eventName, properties, null);
});
}
}

4. Spring Boot Integration

Application Properties:

# application.properties
# Application Insights Configuration
azure.application-insights.instrumentation-key=your-instrumentation-key
azure.application-insights.connection-string=InstrumentationKey=your-key;IngestionEndpoint=https://your-region.in.applicationinsights.azure.com/
# Telemetry Configuration
azure.application-insights.sampling.percentage=100
azure.application-insights.enable-performance-counters=true
azure.application-insights.enable-jmx-metrics=true
# Logging Integration
logging.level.com.microsoft.applicationinsights=INFO

Spring Boot Configuration:

package com.example.config;
import com.example.monitoring.AzureMonitorService;
import com.microsoft.applicationinsights.TelemetryConfiguration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
public class AzureMonitorConfig {
@Value("${azure.application-insights.connection-string:}")
private String connectionString;
@Value("${azure.application-insights.instrumentation-key:}")
private String instrumentationKey;
@Value("${azure.application-insights.sampling.percentage:100}")
private double samplingPercentage;
@PostConstruct
public void init() {
TelemetryConfiguration config = TelemetryConfiguration.getActive();
if (!connectionString.isEmpty()) {
config.setConnectionString(connectionString);
} else if (!instrumentationKey.isEmpty()) {
config.setInstrumentationKey(instrumentationKey);
}
config.setSamplingPercentage(samplingPercentage);
}
@Bean
public AzureMonitorService azureMonitorService() {
return new AzureMonitorService();
}
}

5. Custom Business Monitoring

Business Metrics Service:

package com.example.monitoring;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Service
public class BusinessMetricsService {
private final AzureMonitorService monitorService;
private final Map<String, AtomicLong> counters = new ConcurrentHashMap<>();
public BusinessMetricsService(AzureMonitorService monitorService) {
this.monitorService = monitorService;
}
public void recordOrderCreated(Order order) {
Map<String, String> properties = new HashMap<>();
properties.put("OrderId", order.getId());
properties.put("CustomerId", order.getCustomerId());
properties.put("ProductCount", String.valueOf(order.getItems().size()));
properties.put("PaymentMethod", order.getPaymentMethod());
Map<String, Double> metrics = new HashMap<>();
metrics.put("OrderAmount", order.getAmount().doubleValue());
metrics.put("OrderCount", 1.0);
monitorService.trackEvent("OrderCreated", properties, metrics);
// Increment counter
incrementCounter("OrdersCreated");
incrementCounter("OrdersByPaymentMethod_" + order.getPaymentMethod());
}
public void recordUserRegistration(User user) {
Map<String, String> properties = new HashMap<>();
properties.put("UserId", user.getId());
properties.put("UserType", user.getType());
properties.put("RegistrationSource", user.getRegistrationSource());
monitorService.trackEvent("UserRegistered", properties, null);
incrementCounter("UsersRegistered");
}
public void recordPaymentProcessed(Payment payment, long processingTime, boolean success) {
Map<String, String> properties = new HashMap<>();
properties.put("PaymentId", payment.getId());
properties.put("PaymentGateway", payment.getGateway());
properties.put("Amount", String.valueOf(payment.getAmount()));
properties.put("Success", String.valueOf(success));
Map<String, Double> metrics = new HashMap<>();
metrics.put("ProcessingTimeMs", (double) processingTime);
metrics.put("PaymentAmount", payment.getAmount().doubleValue());
monitorService.trackEvent("PaymentProcessed", properties, metrics);
if (success) {
incrementCounter("PaymentsSuccessful");
monitorService.trackMetric("Payment.Successful.Amount", payment.getAmount().doubleValue(), properties);
} else {
incrementCounter("PaymentsFailed");
}
}
public void recordApiResponseTime(String endpoint, String method, long responseTime, int statusCode) {
Map<String, String> properties = new HashMap<>();
properties.put("Endpoint", endpoint);
properties.put("Method", method);
properties.put("StatusCode", String.valueOf(statusCode));
monitorService.trackMetric("Api.ResponseTime", responseTime, properties);
// Categorize by status code
String statusCategory = statusCode / 100 + "xx";
monitorService.trackMetric("Api.ResponseTime." + statusCategory, responseTime, properties);
}
public void recordDatabaseQuery(String queryType, String table, long executionTime, boolean success) {
Map<String, String> properties = new HashMap<>();
properties.put("QueryType", queryType);
properties.put("Table", table);
properties.put("Success", String.valueOf(success));
monitorService.trackDependency("SQL", queryType + " on " + table, executionTime, success);
monitorService.trackMetric("Database.QueryTime", executionTime, properties);
}
public void recordCacheOperation(String cacheName, String operation, long duration, boolean hit) {
Map<String, String> properties = new HashMap<>();
properties.put("CacheName", cacheName);
properties.put("Operation", operation);
properties.put("Hit", String.valueOf(hit));
monitorService.trackMetric("Cache.OperationTime", duration, properties);
if ("get".equals(operation)) {
if (hit) {
incrementCounter("Cache.Hits");
} else {
incrementCounter("Cache.Misses");
}
}
}
private void incrementCounter(String counterName) {
counters.computeIfAbsent(counterName, k -> new AtomicLong(0)).incrementAndGet();
// Periodically flush counters as metrics
if (counters.get(counterName).get() % 100 == 0) {
monitorService.trackMetric(counterName, counters.get(counterName).get(), null);
}
}
public void flushCounters() {
counters.forEach((name, count) -> {
monitorService.trackMetric(name, count.get(), null);
count.set(0); // Reset after flushing
});
}
}

6. Distributed Tracing with OpenTelemetry

OpenTelemetry Configuration:

package com.example.config;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Tracer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenTelemetryConfig {
@Bean
public Tracer tracer() {
return GlobalOpenTelemetry.getTracer("com.example.application");
}
}

Distributed Tracing Service:

package com.example.monitoring;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.function.Supplier;
@Service
public class DistributedTracingService {
private final Tracer tracer;
public DistributedTracingService(Tracer tracer) {
this.tracer = tracer;
}
public <T> T traceOperation(String operationName, Map<String, String> attributes, Supplier<T> operation) {
Span span = tracer.spanBuilder(operationName)
.setSpanKind(SpanKind.INTERNAL)
.startSpan();
// Set attributes
if (attributes != null) {
attributes.forEach(span::setAttribute);
}
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();
}
}
public void traceAsyncOperation(String operationName, Map<String, String> attributes, Runnable operation) {
Span span = tracer.spanBuilder(operationName)
.setSpanKind(SpanKind.INTERNAL)
.startSpan();
if (attributes != null) {
attributes.forEach(span::setAttribute);
}
// For async operations, you'd need to propagate context
Context context = Context.current().with(span);
// Execute in new thread with context
new Thread(() -> {
try (Scope scope = context.makeCurrent()) {
operation.run();
span.setStatus(io.opentelemetry.api.trace.StatusCode.OK);
} catch (Exception e) {
span.setStatus(io.opentelemetry.api.trace.StatusCode.ERROR);
span.recordException(e);
} finally {
span.end();
}
}).start();
}
public Span startSpan(String name, Map<String, String> attributes) {
Span span = tracer.spanBuilder(name).startSpan();
if (attributes != null) {
attributes.forEach(span::setAttribute);
}
return span;
}
public void endSpan(Span span, boolean success) {
if (span != null) {
span.setStatus(success ? io.opentelemetry.api.trace.StatusCode.OK : io.opentelemetry.api.trace.StatusCode.ERROR);
span.end();
}
}
}

7. Web Request Monitoring

Request Tracking Filter:

package com.example.web;
import com.example.monitoring.AzureMonitorService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Component
public class RequestMonitoringFilter extends OncePerRequestFilter {
private final AzureMonitorService monitorService;
public RequestMonitoringFilter(AzureMonitorService monitorService) {
this.monitorService = monitorService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, 
HttpServletResponse response, 
FilterChain filterChain) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
try {
filterChain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
Map<String, String> properties = new HashMap<>();
properties.put("Method", request.getMethod());
properties.put("Path", request.getRequestURI());
properties.put("QueryString", request.getQueryString());
properties.put("StatusCode", String.valueOf(response.getStatus()));
properties.put("UserAgent", request.getHeader("User-Agent"));
properties.put("ClientIP", getClientIp(request));
// Track request metrics
monitorService.trackMetric("Request.Duration", duration, properties);
monitorService.trackMetric("Request.Status." + response.getStatus(), 1, properties);
// Track slow requests
if (duration > 1000) { // More than 1 second
monitorService.trackTrace("Slow request detected: " + request.getRequestURI() + 
" took " + duration + "ms", 
com.microsoft.applicationinsights.telemetry.SeverityLevel.Warning, 
properties);
}
}
}
private String getClientIp(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0];
}
return request.getRemoteAddr();
}
}

REST Controller with Monitoring:

package com.example.controller;
import com.example.monitoring.AzureMonitorService;
import com.example.monitoring.BusinessMetricsService;
import com.example.monitoring.DistributedTracingService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class OrderController {
private final AzureMonitorService monitorService;
private final BusinessMetricsService businessMetrics;
private final DistributedTracingService tracingService;
public OrderController(AzureMonitorService monitorService, 
BusinessMetricsService businessMetrics,
DistributedTracingService tracingService) {
this.monitorService = monitorService;
this.businessMetrics = businessMetrics;
this.tracingService = tracingService;
}
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody Order order) {
return tracingService.traceOperation("CreateOrder", 
Map.of("orderId", order.getId(), "customerId", order.getCustomerId()),
() -> {
try {
// Validate and process order
Order processedOrder = orderService.process(order);
// Track business metrics
businessMetrics.recordOrderCreated(processedOrder);
// Track custom event
monitorService.trackEvent("OrderAPI_Created", 
Map.of("OrderId", order.getId(), "Status", "Success"), null);
return ResponseEntity.ok(processedOrder);
} catch (Exception e) {
// Track exception with custom properties
monitorService.trackException(e, 
Map.of("OrderId", order.getId(), "Operation", "CreateOrder"), null);
monitorService.trackEvent("OrderAPI_Created", 
Map.of("OrderId", order.getId(), "Status", "Failed"), null);
throw e;
}
});
}
@GetMapping("/orders/{id}")
public ResponseEntity<Order> getOrder(@PathVariable String id) {
long startTime = System.currentTimeMillis();
try {
Order order = orderService.findById(id)
.orElseThrow(() -> new OrderNotFoundException(id));
businessMetrics.recordApiResponseTime("/api/orders/{id}", "GET", 
System.currentTimeMillis() - startTime, 200);
return ResponseEntity.ok(order);
} catch (OrderNotFoundException e) {
businessMetrics.recordApiResponseTime("/api/orders/{id}", "GET", 
System.currentTimeMillis() - startTime, 404);
throw e;
}
}
}

8. Log Integration with Logback

logback-spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Application Insights Appender -->
<appender name="AIAppender" class="com.microsoft.applicationinsights.logback.ApplicationInsightsAppender">
<instrumentationKey>${AI_INSTRUMENTATION_KEY}</instrumentationKey>
</appender>
<!-- Console Appender -->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- File Appender -->
<appender name="File" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Root Logger -->
<root level="INFO">
<appender-ref ref="Console" />
<appender-ref ref="File" />
<appender-ref ref="AIAppender" />
</root>
<!-- Application-specific loggers -->
<logger name="com.example" level="DEBUG" additivity="false">
<appender-ref ref="Console" />
<appender-ref ref="File" />
<appender-ref ref="AIAppender" />
</logger>
</configuration>

9. Custom Performance Monitoring

Performance Monitor:

package com.example.monitoring;
import com.microsoft.applicationinsights.TelemetryClient;
import com.microsoft.applicationinsights.telemetry.MetricTelemetry;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.lang.management.*;
import java.util.HashMap;
import java.util.Map;
@Component
public class PerformanceMonitor {
private final TelemetryClient telemetryClient;
private final MemoryMXBean memoryMXBean;
private final ThreadMXBean threadMXBean;
private final OperatingSystemMXBean osMXBean;
public PerformanceMonitor(TelemetryClient telemetryClient) {
this.telemetryClient = telemetryClient;
this.memoryMXBean = ManagementFactory.getMemoryMXBean();
this.threadMXBean = ManagementFactory.getThreadMXBean();
this.osMXBean = ManagementFactory.getOperatingSystemMXBean();
}
@Scheduled(fixedRate = 60000) // Every minute
public void collectSystemMetrics() {
collectMemoryMetrics();
collectThreadMetrics();
collectCPUMetrics();
collectGCMetrics();
}
private void collectMemoryMetrics() {
MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();
Map<String, String> properties = new HashMap<>();
properties.put("Type", "Heap");
telemetryClient.trackMetric(new MetricTelemetry("Memory.Heap.Used", heapUsage.getUsed()));
telemetryClient.trackMetric(new MetricTelemetry("Memory.Heap.Committed", heapUsage.getCommitted()));
telemetryClient.trackMetric(new MetricTelemetry("Memory.Heap.Max", heapUsage.getMax()));
telemetryClient.trackMetric(new MetricTelemetry("Memory.NonHeap.Used", nonHeapUsage.getUsed()));
telemetryClient.trackMetric(new MetricTelemetry("Memory.NonHeap.Committed", nonHeapUsage.getCommitted()));
}
private void collectThreadMetrics() {
int threadCount = threadMXBean.getThreadCount();
int daemonThreadCount = threadMXBean.getDaemonThreadCount();
long totalStartedThreadCount = threadMXBean.getTotalStartedThreadCount();
telemetryClient.trackMetric(new MetricTelemetry("Thread.Count", threadCount));
telemetryClient.trackMetric(new MetricTelemetry("Thread.DaemonCount", daemonThreadCount));
telemetryClient.trackMetric(new MetricTelemetry("Thread.TotalStarted", totalStartedThreadCount));
}
private void collectCPUMetrics() {
double systemLoad = osMXBean.getSystemLoadAverage();
int availableProcessors = osMXBean.getAvailableProcessors();
telemetryClient.trackMetric(new MetricTelemetry("CPU.SystemLoad", systemLoad));
telemetryClient.trackMetric(new MetricTelemetry("CPU.AvailableProcessors", availableProcessors));
// Process CPU load if supported
if (osMXBean instanceof com.sun.management.OperatingSystemMXBean) {
com.sun.management.OperatingSystemMXBean sunOsMXBean = 
(com.sun.management.OperatingSystemMXBean) osMXBean;
double processCpuLoad = sunOsMXBean.getProcessCpuLoad() * 100;
telemetryClient.trackMetric(new MetricTelemetry("CPU.ProcessLoad", processCpuLoad));
}
}
private void collectGCMetrics() {
for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
long collectionCount = gcBean.getCollectionCount();
long collectionTime = gcBean.getCollectionTime();
Map<String, String> properties = new HashMap<>();
properties.put("GCName", gcBean.getName());
telemetryClient.trackMetric(new MetricTelemetry("GC.CollectionCount", collectionCount));
telemetryClient.trackMetric(new MetricTelemetry("GC.CollectionTime", collectionTime));
}
}
}

10. Error Tracking and Alerting

Global Exception Handler:

package com.example.error;
import com.example.monitoring.AzureMonitorService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
private final AzureMonitorService monitorService;
public GlobalExceptionHandler(AzureMonitorService monitorService) {
this.monitorService = monitorService;
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleException(Exception e) {
Map<String, String> properties = Map.of(
"exceptionType", e.getClass().getSimpleName(),
"handler", "GlobalExceptionHandler"
);
Map<String, Double> metrics = Map.of("errorCount", 1.0);
monitorService.trackException(e, properties, metrics);
monitorService.trackEvent("UnhandledException", properties, metrics);
return ResponseEntity.internalServerError()
.body(Map.of(
"error", "Internal server error",
"timestamp", System.currentTimeMillis()
));
}
@ExceptionHandler(BusinessException.class)
public ResponseEntity<Map<String, Object>> handleBusinessException(BusinessException e) {
Map<String, String> properties = Map.of(
"errorCode", e.getErrorCode(),
"businessContext", e.getContext(),
"exceptionType", "BusinessException"
);
monitorService.trackException(e, properties, null);
monitorService.trackEvent("BusinessException", properties, null);
return ResponseEntity.badRequest()
.body(Map.of(
"error", e.getMessage(),
"code", e.getErrorCode(),
"timestamp", System.currentTimeMillis()
));
}
}

11. Best Practices and Configuration

Application Properties for Production:

# application-prod.properties
# Azure Monitor Configuration
azure.application-insights.connection-string=InstrumentationKey=your-prod-key;IngestionEndpoint=https://your-region.in.applicationinsights.azure.com/
# Telemetry Settings
azure.application-insights.sampling.percentage=20
azure.application-insights.enable-performance-counters=true
azure.application-insights.enable-jmx-metrics=true
# Logging
logging.level.com.microsoft.applicationinsights=WARN
logging.level.com.example=INFO
# Custom dimensions
application.version=2.1.0
environment=production

Conclusion

Azure Monitor provides comprehensive observability for Java applications with:

  1. Application Insights for APM and custom telemetry
  2. Distributed Tracing with OpenTelemetry integration
  3. Custom Metrics for business-specific monitoring
  4. Log Integration for centralized log management
  5. Performance Monitoring for system resources

Key benefits:

  • Deep Application Insights with minimal code changes
  • Powerful Query Capabilities with Kusto query language
  • Smart Alerting with dynamic thresholds
  • Rich Visualization with Azure Dashboards
  • Enterprise-grade scalability and reliability

By implementing these patterns, you can achieve comprehensive observability across your Java applications while leveraging Azure's powerful monitoring ecosystem.

Leave a Reply

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


Macro Nepal Helper