Spring Boot Basic – Tag 5 von 10
Von Elyndra Valen, Senior Entwicklerin bei Java Fleet Systems Consulting


Konfiguration

📍 Deine Position im Kurs

TagThemaStatus
✅ 1Erste REST APIAbgeschlossen
✅ 2Spring Container & DIAbgeschlossen
✅ 3@Controller & Thymeleaf BasicsAbgeschlossen
✅ 4Thymeleaf Forms & MVC-PatternAbgeschlossen
→ 5Konfiguration & Logging👉 DU BIST HIER!
6DI & AOP im DetailNoch nicht freigeschaltet
7Scopes in SpringNoch nicht freigeschaltet
8WebSocketsNoch nicht freigeschaltet
9JAX-RS in Spring BootNoch nicht freigeschaltet
10Integration & AbschlussNoch nicht freigeschaltet

Modul: Spring Boot Basic (10 Arbeitstage)
Dein Ziel: Professionelle Konfiguration & sinnvolles Logging beherrschen!


📋 Voraussetzungen

Du brauchst:

  • ✅ Tag 1-4 abgeschlossen
  • ✅ PersonViewController & PersonService funktionieren
  • ✅ Grundverständnis von Spring Boot

Tag 4 verpasst?Hier geht’s zum Blogbeitrag Tag 4


⚡ Was du heute lernst:

Gestern: Du hast Formulare und MVC-Pattern gemeistert.

Heute: Du lernst, wie du deine Spring Boot App professionell konfigurierst und Logging richtig einsetzt!

Das Problem:

  • Entwicklung braucht andere Einstellungen als Production
  • Debugging ohne Logging ist die Hölle
  • Hardcoded Werte im Code sind unprofessionell
  • Test-Daten müssen beim Start geladen werden
  • Secrets gehören NICHT in den Code!

Die Lösung:

  • application.properties verstehen und nutzen
  • Profile (dev, test, prod)
  • Bootstrap-Klasse für Initial-Daten
  • @Value und @ConfigurationProperties
  • SLF4J & Logback richtig nutzen

🎯 Dein Lernpfad heute:

Du arbeitest heute in mehreren aufbauenden Schwierigkeitsstufen. Arbeite in deinem eigenen Tempo durch die Schritte:

🟢 Grundlagen (Schritte 1-5)

Was du lernst:

  • application.properties verstehen
  • Profile (dev, prod) erstellen und nutzen
  • @Value für einzelne Properties
  • @ConfigurationProperties für strukturierte Configs
  • SLF4J-Logging Basics
  • Log-Level verstehen und anwenden

Ziel: Funktionierende Multi-Environment-Konfiguration + Logging in Service/Controller

🟡 Professional (Schritte 6-7)

Was du lernst:

  • Logging konfigurieren (pro Package/Profil)
  • Logs in Dateien schreiben
  • Log-Pattern anpassen
  • Praktisches Logging-Beispiel

Ziel: Production-Ready Logging-Setup

🔵 Bonus: Enterprise Features (Schritte 8-10)

Was du lernst:

  • Secrets & Umgebungsvariablen
  • Bootstrap-Klasse für Test-Daten
  • CommandLineRunner Pattern
  • Profile-spezifische Bootstraps
  • Logback-XML Konfiguration (Optional)

Ziel: Enterprise-Level Configuration Management

💡 Tipp: Die Grundlagen (Schritte 1-5) sind essenziell – jede Spring Boot App braucht Properties und Logging! Professional (Schritte 6-7) ist sehr empfehlenswert für echte Projekte. Die Bonus-Features zeigen Enterprise-Patterns, die du in größeren Firmen siehst.


💻 Los geht’s!

🟢 GRUNDLAGEN

Schritt 1: application.properties verstehen

1.1 Was ist das überhaupt?

Spring Boot liest beim Start die Konfigurationsdatei:

  • src/main/resources/application.properties

Hier definierst du ALLES: Server-Port, Datenbank, Logging, eigene Einstellungen, etc.

1.2 Deine erste application.properties

Erstelle/Erweitere: src/main/resources/application.properties

# Spring Boot Konfiguration - Persons App

# Server
server.port=8080
server.servlet.context-path=/

# Application Name
spring.application.name=persons-app

# Thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

# Logging
logging.level.root=INFO
logging.level.com.example.helloworldapi=DEBUG
logging.pattern.console=%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

