From c9b5af728267e33d4872329b5c900007d0b9722f Mon Sep 17 00:00:00 2001 From: Lorenz Hilpert Date: Wed, 16 Apr 2025 14:12:26 +0200 Subject: [PATCH] Added error logging guidelines and best practices to code style documentation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 📚 **Docs**: Introduced error logging section with guidelines - ✨ **Feature**: Added context-aware logging examples - 🛠️ **Refactor**: Improved structure for error handling practices --- docs/guidelines/code-style.md | 197 ++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/docs/guidelines/code-style.md b/docs/guidelines/code-style.md index dc6a70456..5ff50c5fd 100644 --- a/docs/guidelines/code-style.md +++ b/docs/guidelines/code-style.md @@ -487,6 +487,203 @@ function getUser(id) { } ``` +## Error Logging + +Proper error logging is critical for diagnosing issues in production applications. Always use the `@isa/core/logging` module for logging throughout the application. + +### Basic Logging Guidelines + +- Always use the appropriate log level for the situation: + - `trace` - For fine-grained debugging information + - `debug` - For development-time debugging information + - `info` - For general runtime information + - `warn` - For potential issues that don't interrupt operation + - `error` - For errors that affect functionality + +### Setting Up Logging + +Use the logger factory function to create loggers in your components and services: + +```typescript +import { logger } from '@isa/core/logging'; + +@Component({ + selector: 'app-example', + templateUrl: './example.component.html', +}) +export class ExampleComponent implements OnInit { + // Create a private logger instance + #logger = logger(); + + constructor(private userService: UserService) {} + + ngOnInit(): void { + this.#logger.info('Component initialized'); + } +} +``` + +### Context-Aware Logging + +Always provide relevant context when logging: + +```typescript +// Good - With detailed context +this.#logger.error( + 'Failed to load user data', + error, + { userId: '123', attempt: 2, source: 'UserProfileComponent' } +); + +// Bad - No context for troubleshooting +this.#logger.error('Failed to load user data'); +``` + +### Component and Service Level Context + +Use the `provideLoggerContext` to set component-level context: + +```typescript +@Component({ + selector: 'app-user-profile', + templateUrl: './user-profile.component.html', + providers: [ + provideLoggerContext({ + component: 'UserProfile', + section: 'Account' + }) + ] +}) +export class UserProfileComponent { + #logger = logger(); + + // All logs from this component will include the context + updateProfile(): void { + this.#logger.info('Updating user profile'); + // Log output will include { component: 'UserProfile', section: 'Account' } + } +} +``` + +### Error Handling Best Practices + +1. **Always log the original Error object**: + +```typescript +// Good - Includes the original error with stack trace +try { + // risky operation +} catch (error) { + this.#logger.error('Operation failed', error, { context: 'additional data' }); +} + +// Bad - Loses the stack trace and error details +try { + // risky operation +} catch (error) { + this.#logger.error(`Operation failed: ${error}`); +} +``` + +2. **Structure error handling with contextual information**: + +```typescript +saveData(data: UserData): void { + this.userService.save(data).pipe( + catchError((error) => { + this.#logger.error( + 'Failed to save user data', + error, + { + userId: data.id, + dataSize: JSON.stringify(data).length, + operation: 'saveData' + } + ); + + // Handle the error appropriately + return throwError(() => new Error('Failed to save data')); + }) + ).subscribe(); +} +``` + +3. **Use log levels appropriately**: + +```typescript +// Debug information during development +this.#logger.debug('Processing data batch', { batchSize: items.length }); + +// General information during runtime +this.#logger.info('User logged in', { userId: user.id }); + +// Potential issues that don't break functionality +this.#logger.warn('API response slow', { responseTime: '2500ms', endpoint: '/users' }); + +// Errors that affect functionality +this.#logger.error('Payment processing failed', error, { orderId: order.id }); +``` + +4. **In RxJS operators, use tap for logging**: + +```typescript +return this.http.get('/api/users').pipe( + tap({ + next: (users) => this.#logger.info('Users loaded', { count: users.length }), + error: (error) => this.#logger.error('Failed to load users', error) + }), + catchError((error) => { + this.#logger.error('Error in users API call', error, { retry: true }); + return this.fallbackUserService.getUsers(); + }) +); +``` + +5. **Log lifecycle events when relevant**: + +```typescript +export class ImportantComponent implements OnInit, OnDestroy { + #logger = logger(); + + ngOnInit(): void { + this.#logger.debug('Component initialized'); + } + + ngOnDestroy(): void { + this.#logger.debug('Component destroyed'); + } +} +``` + +### Performance Considerations + +- Avoid expensive operations in log messages in production: + +```typescript +// Bad - Performs expensive operation even if debug level is disabled +this.#logger.debug('Data state:', JSON.stringify(this.largeDataObject)); + +// Good - Only performs expensive operation if needed +if (isDevMode()) { + this.#logger.debug('Data state:', { data: this.largeDataObject }); +} +``` + +- Consider log level in production environments: + +```typescript +// In app.config.ts +export const appConfig: ApplicationConfig = { + providers: [ + // Other providers... + provideLogging( + withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn), + withSink(ConsoleLogSink) + ) + ] +}; +``` + ## Project-Specific Preferences - **Frameworks**: Follow best practices for Nx, Angular, date-fns, Ngrx, RxJs and Zod.