Spring Boot Yapılandırılmış Loglama 2026: Logback ve OpenTelemetry ile Üretim JSON Logları

Spring Boot 4.x yapılandırılmış loglama rehberi. Yerel JSON desteği, OpenTelemetry starter, MDC izleme ve üretim gözlemlenebilirliği için ELK Stack entegrasyonu.

Spring Boot'ta Logback ve JSON ile yapılandırılmış loglama

Geleneksel metin tabanlı loglar üretim ortamında hızla yönetilemez hale gelir. Saniyede binlerce satır üreten yüzlerce örnek arasında belirli bir hatayı bulmak kâbusa dönüşür. JSON biçimindeki yapılandırılmış loglar bu durumu kökten değiştirir ve her olayı sorgulanabilir, otomatik olarak analiz edilebilir kılar.

Anahtar Nokta

Spring Boot 4.x (Spring Framework 7 üzerine kurulu) ECS, Logstash ve GELF formatlarıyla yapılandırılmış JSON loglamayı doğal olarak destekler. Yeni spring-boot-starter-opentelemetry harici bağımlılıklar olmadan birleşik gözlemlenebilirlik sağlar.

Spring Boot'ta Yapılandırılmış Loglamayı Neden Kullanmalı

Geleneksel Metin Loglarının Sınırları

Tipik bir metin logu şöyle görünür:

text
2026-08-22 10:15:32.456 INFO  [order-service,abc123] c.e.s.OrderService - Order created for user john@example.com, amount: 150.00€, items: 3

Bu biçim üretimde birçok soruna yol açar. Belirli bilgileri çıkarmak karmaşık ve kırılgan düzenli ifadeler gerektirir. Servisler arası ilişkilendirme her ekibin farklı yorumladığı katı kurallar dayatır. Elasticsearch gibi analiz araçları bu yapılandırılmamış dizeleri verimli şekilde indekslemekte zorlanır.

JSON Biçiminin Avantajları

Aynı olay JSON biçiminde anında kullanılabilir hale gelir:

json
{
  "@timestamp": "2026-08-22T10:15:32.456Z",
  "level": "INFO",
  "logger": "com.example.service.OrderService",
  "message": "Order created",
  "service": "order-service",
  "traceId": "abc123",
  "userId": "john@example.com",
  "orderId": "ORD-789456",
  "amount": 150.00,
  "currency": "EUR",
  "itemCount": 3
}

Her alan filtrelenebilir ve toplanabilir hale gelir. Bir Elasticsearch sorgusu son on beş dakikadaki 100 Euro üzerindeki tüm siparişleri anında bulur. Kibana panoları manuel ayrıştırma yapmadan eğilimleri görselleştirir. Bu konu Spring Boot mülakat soruları açısından özellikle önemlidir; üretim gözlemlenebilirlik kalıplarını anlamak kıdemli adayları ayırt eder.

Spring Boot 4.x Yapılandırılmış Loglama Yapılandırması

Yapılandırılmış JSON Loglarını Etkinleştirme

Spring Framework 7 üzerine kurulu Spring Boot 4.x, logging.structured özelliği aracılığıyla olgun yapılandırılmış loglama desteği sunar. Bu yaklaşım ek bağımlılık gerektirmez ve doğrudan Logback 1.5.38 ile entegre olur.

yaml
# application.yml
# Native structured logging configuration for Spring Boot 4.x
logging:
  structured:
    # Output format: ecs (Elastic), logstash, gelf
    format:
      console: ecs
      file: ecs
  file:
    name: /var/log/app/application.log
  level:
    root: INFO
    com.example: DEBUG

ECS (Elastic Common Schema) biçimi ek yapılandırma olmadan Elasticsearch ve Kibana ile doğrudan uyumluluk sağlar.

JSON Alanlarını Özelleştirme

Her loga iş alanları eklemek için Spring Boot ek özniteliklerin yapılandırılmasına izin verir.

yaml
# application.yml
# Custom fields in structured logs
logging:
  structured:
    format:
      console: ecs
    ecs:
      # Service information added to every log
      service:
        name: ${spring.application.name}
        version: ${app.version:1.0.0}
        environment: ${spring.profiles.active:default}
        node-name: ${HOSTNAME:unknown}
