A lightweight, zero-configuration Application Performance Monitoring (APM) client for Spring Boot.
It uses Spring AOP and Servlet Filters to automatically capture HTTP errors, latency profiling, response byte counts, system health, developer execution logs, request IP addresses (requestIp), and transaction correlation IDs (traceId), dispatching them asynchronously to your centralized APM server.
- Zero Code Changes — Works out of the box with no changes to your controllers or exception handlers.
- Distributed Request Correlation — Auto-generates unique
traceIdandspanIdper transaction, injecting into SLF4J MDC ([traceId=...]) and settingX-Trace-IdHTTP response headers. - Multi-Threaded & Async Log Capture — Uses
InheritableThreadLocalandtraceIdcontext registry to capture 100% of logs across@Asyncworker threads and parallel execution pools. - Latency & Payload Size Metrics — High-precision measurement of execution duration (
executionTimeMs) and zero-RAM output byte streaming counters (responseSizeBytes). - Multi-Tagging System — Auto-assigns system tags (
DEPRECATED_API,SLOW_REQUEST,HIGH_PAYLOAD_SIZE,SERVER_ERROR,CLIENT_ERROR), annotation tags (@LogDispatch(tags = {"..."})), and dynamic runtime tags (LogDispatchContext.addTag(...)). - Ignore Control Annotation — Selectively ignore specific controllers or methods using
@LogDispatch(enabled = false). - Configurable Request IP Capture — Captures caller IP (
requestIp), with option to mask (logdispatch.include-request-ip=false). - Configurable Dispatch Modes — Choose between
errors-only,all(100% APM traffic monitoring), orerrors-and-slow. - System Health Snapshots — Captures CPU usage % (
cpuUsagePercent) and RAM Memory usage % (memoryUsagePercent) during request events. - Asynchronous & Resilient — All log pushes run in a
CompletableFuturebackground thread. Fails silently if the APM server is unreachable, so your application never crashes.
Add the dependency to your pom.xml:
<dependency>
<groupId>in.maheshlangote</groupId>
<artifactId>logdispatch-spring-boot-starter</artifactId>
<version>1.2.1</version>
</dependency>logdispatch:
enabled: true
server-url: "https://your-apm-server.com/api/v1/ingest/logs"
api-key: "your-secret-api-key"
dispatch-mode: "errors-and-slow" # 'errors-and-slow' (default), 'all', or 'errors-only'
ignore-options-requests: true # Automatically skip HTTP OPTIONS CORS preflights
exclude-methods: "OPTIONS,HEAD" # Exclude specific HTTP methods
slow-threshold-ms: 3000 # Threshold in ms for slow request classification (default: 3000 ms)
include-request-ip: true # Set to false to mask requestIp as "MASKED"
timeout-ms: 3000
max-stack-frames: 100
masked-headers: "authorization,cookie,x-api-key"
exclude-paths: "/health,/actuator/**,/metrics/**"
logs:
enabled: true
min-level: "DEBUG"
exclude-loggers:
- "com.zaxxer.hikari"logdispatch.enabled=true
logdispatch.server-url=https://your-apm-server.com/api/v1/ingest/logs
logdispatch.api-key=your-secret-api-key
logdispatch.dispatch-mode=errors-and-slow
logdispatch.ignore-options-requests=true
logdispatch.exclude-methods=OPTIONS,HEAD
logdispatch.slow-threshold-ms=3000
logdispatch.include-request-ip=true
logdispatch.timeout-ms=3000
logdispatch.max-stack-frames=100
logdispatch.masked-headers=authorization,cookie,x-api-key
logdispatch.exclude-paths=/health,/actuator/**,/metrics/**
logdispatch.logs.enabled=true
logdispatch.logs.min-level=DEBUG
logdispatch.logs.exclude-loggers=com.zaxxer.hikari| Property | Required | Default | Description |
|---|---|---|---|
logdispatch.enabled |
❌ No | true |
Enables or disables the LogDispatch SDK. |
logdispatch.server-url |
✅ Yes, when enabled | http://localhost:8081/... |
Full URL of the APM ingest endpoint. |
logdispatch.api-key |
✅ Yes, when enabled | default-key |
API key used to authenticate with the APM server. |
logdispatch.dispatch-mode |
❌ No | errors-and-slow |
Controls when payloads are dispatched (DispatchMode.ERRORS_AND_SLOW, DispatchMode.ERRORS_ONLY, DispatchMode.ALL). |
logdispatch.ignore-options-requests |
❌ No | true |
Automatically bypasses HTTP OPTIONS CORS preflight requests. |
logdispatch.exclude-methods |
❌ No | OPTIONS |
Comma-separated list of HTTP methods to exclude from APM dispatching. |
logdispatch.slow-threshold-ms |
❌ No | 3000 |
Latency threshold in ms for tagging slow API requests (SLOW_REQUEST). |
logdispatch.include-request-ip |
❌ No | true |
Captures caller IP in requestIp. Set to false to mask IP as "MASKED". |
logdispatch.timeout-ms |
❌ No | 3000 |
Connection and read timeout in milliseconds. |
logdispatch.max-stack-frames |
❌ No | 100 |
Maximum number of stack trace frames included per error payload. |
logdispatch.masked-headers |
❌ No | [] |
Comma-separated list of HTTP headers to mask (e.g. authorization). |
logdispatch.exclude-paths |
❌ No | [] |
Comma-separated list of URI paths to exclude (supports wildcards like /actuator/**). |
logdispatch.health.enabled |
❌ No | true |
Enables or disables registering the /logdispatch/health endpoint. |
logdispatch.logs.enabled |
❌ No | true |
Enables capturing developer execution debug logs during requests (buffered up to 128 KB). |
logdispatch.logs.min-level |
❌ No | DEBUG |
Minimum log level captured (LogLevel.TRACE, DEBUG, INFO, WARN, ERROR). |
Every event is pushed as a POST request to the configured server-url.
| Header | Value |
|---|---|
Content-Type |
application/json |
X-API-KEY |
Value of logdispatch.api-key |
X-LogDispatch-Version |
Dynamic SDK Version (e.g. 1.2.1) |
X-LogDispatch-Language |
SDK Language (java) |
{
"timestamp": "2026-09-26T10:30:00.123Z",
"traceId": "b3e39005-a5c8-4787-8f48-c3f8b5a70245",
"spanId": "c4f5e6a7-1234-5678",
"parentSpanId": null,
"sdkVersion": "1.2.1",
"sdkLanguage": "java",
"requestIp": "103.21.12.44",
"isError": false,
"isDeprecated": true,
"severity": "SUCCESS",
"tags": [
"CRITICAL_PAYMENT",
"DEPRECATED_API",
"SLOW_REQUEST"
],
"statusCode": 200,
"errorType": "SUCCESS",
"errorMessage": "Request processed successfully.",
"errorPath": "/api/v1/service-requests/statistics",
"affectedFeature": "ServiceRequestControllerV1",
"affectedAPI": "/api/v1/service-requests/statistics",
"apiType": "GET",
"affectedFunction": "loggedInServicePartnerProfileDetails",
"stackTrace": "N/A",
"executionTimeMs": 1420,
"responseSizeBytes": 12850,
"performanceBreakdown": null,
"systemHealth": {
"cpuUsagePercent": 18.5,
"memoryUsagePercent": 64.2
},
"inputInformation": {
"queryString": "status=ACTIVE",
"parameters": {},
"headers": {
"host": "localhost:8080",
"content-type": "application/json",
"user-agent": "PostmanRuntime/7.32.3"
},
"body": "{\"entries\": []}"
},
"executionLogs": [
"2026-09-26T10:30:00.010Z INFO in.maheshlangote.service : Fetched partner statistics"
]
}| Field | Type | Description |
|---|---|---|
timestamp |
String | ISO-8601 UTC timestamp |
traceId |
String | Unique transaction correlation ID |
spanId |
String | Unique span ID for current execution step |
parentSpanId |
String | Parent caller span ID (if nested) |
sdkVersion |
String | SDK Version (e.g. 1.2.1) |
sdkLanguage |
String | SDK Language (java) |
requestIp |
String | Remote caller IP address (X-Forwarded-For or remote addr, or "MASKED") |
isError |
Boolean |
true if HTTP status false if successful ( |
isDeprecated |
Boolean |
true if method/class is annotated with @Deprecated
|
severity |
String |
SUCCESS, WARNING, CRITICAL, SECURITY, HTTP_FILTER_ERROR, ERROR, INFO
|
tags |
Array | System tags + custom annotation tags + dynamic runtime tags |
statusCode |
Number | HTTP status code (e.g. 200, 400, 403, 500) |
errorType |
String | Exception class name, HTTP reason (e.g. Unauthorized, NotFound), or SUCCESS
|
errorMessage |
String | Exception message or status description |
errorPath |
String | Request URI |
affectedFeature |
String | Controller name or @LogDispatch annotation override |
affectedAPI |
String | API path or @LogDispatch annotation override |
apiType |
String | HTTP method (GET, POST, etc.) or execution type |
affectedFunction |
String | Method name or @LogDispatch annotation override |
stackTrace |
String | Full stack trace string (or "N/A" for success) |
executionTimeMs |
Number | Total execution duration in milliseconds |
responseSizeBytes |
Number | Total output response payload size in bytes |
systemHealth |
Object | System health metrics (cpuUsagePercent, memoryUsagePercent) |
inputInformation |
Object | Request metadata including headers, parameters, and body (inputInformation.body) |
executionLogs |
Array | Structured array of developer logs printed during request execution |
Override default metadata (feature, api, function, severity, tags) with custom values:
import in.maheshlangote.logdispatch.annotation.LogDispatch;
import in.maheshlangote.logdispatch.annotation.LogSeverity;
@RestController
@LogDispatch(feature = "Payment Gateway")
public class PaymentController {
@PostMapping("/pay")
@LogDispatch(
api = "Process Payment",
function = "handlePayment",
severity = LogSeverity.CRITICAL,
tags = {"CRITICAL_PAYMENT", "VIP_FLOW"} // 👈 Custom Annotation Tags
)
public void handlePayment() {
// ...
}
}To completely ignore and skip APM telemetry for a specific controller or method:
@RestController
@LogDispatch(enabled = false) // 👈 Ignores all APIs in this controller completely
public class InternalAdminController {
@GetMapping("/ping")
@LogDispatch(enabled = false) // 👈 Ignores this specific endpoint
public String ping() {
return "pong";
}
}Add tags dynamically in code during method execution based on runtime business logic:
import in.maheshlangote.logdispatch.LogDispatchContext;
@Service
public class OrderService {
public void processOrder(Order order) {
if (order.getAmount() > 10000) {
LogDispatchContext.addTag("HIGH_VALUE_ORDER"); // 👈 Dynamic Runtime Tag
}
}
}The starter automatically exposes a lightweight endpoint that allows your APM server to verify application health and uptime.
GET /logdispatch/health{
"status": "UP",
"startupTime": "2026-05-31T02:00:00.000Z",
"uptimeSeconds": 120
}MIT License