-
Notifications
You must be signed in to change notification settings - Fork 1
feat: ROPS-1356 enhance CatchException decorator with log level support #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| # level - decorator | ||
|
|
||
| The `level` option allows you to specify the log level when an exception is caught. This is useful for differentiating between critical errors and expected exceptions like business rule violations. | ||
|
|
||
| ## Available Levels | ||
|
|
||
| - `'error'` (default): For critical errors that require immediate attention | ||
| - `'warn'`: For expected exceptions that should be monitored (e.g., business rule violations) | ||
| - `'info'`: For informational exceptions that are part of normal flow | ||
| - `'debug'`: For debugging purposes | ||
|
|
||
| --- | ||
|
|
||
| ## Use case | ||
|
|
||
| Not all exceptions are critical errors. For example, when a user tries to perform an action that violates a business rule, it's an expected exception that should be logged but not treated as a critical error. | ||
|
|
||
| ## Usage with fixed level | ||
|
|
||
| ```typescript | ||
| import { CatchException } from '@daki/logr' | ||
| import { BusinessRuleError } from './errors' | ||
|
|
||
| export class PaymentService { | ||
|
|
||
| // Business rule violation - use 'warn' instead of 'error' | ||
| @CatchException({ | ||
| kind: 'Domain', | ||
| level: 'warn' | ||
| }) | ||
| public async processPayment(amount: number, userId: string): Promise<void> { | ||
| if (amount < 0) { | ||
| throw new BusinessRuleError('Payment amount cannot be negative') | ||
| } | ||
|
|
||
| if (amount > 10000) { | ||
| throw new BusinessRuleError('Payment amount exceeds limit') | ||
| } | ||
|
|
||
| // Process payment... | ||
| } | ||
|
|
||
| // Critical system error - use 'error' (default) | ||
| @CatchException({ | ||
| kind: 'Infrastructure' | ||
| }) | ||
| public async connectToPaymentGateway(): Promise<void> { | ||
| // Connection logic that might fail critically | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Usage with dynamic level (function) | ||
|
|
||
| You can also provide a function that determines the log level based on the exception: | ||
|
|
||
| ```typescript | ||
| import { CatchException } from '@daki/logr' | ||
| import { BusinessRuleError, ValidationError, SystemError } from './errors' | ||
|
|
||
| export class OrderService { | ||
|
|
||
| @CatchException({ | ||
| kind: 'Domain', | ||
| level: (error) => { | ||
| // Business rules - expected exceptions | ||
| if (error instanceof BusinessRuleError) return 'warn' | ||
|
|
||
| // Validation errors - informational | ||
| if (error instanceof ValidationError) return 'info' | ||
|
|
||
| // System errors - critical | ||
| return 'error' | ||
| } | ||
| }) | ||
| public async createOrder(orderData: any): Promise<Order> { | ||
| // Validation | ||
| if (!orderData.items || orderData.items.length === 0) { | ||
| throw new ValidationError('Order must have at least one item') | ||
| } | ||
|
|
||
| // Business rule | ||
| if (orderData.totalAmount > this.getUserLimit(orderData.userId)) { | ||
| throw new BusinessRuleError('Order exceeds user limit') | ||
| } | ||
|
|
||
| // System operation that might fail | ||
| return await this.repository.save(orderData) | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Log output examples | ||
|
|
||
| ### With level: 'warn' | ||
| ```text | ||
| User not found { | ||
| "timestamp": "2023-09-12T22:45:13.468Z", | ||
| "level": "warn", | ||
| "logger": { | ||
| "name": "PaymentService", | ||
| "method_name": "processPayment", | ||
| "params": [-100, "user123"] | ||
| }, | ||
| "error": { | ||
| "name": "BusinessRuleError", | ||
| "message": "Payment amount cannot be negative", | ||
| "stack": {ErrorStack}, | ||
| "kind": "Domain" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### With level: 'error' (default) | ||
| ```text | ||
| Database connection failed { | ||
| "timestamp": "2023-09-12T22:45:13.468Z", | ||
| "level": "error", | ||
| "logger": { | ||
| "name": "PaymentService", | ||
| "method_name": "connectToPaymentGateway", | ||
| "params": [] | ||
| }, | ||
| "error": { | ||
| "name": "ConnectionError", | ||
| "message": "Database connection failed", | ||
| "stack": {ErrorStack}, | ||
| "kind": "Infrastructure" | ||
| } | ||
| } | ||
| ``` | ||
|
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| export type { ErrorDTO } from './error.dto'; | ||
| export type { ErrorPatternDTO, LogPatternDTO } from './patterns.dto'; | ||
| export type { RegisteredErrorDTO } from './registered-error.dto'; | ||
| export type { LogLevel, RegisteredErrorDTO } from './registered-error.dto'; | ||
| export type { TriggerInDTO, TriggerOutDTO } from './trigger.dto'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,10 @@ import { Logr } from '@core/services'; | |
| import { AsyncTraceStorage } from '@core/storages'; | ||
| import { CatchExceptionOptions } from '@core/types'; | ||
|
|
||
| /** | ||
| * @deprecated Support for the catchException high-order function will be discontinued soon. | ||
| * It is recommended to use the @CatchException decorator for exception handling instead. | ||
| */ | ||
| export function catchException<Fn extends (...args: any[]) => any>( | ||
| fn: Fn, | ||
| options?: CatchExceptionOptions, | ||
|
|
@@ -23,7 +27,13 @@ export function catchException<Fn extends (...args: any[]) => any>( | |
| } | ||
|
|
||
| if (options?.typeErrorHandling === 'REGISTER') { | ||
| AsyncTraceStorage.registeredError = { error, trigger: logger.trigger, title, params }; | ||
| AsyncTraceStorage.registeredError = { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🌴 A função Isso acaba gerando uma inconsistência em relação ao decorator Alguns pontos que podemos avaliar:
|
||
| error, | ||
| trigger: logger.trigger, | ||
| title, | ||
| params, | ||
| level: 'error' | ||
| }; | ||
|
|
||
| return; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🌴 Acredito que aqui as informações críticas do erro (stack trace, nome, kind) são perdidas quando logadas com níveis warn, info ou debug. O erro passado como parâmetro não será formatado corretamente no log, pois os métodos logger.warn(), logger.info() e logger.debug() esperam apenas message: string e ...params: any[], não um objeto de erro.
Para níveis não-error, precisamos incluir as informações do erro no log de forma adequada, similar ao que é feito no nível error com getErrorPattern(). Como podemos garantir que essas informações sejam preservadas?