LoggingConfig.javajava
// Programmatic configuration for additional fields
package com.example.logging.config;

import org.springframework.boot.logging.structured.StructuredLogFormatterCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LoggingConfig {

    @Bean
    StructuredLogFormatterCustomizer<EcsStructuredLogFormatter> ecsCustomizer() {
        return formatter -> formatter
            // Adds static fields to all logs
            .addStaticField("team", "backend")
            .addStaticField("region", System.getenv("AWS_REGION"))
            // Customizes exception formatting
            .setIncludeStacktrace(true)
            .setStacktraceMaxLength(5000);
    }
}

Bu alanlar her log satırında görünür ve panolarda ekibe veya bölgeye göre filtrelemeyi kolaylaştırır.

Tam Gözlemlenebilirlik için OpenTelemetry Starter

Spring Boot 4'teki Yeni Standart

Spring Boot 4.0, daha önce var olan karmaşık çoklu bağımlılık kurulumunu değiştiren spring-boot-starter-opentelemetry paketini tanıttı. Bu tek bağımlılık, traces, metrikler ve log korelasyonu dahil olmak üzere satıcıdan bağımsız gözlemlenebilirlik sağlar.

xml
<!-- pom.xml -->
<!-- OpenTelemetry starter for Spring Boot 4.x -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-opentelemetry</artifactId>
</dependency>
<!-- Log correlation for Logback -->
<dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-logback-appender-1.0</artifactId>
    <version>2.21.0-alpha</version>
</dependency>

Bu starter OpenTelemetry API, Micrometer tracing köprüsü ve OTLP exporterları içerir. Spring Cloud Sleuth artık legacy olarak kabul edilir, dağıtık izleme için endüstri standardı OpenTelemetry'dir.

OpenTelemetry'yi Yapılandırılmış Loglarla Yapılandırma

yaml
# application.yml
# OpenTelemetry configuration with structured logging
spring:
  application:
    name: order-service
    group: commerce

otel:
  exporter:
    otlp:
      endpoint: http://otel-collector:4317
      protocol: grpc
  resource:
    attributes:
      service.namespace: production
      deployment.environment: prod

logging:
  structured:
    format:
      console: ecs
  pattern:
    level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"

Trace ve span kimlikleri otomatik olarak MDC'ye enjekte edilir ve logları servisler arası dağıtık trace'lerle ilişkilendirir. Üretim izleme kurulumları hakkında daha fazla bilgi için Spring Boot Actuator, Micrometer ve Prometheus rehberi incelenebilir.

Kullanımdan Kaldırma Bildirimi

ZipkinWithOpenTelemetryTracingAutoConfiguration kullanımdan kaldırıldı olarak işaretlenmiştir ve Spring Boot 4.2'de kaldırılması planlanmaktadır. Gelecek uyumluluk için yerel OTLP exporter'lara geçiş önerilir.

JSON Encoder ile Klasik Logback Yapılandırması

Gelişmiş Özelleştirme için Logstash Encoder

Gelişmiş özelleştirme ihtiyaçları veya eski Spring Boot sürümlerinden geçiş için Logstash Logback Encoder 9.0 hâlâ kullanılabilir. 9.0 sürümünün Jackson 3.0 ve minimum Java 17 gerektirdiği unutulmamalıdır.

xml
<!-- pom.xml -->
<!-- Dependency for JSON logging with Logback (Jackson 3 required) -->
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>9.0</version>
</dependency>

Tam Logback Yapılandırması

logback-spring.xml dosyası çıktı biçimi üzerinde tam kontrol sağlar.

