Skip to content

feat: ROPS-1356 enhance CatchException decorator with log level support#54

Open
iago-f-s-e wants to merge 2 commits intomainfrom
feature/ROPS-0000/add-log-levels
Open

feat: ROPS-1356 enhance CatchException decorator with log level support#54
iago-f-s-e wants to merge 2 commits intomainfrom
feature/ROPS-0000/add-log-levels

Conversation

@iago-f-s-e
Copy link
Collaborator

@iago-f-s-e iago-f-s-e commented Dec 18, 2025

Description

This PR introduces the level option to the @CatchException decorator, allowing developers to specify different log levels for exceptions based on their severity. This addresses the need to differentiate between critical errors and expected exceptions like business rule violations.

Previously, all exceptions were logged as error level, which made it difficult to distinguish between critical system failures and expected application flows (e.g., validation errors, business rule violations).

ROPS-1356

Changes Made

  • Added LogLevel type: Created 'error' | 'warn' | 'info' | 'debug' type in src/lib/core/dtos/registered-error.dto.ts
  • Extended CatchExceptionOptions: Added level property that accepts:
    • A fixed LogLevel value
    • A dynamic function (exception: any, context?: any, ...params: any[]) => LogLevel
  • Updated RegisteredErrorDTO: Added level: LogLevel field to preserve log level when using typeErrorHandling: 'REGISTER'
  • Enhanced @CatchException decorator: Implements log level determination and uses appropriate logger method (error, warn, info, debug)
  • Updated AsyncTraceStorage: Modified registeredError setter to use complete RegisteredErrorDTO type

Checklist

  • My code follows the project's coding guidelines.
  • I have tested my changes thoroughly.
  • I have updated the project documentation if necessary.
  • I have added comments or documentation to explain complex parts of the code.
  • All existing tests pass successfully.

Additional Notes

Usage Examples

Fixed Level

@CatchException({ 
  kind: 'Domain',
  level: 'warn' // Business rule violations
})
public validateUserAge(age: number) {
  if (age < 18) throw new Error('User must be 18 or older');
}

Dynamic Level

@CatchException({ 
  kind: 'Domain',
  level: (error) => {
    if (error instanceof BusinessRuleError) return 'warn';
    if (error instanceof ValidationError) return 'info';
    return 'error';
  }
})
public processPayment(data: PaymentData) {
  // ... implementation
}

@iago-f-s-e iago-f-s-e self-assigned this Dec 18, 2025
Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌴 Atualmente, não há cobertura de testes para os diferentes níveis de log. Ter esses testes é importante para garantir que a funcionalidade esteja funcionando como esperado e para evitar regressões.

Alguns pontos que talvez valha a pena explorarmos ao pensar nesses testes:

  • Possíveis formas de validar se cada nível (warn, info, debug, error) está sendo aplicado corretamente quando especificado.

  • Como poderíamos abordar testes para o caso em que o nível é definido por uma função dinâmica.

  • De que maneira podemos verificar se o nível de log continua sendo preservado quando usado em conjunto com AsyncTraceStorage e typeErrorHandling: 'REGISTER'.

  • Quais cenários de borda fariam sentido cobrir (por exemplo, função retornando um nível inválido, nível não informado, etc.).

): void {
switch (level) {
case 'warn':
logger.warn(title || error.message, error, ...params);
Copy link

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?


if (options?.typeErrorHandling === 'REGISTER') {
AsyncTraceStorage.registeredError = { error, trigger: logger.trigger, title, params };
AsyncTraceStorage.registeredError = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌴 A função catchException (apesar de já estar deprecated) ainda não foi atualizada para suportar a opção level. Atualmente, ela sempre utiliza logger.error() e registra os logs com level: 'error'.

Isso acaba gerando uma inconsistência em relação ao decorator @CatchException. Além disso, usuários que ainda dependem de catchException não conseguem se beneficiar da nova funcionalidade de configuração de nível de log.

Alguns pontos que podemos avaliar:

  • Como garantir que a função catchException respeite a opção level quando ela for informada?

  • Existe a possibilidade de reutilizar a lógica que já está implementada no decorator @CatchException?

  • Mesmo sendo deprecated, faz sentido manter consistência de comportamento entre as duas abordagens?

@ghost
Copy link

ghost commented Dec 24, 2025

🌴 Talvez valha a pena avaliarmos uma possível refatoração da interface LoggerService para dar um suporte melhor ao log de erros em níveis diferentes de error. Hoje, os métodos warn, info e debug aceitam apenas mensagem e parâmetros, sem a possibilidade de receber diretamente um objeto de erro. Isso acaba exigindo que as informações da exceção sejam incorporadas manualmente ao padrão de log quando usamos esses níveis.

Alguns pontos que podem servir como base para discussão:

  • Possível extensão do LogPatternDTO para incluir informações de erro.
  • Uso de uma função auxiliar para formatar erros em níveis não-error, mantendo compatibilidade.
  • Preservação de dados importantes do erro (stack trace, name, kind) em warn, info e debug.
  • Avaliar se métodos sobrecarregados fazem sentido ou se há uma alternativa mais simples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant