Added error logging guidelines and best practices to code style documentation.

- 📚 **Docs**: Introduced error logging section with guidelines
-  **Feature**: Added context-aware logging examples
- 🛠️ **Refactor**: Improved structure for error handling practices
This commit is contained in:
Lorenz Hilpert
2025-04-16 14:12:26 +02:00
parent 2efc5c3b0d
commit c9b5af7282

View File

@@ -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<User[]>('/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.