xml
<!-- src/main/resources/logback-spring.xml -->
<!-- Logback configuration for structured JSON logs -->
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- Spring Boot properties -->
    <springProperty scope="context" name="appName" source="spring.application.name" defaultValue="app"/>
    <springProperty scope="context" name="appVersion" source="app.version" defaultValue="1.0.0"/>

    <!-- JSON console appender for production -->
    <appender name="JSON_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <!-- Custom fields added to every log -->
            <customFields>{"service":"${appName}","version":"${appVersion}"}</customFields>
            <!-- Includes MDC (tracing context) -->
            <includeMdcKeyName>traceId</includeMdcKeyName>
            <includeMdcKeyName>spanId</includeMdcKeyName>
            <includeMdcKeyName>userId</includeMdcKeyName>
            <includeMdcKeyName>requestId</includeMdcKeyName>
            <!-- ISO8601 timestamp format -->
            <timestampPattern>yyyy-MM-dd'T'HH:mm:ss.SSSZ</timestampPattern>
            <!-- Complete stack traces -->
            <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
                <maxDepthPerThrowable>30</maxDepthPerThrowable>
                <maxLength>4096</maxLength>
                <shortenedClassNameLength>36</shortenedClassNameLength>
                <rootCauseFirst>true</rootCauseFirst>
            </throwableConverter>
        </encoder>
    </appender>

    <!-- Rolling JSON file appender -->
    <appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/var/log/${appName}/application.json</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/var/log/${appName}/application.%d{yyyy-MM-dd}.%i.json.gz</fileNamePattern>
            <maxHistory>30</maxHistory>
            <maxFileSize>100MB</maxFileSize>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <customFields>{"service":"${appName}","version":"${appVersion}"}</customFields>
        </encoder>
    </appender>

    <!-- Text appender for development -->
    <appender name="TEXT_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger{36}) - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Activation by Spring profile -->
    <springProfile name="prod,staging">
        <root level="INFO">
            <appender-ref ref="JSON_CONSOLE"/>
            <appender-ref ref="JSON_FILE"/>
        </root>
    </springProfile>

    <springProfile name="dev,local">
        <root level="DEBUG">
            <appender-ref ref="TEXT_CONSOLE"/>
        </root>
    </springProfile>
</configuration>

Bu yapılandırma JSON loglarını yalnızca üretimde etkinleştirirken geliştirme ortamında okunabilir loglar bırakır.

Dağıtık İzleme için MDC

Trace Bağlamı Yayılımı

MDC (Mapped Diagnostic Context) her logu istek veya trace tanımlayıcıları gibi bağlam bilgileriyle zenginleştirir.

TracingFilter.javajava
// Filter for automatic trace context injection
package com.example.logging.filter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.UUID;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class TracingFilter extends OncePerRequestFilter {

    // Standard MDC keys for tracing
    private static final String TRACE_ID_KEY = "traceId";
    private static final String SPAN_ID_KEY = "spanId";
    private static final String REQUEST_ID_KEY = "requestId";
    private static final String USER_ID_KEY = "userId";

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {

        try {
            // Retrieve or generate trace identifiers
            String traceId = extractOrGenerate(request, "X-Trace-Id", TRACE_ID_KEY);
            String spanId = generateSpanId();
            String requestId = extractOrGenerate(request, "X-Request-Id", REQUEST_ID_KEY);
            String userId = request.getHeader("X-User-Id");

            // Inject into MDC to appear in all logs
            MDC.put(TRACE_ID_KEY, traceId);
            MDC.put(SPAN_ID_KEY, spanId);
            MDC.put(REQUEST_ID_KEY, requestId);
            if (userId != null) {
                MDC.put(USER_ID_KEY, userId);
            }

            // Propagate to responses for inter-service chaining
            response.setHeader("X-Trace-Id", traceId);
            response.setHeader("X-Request-Id", requestId);

            filterChain.doFilter(request, response);

        } finally {
            // Clean MDC after each request
            MDC.clear();
        }
    }

    private String extractOrGenerate(HttpServletRequest request, String header, String key) {
        String value = request.getHeader(header);
        return value != null ? value : UUID.randomUUID().toString().replace("-", "").substring(0, 16);
    }

    private String generateSpanId() {
        return UUID.randomUUID().toString().replace("-", "").substring(0, 8);
    }
}

İstek işleme sırasında üretilen her log otomatik olarak bu tanımlayıcıları içerir.

İş Kodunda MDC Kullanımı

OrderService.javajava
// Business service with enriched contextual logging
package com.example.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private static final Logger log = LoggerFactory.getLogger(OrderService.class);

    public Order createOrder(CreateOrderRequest request) {
        // Add business information to MDC context
        MDC.put("orderId", request.getOrderId());
        MDC.put("customerId", request.getCustomerId());

        try {
            log.info("Creating order with {} items", request.getItems().size());

            // Business logic...
            Order order = processOrder(request);

            log.info("Order created successfully, total: {} {}",
                order.getTotal(), order.getCurrency());

            return order;

        } catch (Exception e) {
            // Exception appears with full MDC context
            log.error("Failed to create order", e);
            throw e;
        } finally {
            // Clean business keys added
            MDC.remove("orderId");
            MDC.remove("customerId");
        }
    }
}