Teste es:

mvn spring-boot:run

Du solltest sehen:

10:30:45.123  INFO 12345 --- [main] c.e.h.HelloWorldApiApplication : Starting HelloWorldApiApplication...

Schritt 2: Profile – Verschiedene Umgebungen

2.1 Das Problem

Development:

  • Server-Port 8080
  • Thymeleaf Cache AUS (Änderungen sofort sichtbar)
  • Debug-Logging AN
  • Detaillierte Fehlerseiten

Production:

  • Server-Port 8080 (oder 80/443)
  • Thymeleaf Cache AN (Performance!)
  • Info-Logging nur
  • Keine Fehlerdetails nach außen

Wir brauchen unterschiedliche Konfigurationen!

2.2 Profile anlegen

Erstelle 3 Dateien:

  1. application.properties (Basis – gilt immer)
  2. application-dev.properties (Development)
  3. application-prod.properties (Production)

application.properties (Basis):

# Spring Boot Basis-Konfiguration

# Application Name
spring.application.name=persons-app

# Standard-Profil
spring.profiles.active=dev

# Basis-Logging Pattern
logging.pattern.console=%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

application-dev.properties:

# Development Konfiguration

# Server
server.port=8080

# Thymeleaf ohne Cache
spring.thymeleaf.cache=false

# Verbose Logging
logging.level.root=INFO
logging.level.com.example.helloworldapi=DEBUG
logging.level.org.springframework.web=DEBUG

application-prod.properties:

# Production Konfiguration

# Server
server.port=8080
server.error.include-message=never
server.error.include-stacktrace=never

# Thymeleaf mit Cache
spring.thymeleaf.cache=true

# Weniger Logging
logging.level.root=WARN
logging.level.com.example.helloworldapi=INFO

# Logging in Datei
logging.file.name=/var/log/persons-app/application.log
logging.file.max-size=10MB
logging.file.max-history=30

2.3 Profile aktivieren

Methode 1: In application.properties

spring.profiles.active=dev

Methode 2: Beim Start (BESSER!)

# Development
mvn spring-boot:run -Dspring-boot.run.profiles=dev

# Production
java -jar target/persons-app.jar --spring.profiles.active=prod

Methode 3: Umgebungsvariable

export SPRING_PROFILES_ACTIVE=prod
java -jar target/persons-app.jar

2.4 Testen

Dev-Profil:

mvn spring-boot:run -Dspring-boot.run.profiles=dev

Öffne: http://localhost:8080/persons

In den Logs siehst du:

10:30:45.123 DEBUG c.e.h.service.PersonService : Getting all persons...

Prod-Profil:

mvn spring-boot:run -Dspring-boot.run.profiles=prod

Öffne: http://localhost:8080/persons

In den Logs siehst du:

10:30:45.123  INFO c.e.h.service.PersonService : PersonService initialized

DEBUG-Logs sind weg!


Schritt 3: @Value – Einzelne Properties auslesen

3.1 Eigene Properties definieren

In application.properties:

# Eigene App-Konfiguration
app.name=Persons Management System
app.version=1.0.0
app.max-persons=100
app.features.email-enabled=true
app.features.export-enabled=false

3.2 @Value im Controller nutzen

PersonViewController.java:

package com.example.helloworldapi.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor
public class PersonViewController {
    
    private final PersonService personService;
    
    // Properties aus application.properties einlesen
    @Value("${app.name}")
    private String appName;
    
    @Value("${app.version}")
    private String appVersion;
    
    @Value("${app.max-persons}")
    private int maxPersons;
    
    @Value("${app.features.email-enabled}")
    private boolean emailEnabled;
    
    @GetMapping("/persons")
    public String showPersons(Model model) {
        model.addAttribute("persons", personService.getAllPersons());
        model.addAttribute("appName", appName);
        model.addAttribute("appVersion", appVersion);
        model.addAttribute("maxPersons", maxPersons);
        return "persons-list";
    }
}

3.3 Default-Werte setzen

@Value("${app.max-persons:50}")  // Default: 50
private int maxPersons;

@Value("${app.features.email-enabled:false}")  // Default: false
private boolean emailEnabled;

Wenn Property nicht existiert → Default-Wert wird genommen!

3.4 Im Template anzeigen

persons-list.html:

<body>
    <h1 th:text="${appName}">Persons Management System</h1>
    <p style="color: #666; font-size: 12px;">
        Version <span th:text="${appVersion}">1.0.0</span> | 
        Max. Personen: <span th:text="${maxPersons}">100</span>
    </p>
    
    <!-- Rest wie gehabt -->
</body>

Schritt 4: @ConfigurationProperties – Typsicher & sauber!

4.1 Das Problem mit @Value

@Value("${app.name}")
private String appName;

@Value("${app.version}")
private String appVersion;

@Value("${app.max-persons}")
private int maxPersons;

@Value("${app.features.email-enabled}")
private boolean emailEnabled;

@Value("${app.features.export-enabled}")
private boolean exportEnabled;

Das wird schnell unübersichtlich! 😵

4.2 Die Lösung: @ConfigurationProperties

Erstelle: src/main/java/.../config/AppProperties.java

package com.example.helloworldapi.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;

@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
    
    private String name;
    private String version;
    private int maxPersons;
    private Features features = new Features();
    
    @Data
    public static class Features {
        private boolean emailEnabled;
        private boolean exportEnabled;
    }
}

application.properties (wie vorher):

app.name=Persons Management System
app.version=1.0.0
app.max-persons=100
app.features.email-enabled=true
app.features.export-enabled=false

4.3 Im Controller nutzen

PersonViewController.java:

@Controller
@RequiredArgsConstructor
public class PersonViewController {
    
    private final PersonService personService;
    private final AppProperties appProperties;  // Injizieren!
    
    @GetMapping("/persons")
    public String showPersons(Model model) {
        model.addAttribute("persons", personService.getAllPersons());
        model.addAttribute("appName", appProperties.getName());
        model.addAttribute("appVersion", appProperties.getVersion());
        model.addAttribute("maxPersons", appProperties.getMaxPersons());
        return "persons-list";
    }
}

Vorteile:

  • ✅ Typsicher (IDE hilft)
  • ✅ Übersichtlich
  • ✅ Validation möglich
  • ✅ Verschachtelung einfach
  • ✅ Besser testbar

4.4 Dependency hinzufügen (falls noch nicht vorhanden)

pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

Schritt 5: Logging verstehen

5.1 Was ist Logging?

Logging = Nachrichten aufzeichnen während die App läuft

Beispiel OHNE Logging:

public Person createPerson(Person person) {
    person.setId(idCounter.getAndIncrement());
    persons.add(person);
    return person;
}

Wenn etwas schiefgeht → Keine Ahnung was passiert ist! 😱

Beispiel MIT Logging:

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

public Person createPerson(Person person) {
    log.debug("Creating new person: {} {}", person.getFirstname(), person.getLastname());
    
    person.setId(idCounter.getAndIncrement());
    persons.add(person);
    
    log.info("Person created successfully with ID: {}", person.getId());
    
    return person;
}

Jetzt sehen wir genau was passiert! ✅

5.2 Die drei großen Logging-Frameworks

In Java gibt es 3 wichtige Logging-Frameworks:

FrameworkBeschreibungWann nutzen?
JULJava Util Logging (java.util.logging)Im JDK enthalten, veraltet
Log4j2Apache Log4j 2Sehr performant, async logging
LogbackNachfolger von Log4j 1.xSpring Boot Standard!

Vergleichstabelle (wichtig für Multiple Choice!):

FeatureJULLog4j2Logback
Im JDK dabei
Spring Boot Standard
Performance⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Async Logging
Remote Logging✅ (nativ)✅ (via Appender)
Garbage-Free
Lambda Support
KonfigurationKompliziertXML/JSON/YAMLXML/Properties
SLF4J SupportVia AdapterVia AdapterNativ ✅
Beliebt❌ (veraltet)✅ (Enterprise)✅ (Standard)

Warum welches Framework?

SzenarioFrameworkGrund
Spring Boot App (normal)LogbackIst schon dabei, reicht für 90%
Microservices (viele Services)Log4j2Remote Logging zu ELK Stack
High-Performance (Trading, Gaming)Log4j2Garbage-Free, schneller
Legacy Java AppJULNur wenn nichts anderes geht
Verteilte SystemeLog4j2Zentrale Log-Aggregation

Was ist SLF4J?

SLF4J = Simple Logging Facade for Java

SLF4J ist KEINE Logging-Implementierung, sondern eine Abstraktionsschicht!

Dein Code
    ↓
  SLF4J (Facade/Interface)
    ↓
  ┌───────┬───────────┬──────────┐
  JUL   Log4j2    Logback

Vorteil: Du programmierst gegen SLF4J → Kannst später die Implementierung wechseln ohne Code zu ändern!

// ✅ RICHTIG - SLF4J Interface
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// Spring Boot nutzt automatisch Logback als Implementierung
private static final Logger log = LoggerFactory.getLogger(MyClass.class);

5.3 SLF4J nutzen (Standard in Spring Boot)

PersonService.java:

package com.example.helloworldapi.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;

@Service
public class PersonService {
    
    // Logger erstellen mit SLF4J
    private static final Logger log = LoggerFactory.getLogger(PersonService.class);
    
    private final List<Person> persons = new ArrayList<>();
    private final AtomicLong idCounter = new AtomicLong(1);
    
    public PersonService() {
        log.info("PersonService initialized");
    }
    
    public List<Person> getAllPersons() {
        log.debug("Getting all persons, current count: {}", persons.size());
        return persons;
    }
    
    public Person getPersonById(Long id) {
        log.debug("Looking for person with ID: {}", id);
        
        Person person = persons.stream()
                .filter(p -> p.getId().equals(id))
                .findFirst()
                .orElse(null);
        
        if (person == null) {
            log.warn("Person with ID {} not found!", id);
        } else {
            log.debug("Found person: {} {}", person.getFirstname(), person.getLastname());
        }
        
        return person;
    }
    
    public Person createPerson(Person person) {
        log.info("Creating new person: {} {}", person.getFirstname(), person.getLastname());
        
        person.setId(idCounter.getAndIncrement());
        persons.add(person);
        
        log.info("Person created successfully with ID: {}", person.getId());
        log.debug("Total persons now: {}", persons.size());
        
        return person;
    }
}

5.4 Log-Level verstehen

LevelWann nutzen?Beispiel
TRACESehr detailliert (fast nie)„Entering method calculateTax()“
DEBUGEntwicklung/Debugging„User input: firstname=Max“
INFONormale Events„Person created with ID 5“
WARNWarnung (nicht kritisch)„Cache is 90% full“
ERRORFehler (App läuft weiter)„Failed to send email“

Faustregel:

  • Development: DEBUG
  • Production: INFO oder WARN

5.5 Logs sehen

Starte die App:

mvn spring-boot:run -Dspring-boot.run.profiles=dev

Console-Output:

10:30:45.123  INFO 12345 --- [main] c.e.h.service.PersonService : PersonService initialized
10:30:45.124 DEBUG 12345 --- [main] c.e.h.service.PersonService : Getting all persons, current count: 0

Jetzt siehst du genau was passiert! 🎉

Was bedeutet die Ausgabe?

10:30:45.123  INFO 12345 --- [main] c.e.h.service.PersonService : Message here
     ↑         ↑     ↑        ↑              ↑                        ↑
  Timestamp  Level  PID    Thread    Logger (Klassenname)        Nachricht

🟡 PROFESSIONAL

Schritt 6: Logging konfigurieren

6.1 Log-Level pro Package

application-dev.properties:

# Logging für Development
logging.level.root=INFO
logging.level.com.example.helloworldapi=DEBUG
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=INFO
logging.level.org.hibernate.SQL=DEBUG

application-prod.properties:

# Logging für Production
logging.level.root=WARN
logging.level.com.example.helloworldapi=INFO
logging.level.org.springframework=WARN

6.2 Log-Pattern anpassen

application.properties:

# Log-Pattern
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

Was bedeutet das?

  • %d{...} = Datum/Zeit
  • [%thread] = Thread-Name
  • %-5level = Log-Level (LEFT-aligned, 5 Zeichen)
  • %logger{36} = Logger-Name (max 36 Zeichen)
  • %msg = Die eigentliche Nachricht
  • %n = Newline

Output:

2025-10-17 10:30:45.123 [main] INFO  c.e.h.service.PersonService - Person created successfully with ID: 1

6.3 Logs in Datei schreiben

application-prod.properties:

# Logging in Datei
logging.file.name=logs/persons-app.log
logging.file.max-size=10MB
logging.file.max-history=30
logging.level.root=WARN
logging.level.com.example.helloworldapi=INFO

