Logstash Logback Encoder in Java

Introduction to Logstash Logback Encoder

The Logstash Logback Encoder provides JSON logging output formatted for Logstash ingestion. It enables structured logging with rich context information, making logs easily searchable and analyzable in ELK stack.

Dependencies and Setup

Maven Configuration

<properties>
<logstash.logback.version>7.4</logstash.logback.version>
<logback.version>1.4.14</logback.version>
</properties>
<dependencies>
<!-- Logstash Logback Encoder -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>${logstash.logback.version}</version>
</dependency>
<!-- Logback Classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- Jackson for JSON processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- For structured logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
</dependencies>

Basic Logback Configuration

logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- Property definitions -->
<property name="LOG_LEVEL" value="${LOG_LEVEL:-INFO}"/>
<property name="LOG_PATH" value="${LOG_PATH:-./logs}"/>
<property name="APP_NAME" value="${spring.application.name:-my-app}"/>
<property name="LOGSTASH_HOST" value="${LOGSTASH_HOST:-localhost}"/>
<property name="LOGSTASH_PORT" value="${LOGSTASH_PORT:-5000}"/>
<!-- Console Appender with Logstash JSON -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"app":"${APP_NAME}","env":"${spring.profiles.active:-default}"}</customFields>
<includeContext>true</includeContext>
<includeMdc>true</includeMdc>
<includeCallerData>true</includeCallerData>
<fieldNames>
<timestamp>timestamp</timestamp>
<version>[ignore]</version>
<level>level</level>
<thread>thread</thread>
<logger>logger</logger>
<message>message</message>
<stackTrace>stack_trace</stackTrace>
<mdc>context</mdc>
</fieldNames>
</encoder>
</appender>
<!-- File Appender with Logstash JSON -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APP_NAME}.json</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.%i.json</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<fieldName>timestamp</fieldName>
<timeZone>UTC</timeZone>
</timestamp>
<logLevel>
<fieldName>level</fieldName>
</logLevel>
<loggerName>
<fieldName>logger</fieldName>
</loggerName>
<pattern>
<pattern>
{
"thread": "%thread",
"message": "#asJson{%message}"
}
</pattern>
</pattern>
<mdc/>
<context/>
<stackTrace>
<fieldName>stack_trace</fieldName>
</stackTrace>
<arguments/>
</providers>
</encoder>
</appender>
<!-- Logstash TCP Appender -->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>${LOGSTASH_HOST}:${LOGSTASH_PORT}</destination>
<reconnectionDelay>10 second</reconnectionDelay>
<keepAliveDuration>5 minutes</keepAliveDuration>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"app":"${APP_NAME}","env":"${spring.profiles.active:-default}","version":"1.0.0"}</customFields>
<includeContext>true</includeContext>
<includeMdc>true</includeMdc>
<includeCallerData>true</includeCallerData>
</encoder>
</appender>
<!-- Async Logstash Appender -->
<appender name="ASYNC_LOGSTASH" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="LOGSTASH" />
<queueSize>8192</queueSize>
<discardingThreshold>0</discardingThreshold>
<includeCallerData>true</includeCallerData>
<neverBlock>true</neverBlock>
</appender>
<!-- Root Logger -->
<root level="${LOG_LEVEL}">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<appender-ref ref="ASYNC_LOGSTASH" />
</root>
<!-- Application-specific loggers -->
<logger name="com.myapp" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<appender-ref ref="ASYNC_LOGSTASH" />
</logger>
<!-- Reduce noise from framework logs -->
<logger name="org.springframework" level="INFO"/>
<logger name="org.hibernate" level="WARN"/>
<logger name="org.apache.kafka" level="WARN"/>
</configuration>

Advanced Encoder Configuration

Custom Logstash Encoder

