Anleitung für Logging-Service
parent
e76bec80ef
commit
9c424029fc
2
Home.md
2
Home.md
@ -36,7 +36,7 @@ Wenn du neu im Projekt bist, lies die Seiten am besten in dieser Reihenfolge:
|
||||
5. [Projektstruktur und Best Practices](Projektstruktur-und-Best-Practices.-)
|
||||
6. [Gitea-Workflow](Gitea-Workflow.-)
|
||||
7. [Coding-Guidelines-PHP](Coding-Guidelines.-)
|
||||
8. [[Coding-Guidelines-C#]] (sofern wir C# benutzen wollen)
|
||||
8. [Logging und Fehlerbehandlung (WICHTIG)](Logging-Service.-)
|
||||
|
||||
---
|
||||
|
||||
|
||||
410
Logging-Service.-.md
Normal file
410
Logging-Service.-.md
Normal file
@ -0,0 +1,410 @@
|
||||
|
||||
# Technische Umsetzung: Logging-Service
|
||||
|
||||
Dieses Kapitel beschreibt die konkrete Implementierung des Logging-Konzepts in der PHP-Anwendung.
|
||||
Ziel ist, dass jedes Teammitglied genau weiß:
|
||||
|
||||
- wo der Logging-Service liegt,
|
||||
- wie er konfiguriert wird,
|
||||
- wie er in Controllern und Services verwendet wird,
|
||||
- welche Regeln beim Loggen gelten.
|
||||
|
||||
Die Inhalte bauen direkt auf der Seite **„Fehlerbehandlung und Logging“** auf und setzen die dort beschriebenen Grundprinzipien praktisch um.
|
||||
|
||||
---
|
||||
|
||||
## 1. Überblick
|
||||
|
||||
### 1.1 Ziel
|
||||
|
||||
- Benutzerfreundliche, neutrale Fehlermeldungen im Frontend.
|
||||
- Detaillierte technische Informationen in Logdateien.
|
||||
- Einheitliches Vorgehen in allen Controllern und Services.
|
||||
|
||||
### 1.2 Position im Projekt
|
||||
|
||||
Der Logging-Service ist eine eigene Klasse im `app/`-Bereich:
|
||||
|
||||
```text
|
||||
app/
|
||||
├── Controllers/
|
||||
├── Models/
|
||||
└── Services/
|
||||
├── Ldap/
|
||||
├── Powershell/
|
||||
├── Snmp/
|
||||
└── Logging/
|
||||
└── LoggingService.php
|
||||
```
|
||||
|
||||
Die Konfiguration erfolgt zentral über `config/config.php`.
|
||||
Die Logdatei liegt aktuell unter:
|
||||
|
||||
```text
|
||||
public/logs/app.log
|
||||
```
|
||||
|
||||
> Hinweis: In der ursprünglichen Projektstruktur war `storage/logs` vorgesehen.
|
||||
> Für das aktuelle Setup wird `public/logs` verwendet.
|
||||
> Auf dem IIS-Server muss das Verzeichnis so konfiguriert werden, dass es zwar für die Anwendung schreibbar ist, aber nicht direkt per URL im Browser geöffnet werden kann (z. B. über Web.config-Regeln).
|
||||
|
||||
---
|
||||
|
||||
## 2. Konfiguration in `config/config.php`
|
||||
|
||||
Damit der Logging-Service funktioniert, wird im Config-Array ein eigener Abschnitt `logging` verwendet:
|
||||
|
||||
```php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'ldap' => [
|
||||
// ...
|
||||
],
|
||||
|
||||
'security' => [
|
||||
// ...
|
||||
],
|
||||
|
||||
'snmp' => [
|
||||
// ...
|
||||
],
|
||||
|
||||
// Logging-Konfiguration
|
||||
'logging' => [
|
||||
// Verzeichnis für Logdateien
|
||||
'log_dir' => __DIR__ . '/../public/logs',
|
||||
// Dateiname der Logdatei
|
||||
'log_file' => 'app.log',
|
||||
// Minimale Log-Stufe: debug, info, warning, error
|
||||
'min_level' => 'info',
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
Wichtige Punkte:
|
||||
|
||||
- `log_dir` zeigt auf das Logverzeichnis.
|
||||
- `log_file` ist der Dateiname, in den geschrieben wird.
|
||||
- `min_level` legt fest, ab welchem Level ein Eintrag wirklich ins Log geschrieben wird.
|
||||
|
||||
---
|
||||
|
||||
## 3. LoggingService – Aufbau und Verhalten
|
||||
|
||||
### 3.1 Pfad und Klasse
|
||||
|
||||
Die Klasse liegt in `app/Services/Logging/LoggingService.php` und verwendet den Namespace `App\Services\Logging`.
|
||||
|
||||
Kernaufgaben:
|
||||
|
||||
- Sicherstellen, dass das Logverzeichnis existiert (`ensureLogDirectoryExists()`).
|
||||
- Logeinträge in eine Datei schreiben (`log()`).
|
||||
- Exceptions einheitlich und mit Kontextinformationen protokollieren (`logException()`).
|
||||
|
||||
### 3.2 Öffentliche Methoden
|
||||
|
||||
**Allgemeines Logging:**
|
||||
|
||||
```php
|
||||
public function log(string $level, string $message, array $context = []): void
|
||||
```
|
||||
|
||||
- `level`: `debug`, `info`, `warning` oder `error`
|
||||
- `message`: kurze Beschreibung
|
||||
- `context`: zusätzliche Informationen (z. B. `route`, `username`, `remote_addr`)
|
||||
|
||||
**Exception-Logging:**
|
||||
|
||||
```php
|
||||
public function logException(string $message, \Throwable $exception, array $context = []): void
|
||||
```
|
||||
|
||||
- `message`: allgemeiner Kontext (z. B. „Fehler beim Laden von Benutzern.“)
|
||||
- `exception`: Original-Exception
|
||||
- `context`: zusätzliche strukturierte Daten
|
||||
|
||||
Beide Methoden kümmern sich darum, dass:
|
||||
|
||||
- der Zeitstempel gesetzt wird,
|
||||
- das Level formatiert wird,
|
||||
- Kontextdaten als JSON in die Logdatei geschrieben werden.
|
||||
|
||||
Beispiel für einen Logeintrag:
|
||||
|
||||
```text
|
||||
[2025-12-04 20:13:12] ERROR Technischer Fehler bei der Anmeldung. {"route":"login.submit","username":"j.doe","remote_addr":"192.168.50.10","exception_class":"RuntimeException","exception_message":"Bind failed", ...}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Globaler Logger in `index.php`
|
||||
|
||||
### 4.1 Initialisierung
|
||||
|
||||
Im Front-Controller (`public/index.php`) wird ein globaler Logger aus der Konfiguration erstellt:
|
||||
|
||||
```php
|
||||
use App\Services\Logging\LoggingService;
|
||||
|
||||
// ...
|
||||
|
||||
/** @var array<string, mixed> $config */
|
||||
$config = require $configPath;
|
||||
|
||||
$globalLogger = new LoggingService($config['logging'] ?? []);
|
||||
```
|
||||
|
||||
### 4.2 Globale Error- und Exception-Handler
|
||||
|
||||
PHP-Fehler und unbehandelte Exceptions werden zentral geloggt:
|
||||
|
||||
```php
|
||||
set_error_handler(
|
||||
static function (
|
||||
int $severity,
|
||||
string $message,
|
||||
string $file = '',
|
||||
int $line = 0
|
||||
) use ($globalLogger): bool {
|
||||
if ((error_reporting() & $severity) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$globalLogger->log(
|
||||
'error',
|
||||
'PHP-Fehler: ' . $message,
|
||||
[
|
||||
'severity' => $severity,
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
]
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
set_exception_handler(
|
||||
static function (\Throwable $exception) use ($globalLogger): void {
|
||||
$globalLogger->logException(
|
||||
'Unbehandelte Exception in der Anwendung.',
|
||||
$exception,
|
||||
[
|
||||
'request_uri' => $_SERVER['REQUEST_URI'] ?? null,
|
||||
'route' => $_GET['route'] ?? null,
|
||||
]
|
||||
);
|
||||
|
||||
http_response_code(500);
|
||||
echo 'Es ist ein unerwarteter Fehler aufgetreten. '
|
||||
. 'Bitte versuchen Sie es später erneut oder wenden Sie sich an den Administrator.';
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Effekt:
|
||||
|
||||
- Alles, was **nicht** im Controller oder Service abgefangen wird, landet automatisch im Log.
|
||||
- Benutzer sehen eine generische, neutrale Fehlermeldung.
|
||||
|
||||
---
|
||||
|
||||
## 5. Logging im Controller
|
||||
|
||||
### 5.1 Grundprinzip
|
||||
|
||||
Controller:
|
||||
|
||||
- fangen Exceptions aus Services ab,
|
||||
- loggen die technischen Details über den LoggingService,
|
||||
- bereiten eine neutrale, fachliche Fehlermeldung für die View vor.
|
||||
|
||||
### 5.2 Muster: Logger im Controller verfügbar machen
|
||||
|
||||
**Schritt 1 – Namespace-Import:**
|
||||
|
||||
```php
|
||||
use App\Services\Logging\LoggingService;
|
||||
```
|
||||
|
||||
**Schritt 2 – Property:**
|
||||
|
||||
```php
|
||||
private LoggingService $logger;
|
||||
```
|
||||
|
||||
**Schritt 3 – Initialisierung im Konstruktor:**
|
||||
|
||||
```php
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
// Fach-Service initialisieren (Beispiel)
|
||||
$this->someService = new SomeService($config['some'] ?? []);
|
||||
|
||||
// Logging-Service initialisieren (immer aus $config['logging'])
|
||||
$this->logger = new LoggingService($config['logging'] ?? []);
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Beispiel: AuthController – Fehler beim Login
|
||||
|
||||
```php
|
||||
public function processLogin(): array
|
||||
{
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = (string)($_POST['password'] ?? '');
|
||||
|
||||
try {
|
||||
$authenticated = $this->ldapAuthService->authenticate($username, $password);
|
||||
} catch (\Throwable $exception) {
|
||||
// Technische Details protokollieren
|
||||
$this->logger->logException(
|
||||
'Technischer Fehler bei der Anmeldung.',
|
||||
$exception,
|
||||
[
|
||||
'route' => 'login.submit',
|
||||
'username' => $username,
|
||||
'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? null,
|
||||
]
|
||||
);
|
||||
|
||||
// Neutrale Fehlermeldung für die View zurückgeben
|
||||
return $this->showLoginForm(
|
||||
'Technischer Fehler bei der Anmeldung. Bitte versuchen Sie es später erneut '
|
||||
. 'oder wenden Sie sich an den Administrator.'
|
||||
);
|
||||
}
|
||||
|
||||
if ($authenticated === false) {
|
||||
return $this->showLoginForm('Benutzername oder Passwort ist ungültig.');
|
||||
}
|
||||
|
||||
// ... erfolgreicher Login (Session setzen, Redirect zurückgeben) ...
|
||||
}
|
||||
```
|
||||
|
||||
Wichtig:
|
||||
|
||||
- **Keine** technischen Details (Exception-Message, Stacktrace) an die View geben.
|
||||
- Username kann im Kontext geloggt werden, Passwörter niemals.
|
||||
|
||||
### 5.4 Beispiel: UserManagementController – Fehler beim Laden von Benutzern
|
||||
|
||||
```php
|
||||
public function show(): array
|
||||
{
|
||||
$error = null;
|
||||
$users = [];
|
||||
$groups = [];
|
||||
|
||||
try {
|
||||
$users = $this->directoryService->getUsers();
|
||||
$groups = $this->directoryService->getGroups();
|
||||
} catch (\Throwable $exception) {
|
||||
$this->logger->logException(
|
||||
'Fehler beim Laden von Benutzern/Gruppen.',
|
||||
$exception,
|
||||
[
|
||||
'route' => 'users',
|
||||
'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? null,
|
||||
]
|
||||
);
|
||||
|
||||
$error = 'Fehler beim Laden von Benutzern/Gruppen. '
|
||||
. 'Bitte versuchen Sie es später erneut oder wenden Sie sich an den Administrator.';
|
||||
}
|
||||
|
||||
// View-Result wie gehabt zurückgeben
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Logging in Services (optional)
|
||||
|
||||
Grundregel aus der bestehenden Architektur:
|
||||
|
||||
- Controller sind die erste Stelle für Fehlerbehandlung + Logging.
|
||||
- Services dürfen Exceptions werfen; sie müssen nicht alles selbst loggen.
|
||||
|
||||
Wenn ein Service zusätzlich loggen soll (z. B. für Ablaufprotokolle), gibt es zwei Möglichkeiten:
|
||||
|
||||
1. **Konstruktor-Injection des LoggingService:**
|
||||
|
||||
```php
|
||||
use App\Services\Logging\LoggingService;
|
||||
|
||||
class LdapDirectoryService
|
||||
{
|
||||
private LoggingService $logger;
|
||||
|
||||
public function __construct(array $config, LoggingService $logger)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function getUsers(): array
|
||||
{
|
||||
$this->logger->log('info', 'Lade Benutzerliste aus LDAP.', [
|
||||
'component' => 'LdapDirectoryService',
|
||||
]);
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In diesem Fall übergibt der Controller beim Erzeugen des Services einfach seinen `LoggingService` mit.
|
||||
|
||||
2. **Nur Controller loggen, Services werfen Exceptions** (empfohlener Standard):
|
||||
|
||||
- Service konzentriert sich auf Fachlogik und wirft bei Fehlern Exceptions.
|
||||
- Controller fängt diese ab, loggt sie und bereitet die Meldung für die View auf.
|
||||
|
||||
Welche Variante verwendet wird, hängt von der Komplexität des Services ab.
|
||||
Für die aktuelle Version des Projekts ist Variante 2 der Standard.
|
||||
|
||||
---
|
||||
|
||||
## 7. Best Practices
|
||||
|
||||
- **Keine Zugangsdaten loggen**
|
||||
Passwörter, Tokens, Connection-Strings usw. werden niemals in Klartext ins Log geschrieben.
|
||||
|
||||
- **Kontext nutzen**
|
||||
`route`, `username`, `remote_addr`, `component`, `server` etc. helfen bei der Fehlersuche.
|
||||
Diese Werte dürfen (soweit unkritisch) im Kontext stehen.
|
||||
|
||||
- **Log-Level sinnvoll wählen**
|
||||
|
||||
- `debug`: nur in der Entwicklung (kann über `min_level` deaktiviert werden)
|
||||
- `info`: normale Abläufe (z. B. „Benutzer X hat sich angemeldet“ – nur wenn gewünscht)
|
||||
- `warning`: auffällige, aber nicht kritische Situationen
|
||||
- `error`: Exceptions und wirklich unerwartete Fehler
|
||||
|
||||
- **Fehler nicht doppelt loggen**
|
||||
Entweder Controller loggt gezielt oder sie werden vom globalen Handler in `index.php` abgefangen.
|
||||
Kritische Stellen (Login, AD-Zugriffe, SNMP-Status) sollten allerdings bewusst im Controller geloggt werden, damit der Kontext vollständig ist.
|
||||
|
||||
---
|
||||
|
||||
## 8. Kurzanleitung für neue Controller
|
||||
|
||||
Bei neuen Controllern reicht folgende Checkliste:
|
||||
|
||||
1. `use App\Services\Logging\LoggingService;` ergänzen.
|
||||
2. `private LoggingService $logger;` als Property anlegen.
|
||||
3. Im Konstruktor:
|
||||
`$this->logger = new LoggingService($config['logging'] ?? []);`
|
||||
4. Alle Aufrufe von Services, die externe Systeme nutzen (LDAP, SNMP, PowerShell etc.), in `try/catch` kapseln.
|
||||
5. Im `catch`:
|
||||
- `$this->logger->logException('Kontexttext', $exception, [...])` aufrufen.
|
||||
- Eine neutrale Fehlermeldung für die View zurückgeben.
|
||||
6. Keine technischen Details an den Benutzer ausgeben.
|
||||
|
||||
Damit ist der Logging-Service im gesamten Projekt einheitlich angebunden und alle Entwickler wissen, wie sie ihn korrekt einsetzen.
|
||||
Loading…
Reference in New Issue
Block a user