9Logging
Niet alle uitvoer is even belangrijk, en in productie wil je achteraf kunnen nagaan wat er gebeurde. Logging gaat via een interface, zodat je de bestemming (console, bestand) kunt wisselen zonder de gebruikende klasse aan te passen (Open/Closed + DIP).
#Eigen logger-interface
public interface ILogger { void Info(string melding); void Fout(string melding); } public class ConsoleLogger : ILogger { public void Info(string m) => Console.WriteLine($"[INFO] {m}"); public void Fout(string m) => Console.WriteLine($"[FOUT] {m}"); } // later erbij, Verhuurbedrijf blijft ongewijzigd: public class BestandLogger : ILogger { private readonly string _pad; public BestandLogger(string pad) => _pad = pad; public void Info(string m) => File.AppendAllText(_pad, $"{DateTime.Now:s} [INFO] {m}\n"); public void Fout(string m) => File.AppendAllText(_pad, $"{DateTime.Now:s} [FOUT] {m}\n"); } // gebruik bedrijf.Logger = new BestandLogger("log.txt");
Uitleg
De klasse die logt kent alleen de interface ILogger. Daardoor kun je de console-logger vervangen door een bestand-logger zonder één regel in die klasse te wijzigen: dat is het Open/Closed-principe in actie.
Functies en keywords
interface- Contract: een lijst met leden die een klasse belooft te leveren, zonder implementatie.
File.AppendAllText- Voegt tekst toe aan het einde van een bestand (maakt het aan als het niet bestaat).
private- Alleen zichtbaar binnen deze klasse.
readonly- Veld mag alleen bij de declaratie of in de constructor een waarde krijgen.
=>- Korte schrijfwijze voor een methode of property die één expressie teruggeeft; in LINQ ook de pijl van een lambda.
$"...{x}"- String-interpolatie: zet de waarde van
xdirect in de tekst.
#.NET ILogger<T> (Microsoft.Extensions.Logging)
public class BestellingService { private readonly ILogger<BestellingService> _logger; public BestellingService(ILogger<BestellingService> logger) => _logger = logger; public void Plaats(int klantId) => _logger.LogInformation("Bestelling geplaatst voor {KlantId}", klantId); }
Uitleg
De standaard logger van .NET. Hij wordt via de constructor geïnjecteerd en het type tussen haakjes wordt de categorie, zodat je ziet waar een regel vandaan komt. Gebruik placeholders zoals {KlantId} in plaats van string-interpolatie, dan blijven de waarden doorzoekbaar.
Functies en keywords
LogInformation- Schrijft een logregel op niveau Information, met placeholders voor waarden.
private- Alleen zichtbaar binnen deze klasse.
readonly- Veld mag alleen bij de declaratie of in de constructor een waarde krijgen.
=>- Korte schrijfwijze voor een methode of property die één expressie teruggeeft; in LINQ ook de pijl van een lambda.
| Level | Wanneer |
|---|---|
| Trace | extreem gedetailleerd (waarde in elke loop-iteratie) |
| Debug | bug opsporen (cache-miss voor product 17) |
| Information | normale gebeurtenis (gebruiker logt in, bestelling geplaatst) |
| Warning | onverwacht maar herstelt (retry na time-out, mislukte login) |
| Error | actie mislukt (opslaan gooit exception) |
| Critical | app/kernfunctie ligt eruit (database onbereikbaar bij opstart) |
- Message template, geen interpolatie:
"Bestelling {Id}", idi.p.v.$"Bestelling {id}"(velden blijven doorzoekbaar, analyzer CA2254). - Exception als eerste argument:
_logger.LogError(ex, "Opslaan mislukt voor {KlantId}", klantId); - Nooit wachtwoorden of gevoelige data loggen.
- Naam botst? Eigen
ILoggeren die van Microsoft heten hetzelfde. In een kale console-app zonder dat package geen probleem.