Perché "log and throw" è considerato un anti-pattern? [chiuso]


89

Questa domanda è stata innescata da una discussione su questo articolo , in cui non ho ricevuto nessuna buona risposta.

Perché registrare la tua eccezione e poi rilanciarla (preservando la traccia dello stack originale ovviamente) dovrebbe essere una cattiva idea se non puoi gestirla diversamente?


Prova a cercare una risposta su softwareengineering.stackexchange.com
chharvey

Risposte:


129

Presumo che la risposta sia in gran parte perché lo prendi se non puoi gestirlo? Perché non lasciare che chiunque possa gestirlo (o chiunque non abbia altra scelta che gestirlo) lo registri, se ritengono che sia degno di nota?

Se lo prendi, lo registri e lo rilanci, non c'è modo per il codice upstream di sapere che hai già registrato l'eccezione, quindi la stessa eccezione potrebbe essere registrata due volte. O peggio, se tutto il codice a monte segue lo stesso schema, l'eccezione potrebbe essere registrata un numero arbitrario di volte, una volta per ogni livello del codice che decide di catturarlo, registrarlo e quindi lanciarlo di nuovo.

Inoltre, alcuni potrebbero obiettare che poiché lanciare e catturare eccezioni sono operazioni relativamente costose, tutto questo catturare e rilanciare non aiuta le prestazioni di runtime. Né aiuta il tuo codice in termini di concisione o manutenibilità.


per ulteriori elaborazioni, vedere i commenti sulla risposta di Jeff
Manu

8
Un motivo per registrare e rilanciare un'eccezione potrebbe essere quello di restringere il motivo e registrare un messaggio specifico.
Mike Argyriou

12
Risposta: potrebbero esserci utili informazioni di debug disponibili nel punto corrente nello stack che non saranno disponibili in un altro punto nella traccia dello stack
rtconner

La cattura e il rilancio a volte è utile se la nuova eccezione è più utile con più informazioni o più specifica.
Borjab

1
Per spendere ciò che ha commentato @rtconner: nel codice asincrono potrebbe essere che il catcher non abbia il contesto disponibile per il lanciatore.
Nir Alfasi

55

Log-and-throw è un buon modello se e solo se l'entità che cattura e rilancia l'eccezione ha motivo di credere che contenga informazioni che non verranno registrate più in alto nello stack di chiamate, almeno non nel modo più desiderato. Un paio di ragioni per cui ciò può verificarsi:

  1. L'eccezione può essere rilevata e lanciata di nuovo al limite del livello dell'applicazione e può contenere informazioni privilegiate. Sarebbe negativo per un livello di database consentire un'eccezione, ad esempio "Tentativo di aggiungere la chiave duplicata 'fnord' al campo 'utenti'" per raggiungere il livello dell'applicazione esterno (che potrebbe a sua volta esporlo a un utente), ma potrebbe essere utile per le parti interne del database per lanciare un'eccezione di questo tipo e l'interfaccia dell'applicazione per catturarla, registrarla in modo sicuro e rilanciare un'eccezione un po 'meno descrittiva.
  2. L'eccezione potrebbe essere quella che il livello esterno probabilmente si aspetterebbe di gestire senza registrazione, ma il livello interno potrebbe conoscere qualcosa che il livello esterno non fa, il che suggerirebbe che la registrazione potrebbe essere utile. Ad esempio, un livello di applicazione intermedio potrebbe essere programmato per provare a connettersi a un server e, se non funziona, provarne un altro. Inondare il registro dell'applicazione con messaggi di "connessione non riuscita" mentre un server è inattivo per manutenzione potrebbe non essere utile, soprattutto perché, dal punto di vista dell'applicazione, tutto ha funzionato correttamente. Può essere utile inoltrare informazioni sull'errore di connessione a una risorsa di logging associata ai server, che potrebbe quindi filtrare i log in modo da produrre un report di quando il server è andato su e giù, invece di un log di ogni singolo tentativo di connessione .