Erstelle Ordner:

mkdir logs

Starte App:

mvn spring-boot:run -Dspring-boot.run.profiles=prod

Prüfe Log-Datei:

cat logs/persons-app.log

Schritt 7: Praktisches Logging-Beispiel

PersonViewController mit Logging:

package com.example.helloworldapi.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor
public class PersonViewController {
    
    private static final Logger log = LoggerFactory.getLogger(PersonViewController.class);
    
    private final PersonService personService;
    private final AppProperties appProperties;
    
    @GetMapping("/persons")
    public String showPersons(Model model) {
        log.debug("Showing persons list");
        
        List<Person> persons = personService.getAllPersons();
        log.info("Displaying {} persons", persons.size());
        
        model.addAttribute("persons", persons);
        model.addAttribute("appName", appProperties.getName());
        
        return "persons-list";
    }
    
    @GetMapping("/persons/{id}")
    public String showPersonDetails(@PathVariable Long id, Model model) {
        log.debug("Showing details for person ID: {}", id);
        
        Person person = personService.getPersonById(id);
        
        if (person == null) {
            log.warn("Person with ID {} not found, redirecting to list", id);
            return "redirect:/persons";
        }
        
        log.debug("Displaying person: {} {}", person.getFirstname(), person.getLastname());
        model.addAttribute("person", person);
        return "person-details";
    }
}

🔵 BONUS: ENTERPRISE FEATURES

Schritt 8: Secrets & Umgebungsvariablen

8.1 Das Problem

NIEMALS so:

# ❌ FALSCH!
app.api.key=geheimer-api-key-123
app.admin.password=admin123

Git → GitHub → Jeder kann’s sehen!

8.2 Die Lösung: Umgebungsvariablen

application-prod.properties:

# Externe API-Keys (Beispiel)
app.api.key=${API_KEY}
app.api.secret=${API_SECRET}

# Admin Credentials
app.admin.username=${ADMIN_USERNAME}
app.admin.password=${ADMIN_PASSWORD}

Beim Start setzen:

export API_KEY=dein-geheimer-key
export API_SECRET=dein-geheimes-secret
export ADMIN_USERNAME=admin
export ADMIN_PASSWORD=geheim123

java -jar target/persons-app.jar --spring.profiles.active=prod

8.3 Default-Werte für Dev

application-dev.properties:

# In Dev nutzen wir Dummy-Werte
app.api.key=${API_KEY:dev-dummy-key}
app.api.secret=${API_SECRET:dev-dummy-secret}
app.admin.username=${ADMIN_USERNAME:dev-admin}
app.admin.password=${ADMIN_PASSWORD:dev123}

Wenn Umgebungsvariablen NICHT gesetzt → Dummy-Werte werden genutzt!


Schritt 9: Bootstrap-Klasse für Test-Daten

9.1 Was ist ein Bootstrap?

Bootstrap = Klasse die beim App-Start Initial-Daten lädt

Warum?

  • Ohne Datenbank brauchen wir trotzdem Daten zum Testen
  • Daten sollen beim Start automatisch da sein
  • Verschiedene Test-Daten für dev/prod möglich

9.2 Bootstrap-Klasse erstellen

Erstelle: src/main/java/.../bootstrap/DataBootstrap.java

package com.example.helloworldapi.bootstrap;

import com.example.helloworldapi.model.Person;
import com.example.helloworldapi.service.PersonService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;

@Component
@Profile("dev")  // Nur in DEV-Profil!
@RequiredArgsConstructor
public class DataBootstrap implements CommandLineRunner {
    
    private static final Logger log = LoggerFactory.getLogger(DataBootstrap.class);
    
    private final PersonService personService;
    
    @Override
    public void run(String... args) throws Exception {
        log.info("Starting DataBootstrap...");
        
        loadPersons();
        
        log.info("DataBootstrap completed! Loaded {} persons", 
            personService.getAllPersons().size());
    }
    
    private void loadPersons() {
        log.debug("Loading test persons...");
        
        // Test-Person 1
        Person person1 = new Person();
        person1.setFirstname("Max");
        person1.setLastname("Mustermann");
        person1.setEmail("max@example.com");
        personService.createPerson(person1);
        
        // Test-Person 2
        Person person2 = new Person();
        person2.setFirstname("Anna");
        person2.setLastname("Schmidt");
        person2.setEmail("anna@example.com");
        personService.createPerson(person2);
        
        log.info("Loaded 2 test persons");
    }
}