@Component
public class CustomLogstashEncoder {
public LogstashEncoder createCustomEncoder() {
LogstashEncoder encoder = new LogstashEncoder();
// Custom fields added to every log message
encoder.setCustomFields("{\"app\":\"my-application\",\"environment\":\"production\"}");
// Include MDC (Mapped Diagnostic Context)
encoder.setIncludeMdc(true);
// Include caller data
encoder.setIncludeCallerData(true);
// Custom field names
encoder.setFieldNames(createFieldNames());
// Timestamp format
encoder.setTimestampPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
encoder.setTimeZone("UTC");
return encoder;
}
private LogstashEncoder.FieldNames createFieldNames() {
LogstashEncoder.FieldNames fieldNames = new LogstashEncoder.FieldNames();
fieldNames.setTimestamp("@timestamp");
fieldNames.setLevel("severity");
fieldNames.setThread("thread_name");
fieldNames.setLogger("logger_name");
fieldNames.setMessage("log_message");
fieldNames.setStackTrace("exception");
fieldNames.setMdc("context");
return fieldNames;
}
}

Composite JSON Encoder

@Configuration
public class LogbackEncoderConfig {
@Bean
public LoggingEventCompositeJsonEncoder compositeJsonEncoder() {
LoggingEventCompositeJsonEncoder encoder = new LoggingEventCompositeJsonEncoder();
List<JsonProvider<ILoggingEvent>> providers = new ArrayList<>();
providers.add(timestampProvider());
providers.add(versionProvider());
providers.add(logLevelProvider());
providers.add(loggerNameProvider());
providers.add(messageProvider());
providers.add(mdcProvider());
providers.add(contextProvider());
providers.add(stackTraceProvider());
providers.add(threadNameProvider());
providers.add(customPatternProvider());
encoder.setProviders(providers);
return encoder;
}
private TimestampJsonProvider timestampProvider() {
TimestampJsonProvider provider = new TimestampJsonProvider();
provider.setFieldName("@timestamp");
provider.setTimeZone("UTC");
return provider;
}
private LogLevelJsonProvider logLevelProvider() {
LogLevelJsonProvider provider = new LogLevelJsonProvider();
provider.setFieldName("level");
return provider;
}
private LoggerNameJsonProvider loggerNameProvider() {
LoggerNameJsonProvider provider = new LoggerNameJsonProvider();
provider.setFieldName("logger");
provider.setShortenedLoggerNameLength(20);
return provider;
}
private MessageJsonProvider messageProvider() {
MessageJsonProvider provider = new MessageJsonProvider();
provider.setFieldName("message");
return provider;
}
private MdcJsonProvider mdcProvider() {
MdcJsonProvider provider = new MdcJsonProvider();
provider.setIncludeMdcContext(true);
return provider;
}
private ContextJsonProvider contextProvider() {
return new ContextJsonProvider();
}
private StackTraceJsonProvider stackTraceProvider() {
StackTraceJsonProvider provider = new StackTraceJsonProvider();
provider.setFieldName("stack_trace");
return provider;
}
private ThreadNameJsonProvider threadNameProvider() {
ThreadNameJsonProvider provider = new ThreadNameJsonProvider();
provider.setFieldName("thread_name");
return provider;
}
private ArgumentsJsonProvider argumentsProvider() {
return new ArgumentsJsonProvider();
}
private PatternJsonProvider customPatternProvider() {
PatternJsonProvider provider = new PatternJsonProvider();
provider.setPattern("{ \"custom_field\": \"custom_value\" }");
return provider;
}
}

Structured Logging in Java

MDC (Mapped Diagnostic Context) Management