Elde edilen JSON log, hata ayıklama için gereken tüm bilgileri içerir:

json
{
  "@timestamp": "2026-08-22T10:15:32.456Z",
  "level": "INFO",
  "logger": "com.example.service.OrderService",
  "message": "Order created successfully, total: 150.00 EUR",
  "traceId": "a1b2c3d4e5f67890",
  "spanId": "12345678",
  "requestId": "req-abc-123",
  "userId": "user-456",
  "orderId": "ORD-789",
  "customerId": "CUST-321"
}

Spring Boot mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Performans için Asenkron Loglama

Thread Havuzu Yapılandırması

Üretimde senkron log yazımları isteklerin gecikmesini etkiler. Asenkron appender, loglamayı ana iş parçacığından ayırır.

xml
<!-- logback-spring.xml -->
<!-- High-performance asynchronous appender configuration -->
<appender name="ASYNC_JSON" class="ch.qos.logback.classic.AsyncAppender">
    <!-- Pending log buffer size -->
    <queueSize>1024</queueSize>
    <!-- Never block the calling thread -->
    <neverBlock>true</neverBlock>
    <!-- Threshold before dropping DEBUG/TRACE logs -->
    <discardingThreshold>20</discardingThreshold>
    <!-- Include caller information (expensive) -->
    <includeCallerData>false</includeCallerData>
    <!-- Actual appender for writing -->
    <appender-ref ref="JSON_FILE"/>
</appender>

<springProfile name="prod">
    <root level="INFO">
        <appender-ref ref="ASYNC_JSON"/>
    </root>
</springProfile>
Spring Boot 4.1.1'deki Kritik Hata Düzeltmesi

Spring Boot 4.1.1, başarısız bir JSON kodlamasının aynı iş parçacığındaki sonraki log olayını bozabildiği kritik bir sorunu düzeltti (#51371). Sessiz log bozulmasından kaçınmak için 4.0.x veya 4.1.0'dan yükseltme yapılması önerilir.

Loglama Sistemi Metrikleri

Loglama sisteminin kendisini izlemek sessiz log kayıplarını önler.

LoggingMetrics.javajava
// Exposing Logback metrics via Micrometer
package com.example.logging.metrics;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.classic.AsyncAppender;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import java.util.Iterator;

@Component
public class LoggingMetrics {

    private final MeterRegistry registry;

    public LoggingMetrics(MeterRegistry registry) {
        this.registry = registry;
    }

    @PostConstruct
    void registerMetrics() {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME);

        // Iterate through appenders to find AsyncAppenders
        Iterator<Appender<ILoggingEvent>> it = rootLogger.iteratorForAppenders();
        while (it.hasNext()) {
            Appender<ILoggingEvent> appender = it.next();
            if (appender instanceof AsyncAppender asyncAppender) {
                registerAsyncMetrics(asyncAppender);
            }
        }
    }

    private void registerAsyncMetrics(AsyncAppender appender) {
        String appenderName = appender.getName();

        // Current queue size
        Gauge.builder("logback.async.queue.size", appender, AsyncAppender::getQueueSize)
            .tag("appender", appenderName)
            .description("Current async appender queue size")
            .register(registry);

        // Remaining capacity
        Gauge.builder("logback.async.queue.remaining", appender, AsyncAppender::getRemainingCapacity)
            .tag("appender", appenderName)
            .description("Remaining capacity in async queue")
            .register(registry);

        // Number of dropped logs
        Gauge.builder("logback.async.discarded", appender, AsyncAppender::getNumberOfElementsInQueue)
            .tag("appender", appenderName)
            .description("Number of discarded log events")
            .register(registry);
    }
}

logback.async.queue.remaining < 100 üzerinde bir Prometheus alarmı, log kaybı riskini önceden bildirir.

ELK Stack Entegrasyonu

Filebeat Yapılandırması

Filebeat, JSON dosyalarını toplar ve dönüşüm uygulamadan Elasticsearch'e gönderir.