Was macht das?

Annotation/InterfaceBedeutung
@ComponentSpring verwaltet diese Klasse
@Profile("dev")Nur in DEV-Profil aktiv!
CommandLineRunnerrun() wird beim App-Start aufgerufen
@RequiredArgsConstructorConstructor Injection (Lombok)

9.3 PersonService anpassen

Entferne die Test-Daten aus dem Constructor!

PersonService.java:

@Service
public class PersonService {
    
    private static final Logger log = LoggerFactory.getLogger(PersonService.class);
    
    private final List<Person> persons = new ArrayList<>();
    private final AtomicLong idCounter = new AtomicLong(1);
    
    public PersonService() {
        log.info("PersonService initialized");
        // KEINE Test-Daten mehr hier!
    }
    
    // Rest der Methoden bleiben gleich...
}

9.4 Testen

Dev-Profil (mit Test-Daten):

mvn spring-boot:run -Dspring-boot.run.profiles=dev

Console zeigt:

10:30:45.123  INFO c.e.h.service.PersonService : PersonService initialized
10:30:45.125  INFO c.e.h.bootstrap.DataBootstrap : Starting DataBootstrap...
10:30:45.133  INFO c.e.h.bootstrap.DataBootstrap : Loaded 2 test persons

Öffne: http://localhost:8080/persons → 2 Personen sind da! ✅


Schritt 10: Bonus – Logback-Konfiguration (Optional)

Erstelle: src/main/resources/logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    
    <!-- Console Appender -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{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/persons-app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/persons-app.%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" />
    </root>
    
    <!-- Deine App: DEBUG -->
    <logger name="com.example.helloworldapi" level="DEBUG" />
    
</configuration>

Jetzt gehen Logs zu Console UND Datei!


✅ Checkpoint: Hast du Tag 5 geschafft?

Grundlagen (🟢):

  • [ ] application.properties existiert und funktioniert
  • [ ] Profile (dev, prod) sind angelegt
  • [ ] Du kannst Profile wechseln
  • [ ] @Value funktioniert
  • [ ] @ConfigurationProperties sind erstellt
  • [ ] Logging ist im PersonService implementiert
  • [ ] Log-Level verstanden (DEBUG, INFO, WARN, ERROR)

Professional (🟡):

  • [ ] Log-Level pro Profil eingestellt
  • [ ] Logs werden in Datei geschrieben (prod)
  • [ ] Logging ist im PersonViewController implementiert
  • [ ] Log-Pattern angepasst

Bonus (🔵):

  • [ ] Umgebungsvariablen für Secrets funktionieren
  • [ ] Bootstrap-Klasse lädt Test-Daten
  • [ ] Bootstrap läuft nur im dev-Profil
  • [ ] Logback-XML konfiguriert (optional)

Alles ✅? Du bist bereit für Tag 6!

Nicht alles funktioniert? → Schau in die Troubleshooting-Sektion!


🆘 Troubleshooting: Die häufigsten Probleme

Problem 1: Profile wird nicht geladen

Symptom: Änderungen in application-dev.properties haben keine Wirkung

Lösung:

# Prüfe welches Profil aktiv ist
mvn spring-boot:run -Dspring-boot.run.profiles=dev

# In Logs schauen:
# "The following profiles are active: dev"

Problem 2: Property nicht gefunden

Symptom:

Could not resolve placeholder 'app.name' in value "${app.name}"

Lösung:

# Prüfe Schreibweise!
app.name=Persons Management System
# NICHT app-name oder app_name

Problem 3: @ConfigurationProperties funktioniert nicht

Symptom: Properties sind null

Lösung 1: @Component vergessen?

@Component  // MUSS da sein!
@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
    // ...
}

Lösung 2: Dependency fehlt?

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

Problem 4: Logs erscheinen nicht

Symptom: Logger schreibt nichts

Ursache 1: Log-Level zu hoch

# application.properties
logging.level.com.example.helloworldapi=DEBUG
# Nicht INFO oder WARN!

Ursache 2: Logger falsch importiert

// ❌ FALSCH
import java.util.logging.Logger;