@Component
public class MDCContextManager {
private static final String TRACE_ID = "traceId";
private static final String SPAN_ID = "spanId";
private static final String USER_ID = "userId";
private static final String SESSION_ID = "sessionId";
private static final String REQUEST_PATH = "requestPath";
private static final String HTTP_METHOD = "httpMethod";
private static final String CLIENT_IP = "clientIp";
public static void putTraceContext(String traceId, String spanId) {
MDC.put(TRACE_ID, traceId);
MDC.put(SPAN_ID, spanId);
}
public static void putUserContext(String userId, String sessionId) {
MDC.put(USER_ID, userId);
MDC.put(SESSION_ID, sessionId);
}
public static void putRequestContext(String path, String method, String clientIp) {
MDC.put(REQUEST_PATH, path);
MDC.put(HTTP_METHOD, method);
MDC.put(CLIENT_IP, clientIp);
}
public static void clear() {
MDC.clear();
}
public static String getTraceId() {
return MDC.get(TRACE_ID);
}
public static String getSpanId() {
return MDC.get(SPAN_ID);
}
public static Runnable wrap(Runnable runnable) {
Map<String, String> context = MDC.getCopyOfContextMap();
return () -> {
Map<String, String> previous = MDC.getCopyOfContextMap();
try {
if (context != null) {
MDC.setContextMap(context);
} else {
MDC.clear();
}
runnable.run();
} finally {
if (previous != null) {
MDC.setContextMap(previous);
} else {
MDC.clear();
}
}
};
}
public static <T> Callable<T> wrap(Callable<T> callable) {
Map<String, String> context = MDC.getCopyOfContextMap();
return () -> {
Map<String, String> previous = MDC.getCopyOfContextMap();
try {
if (context != null) {
MDC.setContextMap(context);
} else {
MDC.clear();
}
return callable.call();
} finally {
if (previous != null) {
MDC.setContextMap(previous);
} else {
MDC.clear();
}
}
};
}
}

Structured Logger Wrapper

@Component
public class StructuredLogger {
private final Logger logger;
public StructuredLogger(Class<?> clazz) {
this.logger = LoggerFactory.getLogger(clazz);
}
public void info(String message, Object... args) {
if (logger.isInfoEnabled()) {
logger.info(message, args);
}
}
public void info(String message, Map<String, Object> context) {
if (logger.isInfoEnabled()) {
withContext(context, () -> logger.info(message));
}
}
public void error(String message, Throwable throwable, Map<String, Object> context) {
if (logger.isErrorEnabled()) {
withContext(context, () -> logger.error(message, throwable));
}
}
public void warn(String message, Map<String, Object> context) {
if (logger.isWarnEnabled()) {
withContext(context, () -> logger.warn(message));
}
}
public void debug(String message, Map<String, Object> context) {
if (logger.isDebugEnabled()) {
withContext(context, () -> logger.debug(message));
}
}
public void businessEvent(String eventType, String eventName, Map<String, Object> eventData) {
Map<String, Object> context = new HashMap<>();
context.put("event_type", eventType);
context.put("event_name", eventName);
context.put("event_data", eventData);
context.put("timestamp", Instant.now().toString());
withContext(context, () -> logger.info("Business event: {}", eventName));
}
public void performanceMetric(String operation, long durationMs, Map<String, Object> metadata) {
Map<String, Object> context = new HashMap<>();
context.put("metric_type", "performance");
context.put("operation", operation);
context.put("duration_ms", durationMs);
context.putAll(metadata);
withContext(context, () -> logger.info("Performance metric: {} took {}ms", operation, durationMs));
}
private void withContext(Map<String, Object> context, Runnable loggingAction) {
Map<String, String> previousContext = MDC.getCopyOfContextMap();
try {
// Convert all context values to strings for MDC
context.forEach((key, value) -> 
MDC.put(key, value != null ? value.toString() : "null"));
loggingAction.run();
} finally {
if (previousContext != null) {
MDC.setContextMap(previousContext);
} else {
MDC.clear();
}
}
}
}

Spring Boot Integration

Logging Configuration Properties

