TL;DR: Logging ist in Angular weit mehr als console.log(). Mit einem eigenen TypeScript-Decorator loggst du automatisch Methodenaufrufe, ein LoggerService bringt Struktur rein, und mit Sentry oder LogRocket schickst du Logs direkt in die Cloud. Hier erfährst du alles, was du für professionelles Logging brauchst.In der Softwareentwicklung spielt das Logging eine entscheidende Rolle, um die Ausführung von Programmen zu überwachen und Fehler schnell zu identifizieren. Insbesondere in großen Anwendungen ist ein effektives Logging unverzichtbar. Dieser Artikel stellt ein Logging-Decorator für Angular vor, der es ermöglicht, Methodenaufrufe und deren Rückgabewerte komfortabel zu protokollieren.
🔧 Der Code im Detail
Der bereitgestellte Code nutzt TypeScript und die Angular-Entwicklungsumgebung, um ein flexibles Logging-System zu erstellen. Er besteht aus mehreren Teilen, die jeweils spezifische Aufgaben erfüllen:
Import der Entwicklungsmodus-Funktion
import { isDevMode } from '@angular/core';
Diese Zeile importiert die isDevMode-Funktion aus dem Angular-Kernmodul, um festzustellen, ob die Anwendung im Entwicklungsmodus läuft.
Konstanten für das Log-Format
import { isDevMode } from '@angular/core';
export const logFormat = 'font-size:11px;display:block;border-left:5px solid {{color}};padding-left:5px;';
const startColor = 'green';
const endColor = 'red';
export function Log(whitelist?: Array<string>, blacklist?: Array<string>, logType: logType = { type: 'both' }): ClassDecorator {
return function (cls: any) {
const properties = whitelist || Object.getOwnPropertyNames(cls.prototype);
properties.forEach(propName => {
if (shouldLog(propName, whitelist, blacklist)) {
log(cls, propName, logType);
}
});
};
}
function shouldLog(propName: string, whitelist?: Array<string>, blacklist?: Array<string>): boolean {
const isBlacklisted = blacklist?.some(f => f.toLowerCase() === propName.toLowerCase());
return !isBlacklisted && (!whitelist || whitelist.includes(propName));
}
function log(cls: any, fnName: string, logType: logType) {
if (!isDevMode()) return;
const original = cls.prototype[fnName];
if (typeof original === 'function') {
cls.prototype[fnName] = function (...args) {
if (logType.type === 'in' || logType.type === 'both') {
console.log(`%c${cls.name}.${fnName}():`, logFormat.replace('{{color}}', startColor), ...args);
}
const ret = original.apply(this, args);
if (logType.type === 'out' || logType.type === 'both') {
console.log(`%c${cls.name}.${fnName}()`, logFormat.replace('{{color}}', endColor), ret);
}
return ret;
};
}
}
export interface logLine {
logging: boolean;
}
export interface logType {
type: 'in' | 'out' | 'both';
}
🏗️ Den Decorator in einer Komponente verwenden
Jetzt wird es spannend: Du kannst den @Log()-Decorator direkt auf eine Angular-Komponente anwenden. Damit werden automatisch alle Methodenaufrufe geloggt – ganz ohne manuelles console.log() in jeder Methode.
import { Component, OnInit } from '@angular/core';
import { Log } from './log.decorator';
@Log()
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
})
export class DashboardComponent implements OnInit {
ngOnInit(): void {
this.loadData();
}
loadData(): void {
// Deine Logik hier...
console.log('Daten werden geladen');
}
calculateSum(a: number, b: number): number {
return a + b;
}
}
Sobald du die Komponente im Entwicklungsmodus aufrufst, siehst du in der Konsole automatisch:
- Grüner Balken: Methodenaufruf mit übergebenen Argumenten
- Roter Balken: Rückgabewert der Methode
Du kannst den Decorator auch mit einer Whitelist oder Blacklist nutzen, um nur bestimmte Methoden zu loggen:
// Nur loadData() loggen
@Log(['loadData'])
// Alles außer ngOnInit() loggen
@Log(undefined, ['ngOnInit'])
// Nur Eingabeparameter loggen, keine Rückgabewerte
@Log(undefined, undefined, { type: 'in' })
📋 Überblick über die wichtigsten Console-Methoden
Bevor wir tiefer einsteigen, hier ein Überblick über die wichtigsten Console-Methoden, die dir im Alltag helfen:
console.log(), console.warn(), console.error()
Die Klassiker – für verschiedene Log-Level:
console.log('📝 Info: Benutzer eingeloggt');
console.warn('⚠️ Warnung: API-Antwort langsam');
console.error('❌ Fehler: Netzwerk nicht erreichbar');
console.table()
Perfekt für Arrays und Objekte – stellt Daten als Tabelle dar:
const users = [
{ name: 'Anna', role: 'Admin' },
{ name: 'Max', role: 'User' },
{ name: 'Lisa', role: 'Editor' },
];
console.table(users);
console.group() und console.groupEnd()
Gruppiere zusammengehörige Logs für bessere Übersicht:
console.group('🔄 HTTP Request');
console.log('URL: /api/users');
console.log('Method: GET');
console.log('Status: 200');
console.groupEnd();
console.time() und console.timeEnd()
Miss die Ausführungszeit von Code-Blöcken:
console.time('Datenladen');
await this.httpClient.get('/api/data').toPromise();
console.timeEnd('Datenladen');
// Ausgabe: Datenladen: 142.5ms
🏛️ Strukturiertes Logging mit einem LoggerService
Für größere Projekte reicht console.log() allein nicht aus. Ein zentraler LoggerService gibt dir Kontrolle über Log-Level, Formatierung und Ausgabekanäle:
import { Injectable, isDevMode } from '@angular/core';
export enum LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3,
OFF = 4,
}
@Injectable({ providedIn: 'root' })
export class LoggerService {
private level: LogLevel = isDevMode() ? LogLevel.DEBUG : LogLevel.WARN;
setLevel(level: LogLevel): void {
this.level = level;
}
debug(message: string, ...data: any[]): void {
if (this.level <= LogLevel.DEBUG) {
console.log(`[DEBUG] ${this.timestamp()} ${message}`, ...data);
}
}
info(message: string, ...data: any[]): void {
if (this.level <= LogLevel.INFO) {
console.log(`[INFO] ${this.timestamp()} ${message}`, ...data);
}
}
warn(message: string, ...data: any[]): void {
if (this.level <= LogLevel.WARN) {
console.warn(`[WARN] ${this.timestamp()} ${message}`, ...data);
}
}
error(message: string, ...data: any[]): void {
if (this.level <= LogLevel.ERROR) {
console.error(`[ERROR] ${this.timestamp()} ${message}`, ...data);
}
}
private timestamp(): string {
return new Date().toISOString();
}
}
Den Service nutzt du dann in deinen Komponenten per Dependency Injection:
import { Component, OnInit } from '@angular/core';
import { LoggerService } from './logger.service';
@Component({
selector: 'app-orders',
templateUrl: './orders.component.html',
})
export class OrdersComponent implements OnInit {
constructor(private logger: LoggerService) {}
ngOnInit(): void {
this.logger.info('OrdersComponent initialisiert');
this.loadOrders();
}
loadOrders(): void {
this.logger.debug('Lade Bestellungen...');
try {
// HTTP-Call...
this.logger.info('Bestellungen erfolgreich geladen', { count: 42 });
} catch (error) {
this.logger.error('Fehler beim Laden der Bestellungen', error);
}
}
}
Der große Vorteil: Im Production-Build werden automatisch nur Warnungen und Fehler geloggt, während im Dev-Modus alle Details sichtbar sind.
🌐 Integration mit externen Logging-Services
Für produktionsreife Anwendungen willst du Logs nicht nur in der Browser-Konsole sehen. Externe Services wie Sentry und LogRocket helfen dir, Fehler in Echtzeit zu tracken.
Sentry-Integration
Sentry fängt Fehler automatisch ab und gruppiert sie intelligent. Die Integration in Angular ist einfach:
import * as Sentry from '@sentry/angular';
// In deinem app.config.ts oder app.module.ts
Sentry.init({
dsn: 'https://[email protected]/projekt-id',
integrations: [
Sentry.browserTracingIntegration(),
],
tracesSampleRate: 1.0,
environment: isDevMode() ? 'development' : 'production',
});
Du kannst den LoggerService erweitern, um Fehler automatisch an Sentry zu senden:
error(message: string, ...data: any[]): void {
if (this.level <= LogLevel.ERROR) {
console.error(`[ERROR] ${this.timestamp()} ${message}`, ...data);
Sentry.captureException(data[0] instanceof Error ? data[0] : new Error(message));
}
}
LogRocket-Integration
LogRocket zeichnet Benutzersitzungen auf und verknüpft Logs mit dem, was der User tatsächlich gesehen hat:
import LogRocket from 'logrocket';
LogRocket.init('dein-app-id/projekt');
// Im LoggerService
error(message: string, ...data: any[]): void {
if (this.level <= LogLevel.ERROR) {
console.error(`[ERROR] ${this.timestamp()} ${message}`, ...data);
LogRocket.captureException(data[0] instanceof Error ? data[0] : new Error(message));
}
}
✅ Best Practices für Logging in Angular
Hier sind die wichtigsten Regeln, die du beim Logging beachten solltest:
1. Nutze Log-Level konsequent
- DEBUG: Detaillierte Informationen für die Entwicklung
- INFO: Wichtige Geschäftsereignisse (Login, Bestellung, etc.)
- WARN: Unerwartetes Verhalten, das aber kein Fehler ist
- ERROR: Echte Fehler, die Aufmerksamkeit erfordern
2. Unterscheide zwischen Production und Development
Im Development-Modus willst du so viele Informationen wie möglich. In Production solltest du nur das Nötigste loggen:
// Im LoggerService bereits eingebaut:
private level: LogLevel = isDevMode() ? LogLevel.DEBUG : LogLevel.WARN;
Damit stellst du sicher, dass keine sensiblen Daten in der Produktionskonsole landen und die Performance nicht leidet.
3. Achte auf Performance
- Vermeide exzessives Logging in Schleifen oder häufig aufgerufenen Methoden
- Nutze
console.time()um Performance-Bottlenecks zu identifizieren - Deaktiviere Debug-Logs in Production über den LoggerService
- Verwende Tree-Shaking-freundliche Imports für externe Logging-Bibliotheken
4. Logge strukturierte Daten
Statt einzelner Strings solltest du strukturierte Objekte loggen – das macht die Analyse einfacher:
// ❌ Schlecht
this.logger.info('User logged in: ' + user.email);
// ✅ Gut
this.logger.info('User logged in', { email: user.email, role: user.role, timestamp: Date.now() });
5. Logge keine sensiblen Daten
Achte darauf, dass du niemals Passwörter, Tokens oder persönliche Daten in Logs schreibst. Nutze Filtering oder Masking:
// ❌ Niemals
this.logger.debug('Auth Token:', token);
// ✅ Sicher
this.logger.debug('Auth Token:', token.substring(0, 8) + '***');
🎯 Fazit
Logging in Angular ist mehr als nur console.log(). Mit dem richtigen Setup hast du ein mächtiges Werkzeug zur Fehlersuche und Überwachung:
- Ein TypeScript-Decorator automatisiert das Logging auf Klassenebene
- Die Console-API bietet weit mehr als nur
.log()– nutze.table(),.group()und.time() - Ein LoggerService bringt Struktur und Kontrolle über Log-Level
- Externe Services wie Sentry und LogRocket machen dein Logging produktionsreif
- Best Practices sorgen dafür, dass dein Logging sicher, performant und nützlich bleibt
Fang am besten mit dem Decorator und dem LoggerService an – du wirst schnell merken, wie viel einfacher das Debugging wird! 🚀
Mehr Artikel entdecken
Logging in Angular: A Powerful Tool for Debugging and Monitoring 🕵️
Logging in Angular is essential. From TypeScript decorators to structured LoggerServices – here's how to debug efficiently.
Angular und TailwindCSS
Angular und TailwindCSS sind ein starkes Duo. So richtest du Tailwind in deinem Angular-Projekt ein und nutzt Utility-First CSS effektiv.
Angular input() für Route-Parameter: Schluss mit ActivatedRoute 🚀
Mit Angular 16+ kannst du Route-Parameter direkt per input() in deine Komponente binden – ganz ohne ActivatedRoute. So geht's!
Angular: Framework für Single Page Applications🌐
Entdecke Angular, das leistungsstarke Framework von Google, ideal für dynamische Webanwendungen. Erfahre mehr über Komponenten, Datenbindung und Routing! 🌐