// ✅ RICHTIG
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Problem 5: Bootstrap läuft nicht

Symptom: Keine Test-Daten beim Start

Lösung:

# Bootstrap läuft nur in dev!
mvn spring-boot:run -Dspring-boot.run.profiles=dev

Hier sind noch einige externe Links, die sich hervorragend zum Thema „Konfiguration & Logging in Spring Boot“ eignen — mit guten Erklärungen, Beispielen und Praxisbezug damit kannst du dein Wissen noch mehr vertiefen:

  • „Logging :: Spring Boot” — offizielle Dokumentation von Spring Boot zu Logging und Konfiguration. Home+1
  • „Logging in Spring Boot – Baeldung” — ein ausführlicher Artikel über Logging-Frameworks (Logback, Log4j2) in Spring Boot. Baeldung on Kotlin
  • „Spring Boot – Logging | GeeksforGeeks” — Einsteigerfreundlicher Artikel mit Beispielen zur Konfiguration über application.properties und Logging in Dateien. GeeksforGeeks+1
  • „A Guide to Spring Boot Logging: Best Practices & Techniques” — Blogbeitrag mit Überblick über Best Practices beim Logging in Spring Boot-Anwendungen. Last9
  • „How to Configure Default Log Files – Spring Boot Logging” — Praxisanleitung zum Einrichten von Logdateien, statt nur Konsolenausgabe. signoz.io

🔥 Elyndras Real Talk:

Nova kam heute zu mir und fragte: „Elyndra, warum machen wir das so kompliziert mit Profilen? Kann ich nicht einfach eine properties-Datei haben?“

Ich musste schmunzeln – genau die Frage hatte ich vor 5 Jahren auch gestellt!

Meine Antwort: „Stell dir vor, du entwickelst lokal mit Thymeleaf-Cache AUS auf Port 8080. Dann deployest du auf den Test-Server – Cache AN, Port 80, andere Log-Level. Dann kommt Production – wieder andere Settings, Logging in Dateien statt Console. Willst du wirklich jedes Mal die properties-Datei händisch ändern und hoffen, dass du nichts vergisst?“

Nova nickte: „Okay, verstehe. Aber warum Logging auf DEBUG in Dev und INFO in Prod?“

Code Sentinel, der gerade vorbeikam, warf ein: „Weil du in Production nicht jeden einzelnen Request-Parameter in deinen Logs haben willst. Das sind Gigabytes an Daten! Und Performance-Killer. In Dev willst du aber ALLES sehen, um Bugs zu finden.“

Ich ergänzte: „Genau! Und noch was – in Dev loggst du nach Console, in Prod in Dateien mit Rotation. Sonst hast du nach 3 Monaten eine 50GB-Log-Datei und dein Server ist voll.“

Das ist der Unterschied zwischen Junior und Senior Developer: Nicht nur Features bauen, sondern auch an verschiedene Umgebungen, Wartbarkeit und Fehlerquellen denken!


❓ FAQ (Häufige Fragen)

Q: Warum application.properties und nicht application.yml?
A: Das ist unser Standard in diesem Kurs! Properties ist einfacher für Anfänger und hat keine Whitespace-Probleme wie YAML.

Q: Wie viele Profile sollte ich haben?
A: Mindestens 2 (dev, prod). Optional: test, staging, local. Nicht übertreiben!

Q: Sollte ich logback-spring.xml nutzen oder application.properties?
A: Für die meisten Fälle reicht application.properties. Nur bei komplexen Anforderungen lohnt sich logback-spring.xml.

Q: Was ist der Unterschied zwischen JUL, Log4j2 und Logback?
A: JUL = Im JDK enthalten, veraltet. Log4j2 = Sehr performant, Remote Logging, Garbage-Free. Logback = Spring Boot Standard, gute Balance.

Q: Warum benutzt Spring Boot Logback und nicht Log4j2?
A: Logback ist „gut genug“ für die meisten Apps und hat native SLF4J-Unterstützung. Log4j2 ist performanter (Remote Logging, Garbage-Free), aber meist nicht nötig.

Q: Wann sollte ich Log4j2 statt Logback nutzen?
A: Microservices (zentrale Logs mit ELK Stack), High-Performance (Garbage-Free), Lambda-Support. Für normale Apps reicht Logback.