@ConfigurationProperties(prefix = "app.logging")
public class LoggingProperties {
private String level = "INFO";
private String path = "./logs";
private Logstash logstash = new Logstash();
private Metrics metrics = new Metrics();
public static class Logstash {
private boolean enabled = true;
private String host = "localhost";
private int port = 5000;
private int queueSize = 8192;
private String customFields = "{}";
// Getters and setters
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public int getQueueSize() { return queueSize; }
public void setQueueSize(int queueSize) { this.queueSize = queueSize; }
public String getCustomFields() { return customFields; }
public void setCustomFields(String customFields) { this.customFields = customFields; }
}
public static class Metrics {
private boolean enabled = true;
private long slowThresholdMs = 1000;
private boolean logSql = false;
// Getters and setters
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public long getSlowThresholdMs() { return slowThresholdMs; }
public void setSlowThresholdMs(long slowThresholdMs) { this.slowThresholdMs = slowThresholdMs; }
public boolean isLogSql() { return logSql; }
public void setLogSql(boolean logSql) { this.logSql = logSql; }
}
// Getters and setters for main class
public String getLevel() { return level; }
public void setLevel(String level) { this.level = level; }
public String getPath() { return path; }
public void setPath(String path) { this.path = path; }
public Logstash getLogstash() { return logstash; }
public void setLogstash(Logstash logstash) { this.logstash = logstash; }
public Metrics getMetrics() { return metrics; }
public void setMetrics(Metrics metrics) { this.metrics = metrics; }
}

Spring Boot Auto-Configuration

@Configuration
@EnableConfigurationProperties(LoggingProperties.class)
@ConditionalOnClass(LogstashEncoder.class)
public class LogstashAutoConfiguration {
@Autowired
private LoggingProperties loggingProperties;
@Bean
@ConditionalOnProperty(name = "app.logging.logstash.enabled", havingValue = "true")
public LogstashTcpSocketAppender logstashAppender() {
LogstashTcpSocketAppender appender = new LogstashTcpSocketAppender();
appender.addDestination(
String.format("%s:%d", 
loggingProperties.getLogstash().getHost(), 
loggingProperties.getLogstash().getPort())
);
appender.setEncoder(logstashEncoder());
appender.setReconnectionDelay(new Duration(10000)); // 10 seconds
return appender;
}
@Bean
public LogstashEncoder logstashEncoder() {
LogstashEncoder encoder = new LogstashEncoder();
encoder.setCustomFields(loggingProperties.getLogstash().getCustomFields());
encoder.setIncludeMdc(true);
encoder.setIncludeCallerData(true);
// Custom field names for better Elasticsearch mapping
LogstashEncoder.FieldNames fieldNames = new LogstashEncoder.FieldNames();
fieldNames.setTimestamp("@timestamp");
fieldNames.setLevel("level");
fieldNames.setThread("thread_name");
fieldNames.setLogger("logger_name");
fieldNames.setMessage("message");
fieldNames.setStackTrace("exception");
fieldNames.setMdc("context");
encoder.setFieldNames(fieldNames);
return encoder;
}
@Bean
public StructuredLogger structuredLogger() {
return new StructuredLogger(StructuredLogger.class);
}
@Bean
public LoggingAspect loggingAspect(StructuredLogger logger) {
return new LoggingAspect(logger, loggingProperties);
}
}

Aspect-Oriented Logging

Logging Aspect for Method Tracing