4
# 1 è davvero un caso d'uso generale ragionevole, tuttavia l'OP ha chiesto esplicitamente di "rilanciarlo (preservando la traccia dello stack originale ovviamente)", quindi # 1 non è la risposta giusta.
Geoffrey Zheng

@ GeoffreyZheng: Ciò dipenderà dal fatto che la registrazione sicura della traccia dello stack originale venga considerata come "conservazione".
supercat

3
Per quanto riguarda # 1: l'esposizione del contenuto dell'eccezione (come la visualizzazione di e.getMessage()/ stacktrace sull'interfaccia utente, nonché l'invio come risposta REST) ​​dovrebbe essere trattata come una vulnerabilità stessa, poiché l'eccezione di runtime può contenere qualsiasi tipo di informazione sensibile. Per quanto riguarda # 2: puoi rilanciare l'eccezione aggiungendo tutte le informazioni che vuoi che il client sappia (+ causa principale), non c'è bisogno di registrare nulla.
Nikita Bosik

@NikitaBosik: Indipendentemente dal fatto che le eccezioni debbano essere esposte o meno sul lato client, le applicazioni che lo fanno sono sufficientemente comuni che il principio della "sicurezza in profondità" suggerirebbe che i messaggi di eccezione dovrebbero essere cancellati dalle informazioni sensibili. Inoltre, se il livello esterno non si aspetta di scartare un particolare tipo di eccezione senza registrarlo né inoltrare informazioni a un'entità esterna che potrebbe essere interessata, avere un livello intermedio che aggiunge informazioni che il livello esterno ignorerà non aiuterà nulla.
supercat

20

Immagino che il motivo più semplice sia che in genere avresti un singolo gestore di primo livello che lo fa per te, quindi non è necessario inquinare il codice con questa gestione delle eccezioni.

L'argomento trasversale delle preoccupazioni è fondamentalmente che è una perdita di tempo a gestire gli errori che non ti riguardano. Molto meglio lasciare che l'errore si riempia di bolle fino a quando non si trova un gestore appropriato.

Secondo me, l'unica volta in cui dovresti catturare un'eccezione è quando puoi fare qualcosa di utile con i risultati. Catturare semplicemente il log non è utile, perché potresti centralizzare quel lavoro più in alto.


2
Sono d'accordo che dovresti avere un unico gestore di primo livello che lo fa per te. Ma a mio parere, quel gestore di primo livello DOVREBBE essere "log and throw" -ing. Quindi l'argomento è intorno A QUALE PUNTO "loggare e lanciare", non se farlo o non farlo affatto?!?
Manu

@Manu, immagino che il punto sia che se si tratta di un singolo gestore (centralizzato), va bene. Se stai duplicando il codice, non va bene. Non credo che ci sia altro oltre a questo!
Jeff Foster

5
@Manu - A cosa sta lanciando il gestore di primo livello in quel caso? Mi sembra che se c'è qualcosa a disposizione a cui lanciare, allora non è proprio il gestore di primo livello. E sicuramente non vuoi lanciare nuovamente un'eccezione nell'ambiente di runtime stesso. Ciò porterà la maggior parte delle applicazioni a un arresto anomalo.
aroth

1
@aroth: Capisco, quello che stai dicendo è "log and handle" (se sei il gestore di primo livello) o "non log e lancia (o gestisci altrimenti)" se non lo sei. Grazie per la segnalazione.
Manu

9

Il log and throw dell'IMO è una chiara violazione del principio della minima sorpresa.

Se l'eccezione viene gestita correttamente più in alto nello stack di chiamate, potrebbe non valere affatto la pena di una voce di registro degli errori. E poi è fonte di confusione trovare una voce di registro degli errori.


E i log non di errore?
Su Zhang

6
@ SuZhang Non capisco bene la tua domanda. Se non ci sono errori, non c'è niente da lanciare. Ovviamente puoi e dovresti scrivere log non di errore.
Bastian Voigt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.