Q: Was ist SLF4J genau?
A: SLF4J ist KEINE Logging-Implementierung, sondern eine Abstraktionsschicht. Du programmierst gegen SLF4J-Interfaces, Spring Boot nutzt Logback als Implementierung.

Q: Welches Log-Level für Production?
A: INFO oder WARN für root. DEBUG nur für deine eigene App wenn du Probleme debuggen musst.

Q: Wo speichere ich Secrets?
A: Umgebungsvariablen, Vault, Cloud Secrets Manager. NIEMALS im Code oder Git!


📅 Nächster Kurstag: Tag 6

Morgen im Kurs / Nächster Blogbeitrag:

„DI & AOP im Detail – Dependency Injection & Aspektorientierte Programmierung“

Was du lernen wirst:

  • Wie funktioniert Dependency Injection wirklich?
  • @Autowired, @Qualifier, @Primary verstehen
  • Constructor Injection vs Field Injection
  • AOP-Grundlagen mit @Aspect
  • Custom Annotations erstellen
  • Cross-Cutting Concerns (Logging, Security, etc.)

Voraussetzung: Tag 5 abgeschlossen

👉 Zum Blogbeitrag Tag 6 (erscheint morgen)


📚 Deine Fortschritts-Übersicht

TagThemaStatus
✅ 1Erste REST APIABGESCHLOSSEN! 🎉
✅ 2Spring Container & DIABGESCHLOSSEN! 🎉
✅ 3@Controller & Thymeleaf BasicsABGESCHLOSSEN! 🎉
✅ 4Thymeleaf Forms & MVC-PatternABGESCHLOSSEN! 🎉
✅ 5Konfiguration & LoggingABGESCHLOSSEN! 🎉
→ 6DI & AOP im DetailAls nächstes
7Scopes in SpringNoch offen
8WebSocketsNoch offen
9JAX-RS in Spring BootNoch offen
10Integration & AbschlussNoch offen

Du hast 50% des Kurses geschafft! 💪

Alle Blogbeiträge dieser Serie:
👉 Spring Boot Basic – Komplette Übersicht


📥 Download & Ressourcen

Projekt zum Download:
👉 SpringBootConfig-v1.0.zip (Stand: 17.10.2025)

Was ist im ZIP enthalten:

  • ✅ Komplettes Maven-Projekt
  • ✅ application.properties mit Profilen (dev, prod)
  • ✅ @ConfigurationProperties Beispiel
  • ✅ Vollständiges Logging in Service & Controller
  • ✅ Bootstrap-Klasse für Test-Daten
  • ✅ Umgebungsvariablen-Support
  • ✅ logback-spring.xml Beispiel
  • ✅ README mit Schnellstart

Projekt starten:

# ZIP entpacken
# In NetBeans öffnen: File → Open Project

# Development-Modus:
mvn spring-boot:run -Dspring-boot.run.profiles=dev

# Production-Modus:
mvn spring-boot:run -Dspring-boot.run.profiles=prod

Probleme? Issue melden oder schreib mir: elyndra@java-developer.online


Das war Tag 5 von Spring Boot Basic!

Du kannst jetzt:

  • ✅ application.properties komplett nutzen
  • ✅ Profile für verschiedene Umgebungen einsetzen
  • ✅ @Value und @ConfigurationProperties verwenden
  • ✅ SLF4J-Logging professionell nutzen
  • ✅ Log-Level pro Umgebung konfigurieren
  • ✅ Logs in Dateien schreiben
  • ✅ Secrets sicher mit Umgebungsvariablen verwalten
  • ✅ Bootstrap-Klassen für Initial-Daten erstellen
  • ✅ Das komplette Konfigurationssystem verstehen!

Morgen: Dependency Injection & AOP im Detail! 🚀

Keep coding, keep learning! 💚


Tag 6 erscheint morgen. Bis dahin: Happy Coding!


Tags: #SpringBoot #Configuration #Logging #Properties #Profiles #Tag5

Autor

  • Elyndra Valen

    28 Jahre alt, wurde kürzlich zur Senior Entwicklerin befördert nach 4 Jahren intensiver Java-Entwicklung. Elyndra kennt die wichtigsten Frameworks und Patterns, beginnt aber gerade erst, die tieferen Zusammenhänge und Architektur-Entscheidungen zu verstehen. Sie ist die Brücke zwischen Junior- und Senior-Welt im Team.