@Aspect
@Component
public class LoggingAspect {
private final StructuredLogger logger;
private final LoggingProperties properties;
public LoggingAspect(StructuredLogger logger, LoggingProperties properties) {
this.logger = logger;
this.properties = properties;
}
@Around("@annotation(LogExecution)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Map<String, Object> context = new HashMap<>();
context.put("class", className);
context.put("method", methodName);
context.put("operation", "method_execution");
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
context.put("duration_ms", duration);
context.put("status", "success");
// Log slow methods
if (duration > properties.getMetrics().getSlowThresholdMs()) {
context.put("slow", true);
logger.warn("Slow method execution detected", context);
} else {
logger.debug("Method executed successfully", context);
}
return result;
} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
context.put("duration_ms", duration);
context.put("status", "error");
context.put("error_type", e.getClass().getSimpleName());
context.put("error_message", e.getMessage());
logger.error("Method execution failed", e, context);
throw e;
}
}
@Around("execution(* com.myapp.service.*.*(..))")
public Object logServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {
return logExecutionTime(joinPoint);
}
@Around("execution(* com.myapp.repository.*.*(..))")
public Object logRepositoryMethods(ProceedingJoinPoint joinPoint) throws Throwable {
if (properties.getMetrics().isLogSql()) {
return logExecutionTime(joinPoint);
}
return joinPoint.proceed();
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecution {
String value() default "";
boolean logParameters() default false;
boolean logResult() default false;
}

HTTP Request Logging

Request/Response Logging Filter

@Component
public class RequestResponseLoggingFilter implements Filter {
private final StructuredLogger logger = new StructuredLogger(RequestResponseLoggingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
chain.doFilter(request, response);
return;
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
long startTime = System.currentTimeMillis();
// Capture request details
String requestId = UUID.randomUUID().toString();
String traceId = httpRequest.getHeader("X-Trace-Id");
String spanId = httpRequest.getHeader("X-Span-Id");
// Set MDC context
MDCContextManager.putTraceContext(
traceId != null ? traceId : requestId, 
spanId != null ? spanId : requestId
);
MDCContextManager.putRequestContext(
httpRequest.getRequestURI(),
httpRequest.getMethod(),
httpRequest.getRemoteAddr()
);
// Log request
Map<String, Object> requestContext = new HashMap<>();
requestContext.put("request_id", requestId);
requestContext.put("http_method", httpRequest.getMethod());
requestContext.put("request_uri", httpRequest.getRequestURI());
requestContext.put("query_string", httpRequest.getQueryString());
requestContext.put("client_ip", httpRequest.getRemoteAddr());
requestContext.put("user_agent", httpRequest.getHeader("User-Agent"));
requestContext.put("content_type", httpRequest.getContentType());
requestContext.put("content_length", httpRequest.getContentLength());
logger.info("HTTP Request received", requestContext);
try {
chain.doFilter(request, response);
// Log response
long duration = System.currentTimeMillis() - startTime;
Map<String, Object> responseContext = new HashMap<>();
responseContext.put("request_id", requestId);
responseContext.put("http_status", httpResponse.getStatus());
responseContext.put("duration_ms", duration);
responseContext.put("response_size", httpResponse.getBufferSize());
if (httpResponse.getStatus() >= 400) {
logger.warn("HTTP Request completed with error", responseContext);
} else {
logger.info("HTTP Request completed successfully", responseContext);
}
} catch (Exception e) {
// Log error
long duration = System.currentTimeMillis() - startTime;
Map<String, Object> errorContext = new HashMap<>();
errorContext.put("request_id", requestId);
errorContext.put("duration_ms", duration);
errorContext.put("error_type", e.getClass().getSimpleName());
errorContext.put("error_message", e.getMessage());
logger.error("HTTP Request failed", e, errorContext);
throw e;
} finally {
MDCContextManager.clear();
}
}
}

Custom Log Event Types

Business Event Logging

@Component
public class BusinessEventLogger {
private final StructuredLogger logger;
public BusinessEventLogger() {
this.logger = new StructuredLogger(BusinessEventLogger.class);
}
public void logUserLogin(String userId, String sessionId, boolean success, String reason) {
Map<String, Object> eventData = new HashMap<>();
eventData.put("user_id", userId);
eventData.put("session_id", sessionId);
eventData.put("success", success);
eventData.put("reason", reason);
eventData.put("ip_address", getClientIp());
logger.businessEvent("authentication", "user_login", eventData);
}
public void logOrderCreated(String orderId, String customerId, BigDecimal amount, String currency) {
Map<String, Object> eventData = new HashMap<>();
eventData.put("order_id", orderId);
eventData.put("customer_id", customerId);
eventData.put("amount", amount);
eventData.put("currency", currency);
eventData.put("timestamp", Instant.now().toString());
logger.businessEvent("order", "order_created", eventData);
}
public void logPaymentProcessed(String paymentId, String orderId, String status, String gateway) {
Map<String, Object> eventData = new HashMap<>();
eventData.put("payment_id", paymentId);
eventData.put("order_id", orderId);
eventData.put("status", status);
eventData.put("gateway", gateway);
eventData.put("timestamp", Instant.now().toString());
logger.businessEvent("payment", "payment_processed", eventData);
}
public void logSystemHealth(String component, String status, String message, Map<String, Object> metrics) {
Map<String, Object> eventData = new HashMap<>();
eventData.put("component", component);
eventData.put("status", status);
eventData.put("message", message);
eventData.put("metrics", metrics);
eventData.put("timestamp", Instant.now().toString());
logger.businessEvent("system", "health_check", eventData);
}
private String getClientIp() {
// Implementation to get client IP
return "unknown";
}
}

Testing Logstash Configuration

Test Configuration

@SpringBootTest
@TestPropertySource(properties = {
"app.logging.level=DEBUG",
"app.logging.logstash.enabled=false",
"app.logging.metrics.slow-threshold-ms=50"
})
@Slf4j
public class LogstashEncoderTest {
@Autowired
private StructuredLogger structuredLogger;
@Autowired
private BusinessEventLogger businessEventLogger;
@Test
public void testStructuredLogging() {
Map<String, Object> context = new HashMap<>();
context.put("user_id", "test-user-123");
context.put("action", "test_action");
context.put("result", "success");
structuredLogger.info("Test structured log message", context);
// Verify log output (you might need to capture log output)
}
@Test
public void testBusinessEventLogging() {
businessEventLogger.logUserLogin("user123", "session456", true, "successful_login");
// Verify business event was logged
}
@Test
public void testMDCContextPropagation() {
MDCContextManager.putTraceContext("test-trace-123", "test-span-456");
MDCContextManager.putUserContext("test-user", "test-session");
try {
structuredLogger.info("Test message with MDC context", new HashMap<>());
} finally {
MDCContextManager.clear();
}
}
@Test
public void testAsyncMDCPropagation() throws Exception {
MDCContextManager.putTraceContext("async-trace", "async-span");
CompletableFuture<String> future = CompletableFuture.supplyAsync(
MDCContextManager.wrap(() -> {
String traceId = MDCContextManager.getTraceId();
structuredLogger.info("Async log message", Map.of("async", true));
return traceId;
})
);
String result = future.get();
assertEquals("async-trace", result);
}
}

Application Properties

application.yml

app:
logging:
level: INFO
path: ./logs
logstash:
enabled: true
host: localhost
port: 5000
queue-size: 8192
custom-fields: '{"app":"my-service","env":"${spring.profiles.active:development}","version":"1.0.0"}'
metrics:
enabled: true
slow-threshold-ms: 1000
log-sql: false
spring:
application:
name: my-service
profiles:
active: development
# Logback configuration
logging:
config: classpath:logback-spring.xml
level:
com.myapp: DEBUG
org.springframework: INFO
org.hibernate: WARN

Conclusion

The Logstash Logback Encoder provides:

  1. Structured JSON Logging - Machine-readable log format for ELK stack
  2. Rich Context - MDC integration for request tracing and business context
  3. Flexible Configuration - Multiple appenders and encoder configurations
  4. Performance - Async logging with configurable batching
  5. Integration - Spring Boot auto-configuration and custom aspects
  6. Business Events - Structured business event logging

This setup enables comprehensive observability with searchable, analyzable logs that include full context for debugging, monitoring, and business intelligence.

Leave a Reply

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


Macro Nepal Helper