yaml
# filebeat.yml
# Filebeat configuration for Spring Boot JSON logs
filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/log/*/application.json
    # Automatic JSON parsing
    json:
      keys_under_root: true
      overwrite_keys: true
      add_error_key: true
      message_key: message

processors:
  # Add Kubernetes metadata if available
  - add_kubernetes_metadata:
      host: ${NODE_NAME}
      matchers:
        - logs_path:
            logs_path: "/var/log/containers/"
  # Parse timestamp
  - timestamp:
      field: "@timestamp"
      layouts:
        - '2006-01-02T15:04:05.000Z'
        - '2006-01-02T15:04:05.000-07:00'
      test:
        - '2026-08-22T10:15:32.456Z'

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
  index: "logs-%{[service]}-%{+yyyy.MM.dd}"
  pipeline: "spring-boot-logs"

setup.template:
  name: "logs"
  pattern: "logs-*"

Zenginleştirme için Elasticsearch Pipeline

json
{
  "description": "Spring Boot logs enrichment",
  "processors": [
    {
      "geoip": {
        "field": "client.ip",
        "target_field": "client.geo",
        "ignore_missing": true
      }
    },
    {
      "user_agent": {
        "field": "user_agent.original",
        "target_field": "user_agent",
        "ignore_missing": true
      }
    },
    {
      "set": {
        "field": "event.ingested",
        "value": "{{_ingest.timestamp}}"
      }
    },
    {
      "script": {
        "description": "Classify log level severity",
        "source": "def level = ctx.level; if (level == 'ERROR') ctx.severity = 4; else if (level == 'WARN') ctx.severity = 3; else if (level == 'INFO') ctx.severity = 2; else ctx.severity = 1;"
      }
    }
  ]
}

Spring Boot Loglaması için Üretim En İyi Uygulamaları

Sistematik Olarak Dahil Edilecek Bilgiler

Her log, hata ayıklama ve ilişkilendirme için minimum bilgi içermelidir.

StructuredLogger.javajava
// Helper for consistent structured logs
package com.example.logging;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import java.util.Map;
import java.util.function.Supplier;

public final class StructuredLogger {

    private final Logger delegate;

    private StructuredLogger(Class<?> clazz) {
        this.delegate = LoggerFactory.getLogger(clazz);
    }

    public static StructuredLogger getLogger(Class<?> clazz) {
        return new StructuredLogger(clazz);
    }

    // Log with temporary business context
    public void info(String message, Map<String, String> context) {
        try {
            context.forEach(MDC::put);
            delegate.info(message);
        } finally {
            context.keySet().forEach(MDC::remove);
        }
    }

    // Log with supplier for lazy evaluation
    public void debug(Supplier<String> messageSupplier, Map<String, String> context) {
        if (delegate.isDebugEnabled()) {
            try {
                context.forEach(MDC::put);
                delegate.debug(messageSupplier.get());
            } finally {
                context.keySet().forEach(MDC::remove);
            }
        }
    }

    // Error log with full context
    public void error(String message, Throwable t, Map<String, String> context) {
        try {
            context.forEach(MDC::put);
            delegate.error(message, t);
        } finally {
            context.keySet().forEach(MDC::remove);
        }
    }
}
java
// Usage in business code
private static final StructuredLogger log = StructuredLogger.getLogger(PaymentService.class);

public void processPayment(Payment payment) {
    log.info("Processing payment", Map.of(
        "paymentId", payment.getId(),
        "amount", String.valueOf(payment.getAmount()),
        "currency", payment.getCurrency(),
        "method", payment.getMethod().name()
    ));
}

Hariç Tutulması Gereken Hassas Bilgiler

Loglar asla kişisel veya hassas verileri içermemelidir.

SensitiveDataFilter.javajava
// Sensitive data masking utility
package com.example.logging.filter;

import java.util.regex.Pattern;

public final class SensitiveDataFilter {

    // Sensitive data patterns to mask
    private static final Pattern EMAIL_PATTERN =
        Pattern.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
    private static final Pattern CREDIT_CARD_PATTERN =
        Pattern.compile("\\b\\d{4}[- ]?\\d{4}[- ]?\\d{4}[- ]?\\d{4}\\b");
    private static final Pattern PASSWORD_PATTERN =
        Pattern.compile("(?i)(password|pwd|secret|token)[\"']?\\s*[:=]\\s*[\"']?[^\\s,}\"']+");

    private SensitiveDataFilter() {}

    // Utility method to mask data
    public static String maskSensitiveData(String input) {
        if (input == null) return null;

        String result = input;
        result = EMAIL_PATTERN.matcher(result).replaceAll("[EMAIL_MASKED]");
        result = CREDIT_CARD_PATTERN.matcher(result).replaceAll("[CARD_MASKED]");
        result = PASSWORD_PATTERN.matcher(result).replaceAll("$1=[REDACTED]");

        return result;
    }
}

Uygun Log Seviyeleri

SeviyeKullanım DurumuÖrnekler
ERRORMüdahale gerektiren arızaKurtarılamaz istisnalar, kritik işlem hataları, harici servis erişilememesi
WARNAnormal ama işlenmiş durumYeniden deneme devam ediyor, performans düşüşü, limit yaklaşan kaynaklar
INFOÖnemli iş olaylarıİşlem başlangıç/bitiş, önemli durum değişiklikleri, anahtar kullanıcı eylemleri
DEBUGTanılama bilgisiYürütme detayları, önemli değişken değerleri, dallanma kararları
TRACEÇok ince detaylarMetot giriş/çıkış, tam nesne içerikleri, döngüler ve iterasyonlar

Yapılandırılmış Logları Test Etme ve Doğrulama

JSON Yapısı için Birim Testleri

StructuredLoggingTest.javajava
// Structured log validation tests
package com.example.logging;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import static org.assertj.core.api.Assertions.assertThat;

class StructuredLoggingTest {

    private ListAppender<ILoggingEvent> listAppender;
    private Logger logger;

    @BeforeEach
    void setUp() {
        logger = (Logger) LoggerFactory.getLogger(StructuredLoggingTest.class);
        listAppender = new ListAppender<>();
        listAppender.start();
        logger.addAppender(listAppender);
    }

    @Test
    void shouldIncludeMdcFieldsInLog() {
        // Given
        MDC.put("traceId", "test-trace-123");
        MDC.put("userId", "user-456");

        // When
        logger.info("Test message with MDC context");

        // Then
        ILoggingEvent event = listAppender.list.get(0);
        assertThat(event.getMDCPropertyMap())
            .containsEntry("traceId", "test-trace-123")
            .containsEntry("userId", "user-456");

        MDC.clear();
    }

    @Test
    void shouldLogExceptionWithStackTrace() {
        // Given
        Exception testException = new RuntimeException("Test error");

        // When
        logger.error("Operation failed", testException);

        // Then
        ILoggingEvent event = listAppender.list.get(0);
        assertThat(event.getThrowableProxy()).isNotNull();
        assertThat(event.getThrowableProxy().getMessage()).isEqualTo("Test error");
    }
}

Yapılandırılmış loglamayla uyumlu entegrasyon test kalıpları için Testcontainers Spring Boot entegrasyon testi rehberi incelenebilir.

Kaynaklar

Spring Boot 4.x için Yapılandırılmış Loglama Kontrol Listesi

  • ECS, Logstash veya GELF formatıyla yerel yapılandırılmış loglama yalnızca logging.structured.format yapılandırması gerektirir
  • spring-boot-starter-opentelemetry eski Sleuth kurulumlarını satıcıdan bağımsız gözlemlenebilirlikle değiştirir
  • MDC, trace tanımlayıcılarını servisler arasında otomatik olarak yayar
  • neverBlock=true ile asenkron appender'lar, loglamanın istek gecikmesini etkilemesini önler
  • Logstash Logback Encoder 9.0, Jackson 3.0 ve Java 17 gerektirir
  • Spring Boot 4.1.1, eşzamanlı iş parçacıklarını etkileyen kritik bir JSON kodlama hatasını düzeltti
  • Hassas veri maskeleme, üretim loglarında GDPR/KVKK uyumluluğunu sağlar
  • Asenkron kuyruk kapasitesi metrikleri, log kaybı öncesi uyarı verilmesini mümkün kılar

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Günün meydan okuması

Spring Boot kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

22 Ağustos 2026 tarihinde güncellendi

Etiketler

#spring boot logging
#logback json
#structured logs
#elk stack
#observability

Paylaş

İlgili makaleler