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.-)
|
5. [Projektstruktur und Best Practices](Projektstruktur-und-Best-Practices.-)
|
||||||
6. [Gitea-Workflow](Gitea-Workflow.-)
|
6. [Gitea-Workflow](Gitea-Workflow.-)
|
||||||
7. [Coding-Guidelines-PHP](Coding-Guidelines.-)
|
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