Come scrivere un messaggio di buona eccezione

96

Attualmente sto facendo una revisione del codice e una delle cose che sto notando sono il numero di eccezioni in cui il messaggio di eccezione sembra solo reiterare dove si è verificata l'eccezione. per es.

throw new Exception("BulletListControl: CreateChildControls failed.");

Tutti e tre gli elementi in questo messaggio posso lavorare fuori dal resto dell'eccezione. Conosco la classe e il metodo dalla traccia dello stack e so che non è riuscito (perché ho un'eccezione).

Mi ha fatto pensare a quale messaggio ho inserito nei messaggi di eccezione. Per prima cosa creo una classe di eccezioni, se non ne esiste già una, per il motivo generale (ad esempio PropertyNotFoundException - il perché ), e poi quando lancio il messaggio indica cosa è andato storto (ad esempio " Impossibile trovare la proprietà 'IDontExist' sul nodo 1234 "- il cosa ). Il dove si trova in StackTrace . quando può finire nel log (se applicabile). come è per lo sviluppatore di risolvere (e risolvere)

Hai altri suggerimenti per lanciare eccezioni? Specificamente per quanto riguarda la creazione di nuovi tipi e il messaggio di eccezione.

    
posta Colin Mackay 23.12.2010 - 12:43
fonte

7 risposte

67

Dirò di più la mia risposta a ciò che viene dopo un'eccezione: a cosa serve e come dovrebbe comportarsi il software, cosa dovrebbero fare gli utenti con l'eccezione? Una grande tecnica che ho incontrato all'inizio della mia carriera è stata quella di segnalare sempre problemi ed errori in 3 parti: contesto, problema e amp; soluzione. L'uso di questa diciplina modifica enormemente la gestione degli errori e rende il software di gran lunga migliore per gli operatori.

Ecco alcuni esempi.

Context: Saving connection pooling configuration changes to disk.
Problem: Write permission denied on file '/xxx/yyy'.
Solution: Grant write permission to the file.

In questo caso, l'operatore sa esattamente cosa fare e su quale file deve essere interessato. Sanno anche che le modifiche al pooling delle connessioni non hanno richiesto e dovrebbero essere ripetute.

Context: Sending email to '[email protected]' regarding 'Blah'.
Problem: SMTP connection refused by server 'mail.xyz.com'.
Solution: Contact the mail server administrator to report a service problem.  The email will be sent later. You may want to tell '[email protected]' about this problem.

Scrivo sistemi lato server e i miei operatori sono generalmente di supporto tecnico di prima linea. Scriverei i messaggi in modo diverso per i software desktop che hanno un pubblico diverso ma includono le stesse informazioni.

Molte cose meravigliose accadono se si utilizza questa tecnica. Lo sviluppatore software è spesso nella posizione migliore per sapere come risolvere i problemi nel proprio codice in modo da codificare le soluzioni in questo modo mentre si scrive il codice è di enorme beneficio per gli utenti che sono in svantaggio nel trovare soluzioni poiché spesso mancano informazioni su cosa stava facendo esattamente il software. Chiunque abbia mai letto un messaggio di errore di Oracle saprà cosa intendo.

La seconda cosa meravigliosa che viene in mente è quando ti ritrovi a cercare di descrivere una soluzione nella tua eccezione e stai scrivendo "Controlla X e se A poi B else C". Questo è un segno molto chiaro ed evidente che la tua eccezione viene controllata nel posto sbagliato. Il programmatore ha la capacità di confrontare le cose nel codice così "se" le istruzioni dovrebbero essere eseguite nel codice, perché coinvolgere l'utente in qualcosa che può essere automatizzato? È probabile che provenga da una parte più profonda del codice e qualcuno abbia fatto la cosa pigra e generato IOException da un numero qualsiasi di metodi e individuato potenziali errori da parte di tutti in un blocco di codice chiamante che non può descrivere adeguatamente cosa è andato storto, cosa contesto specifico e come risolverlo. Questo ti incoraggia a scrivere errori di grana più fine, a catturarli e gestirli nel posto giusto nel tuo codice in modo che tu possa articolare correttamente i passi che l'operatore dovrebbe compiere.

In una società abbiamo avuto operatori di prim'ordine che hanno avuto modo di conoscere il software molto bene e hanno mantenuto il loro "run book" che ha aumentato il nostro report degli errori e le soluzioni suggerite. Per riconoscere questo, il software ha iniziato a includere collegamenti wiki al run book in eccezioni, in modo che fosse disponibile una spiegazione di base e collegamenti a discussioni e osservazioni più avanzate da parte degli operatori nel tempo.

Se hai la diciplina per provare questa tecnica, diventa molto più ovvio che dovresti nominare le tue eccezioni nel codice quando crei il tuo. NonRecoverableConfigurationReadFailedException diventa un po 'di stenografia per ciò che stai per descrivere in modo più completo all'operatore. Mi piace essere prolisso e penso che sarà più facile per il prossimo sviluppatore che tocca il mio codice per interpretare.

    
risposta data 23.12.2010 - 13:14
fonte
22

In questa domanda più recente ho sottolineato che le eccezioni non dovrebbero contenere alcun messaggio. A mio parere, il fatto che lo facciano è un enorme equivoco. Quello che sto proponendo è che

The "message" of the exception is the (fully qualified) class name of the exception.

Un'eccezione dovrebbe contenere all'interno delle proprie variabili membro quanti più dettagli possibili su esattamente cosa è successo; per esempio, un IndexOutOfRangeException dovrebbe contenere il valore dell'indice che è stato trovato non valido, così come i valori superiore e inferiore che erano validi nel momento in cui l'eccezione è stata lanciata. In questo modo, usando la reflection puoi avere un messaggio costruito automaticamente che si legge così: IndexOutOfRangeException: index = -1; min=0; max=5 e questo, insieme alla traccia dello stack, dovrebbe essere tutte le informazioni oggettive di cui hai bisogno per risolvere il problema. La formattazione in un messaggio carino come "indice -1 non era tra 0 e 5" non aggiunge alcun valore.

Nel tuo particolare esempio, la classe NodePropertyNotFoundException conterrà il nome della proprietà che non è stata trovata e un riferimento al nodo che non contiene la proprietà. Questo è importante: dovrebbe non contenere il nome del nodo; dovrebbe contenere un riferimento al nodo attuale. Nel vostro caso particolare, questo potrebbe non essere necessario, ma è una questione di principio e un modo di pensare preferito: la preoccupazione principale quando si costruisce un'eccezione è che deve essere utilizzabile dal codice che potrebbe catturarlo. L'usabilità da parte dell'uomo è un'importante, ma solo una preoccupazione secondaria.

Questo si prende cura della situazione molto frustrante alla quale potresti essere stato testimone in qualche momento della tua carriera, in cui potresti aver colto un'eccezione contenente informazioni vitali su ciò che accadeva nel testo del messaggio, ma non all'interno delle sue variabili membro , quindi hai dovuto eseguire il parsing di stringhe del testo per capire cosa è successo, sperando che il testo del messaggio rimanga lo stesso nelle versioni future del livello sottostante e pregando che il testo del messaggio non sia in qualche lingua straniera quando il tuo programma viene eseguito in altri Paesi.

Naturalmente, poiché il nome classe dell'eccezione è il messaggio dell'eccezione (e le variabili membro dell'eccezione sono i dettagli specifici), ciò significa che sono necessarie molte e molte eccezioni per trasmettere tutti i diversi messaggi, e va bene.

Ora, a volte, mentre scriviamo il codice, ci imbattiamo in una situazione errata per cui vogliamo semplicemente codificare rapidamente un'istruzione throw e continuare a scrivere il nostro codice invece di dover interrompere ciò che stiamo facendo per creare una nuova classe di eccezione in modo che possiamo lanciarlo proprio lì. Per questi casi, ho una classe GenericException che in effetti accetta un messaggio di stringa come parametro di costruzione, ma il costruttore di questa classe di eccezioni è decorato con un grande, enorme e luminoso commento FIXME XXX TODO che afferma che ogni singolo l'istanziazione di questa classe deve essere sostituita con un'istanza di alcune classi di eccezioni più specializzate prima che il sistema software venga rilasciato, preferibilmente prima che il codice venga eseguito.

    
risposta data 14.04.2015 - 13:31
fonte
13

Come regola generale, un'eccezione dovrebbe aiutare gli sviluppatori a individuare la causa fornendo informazioni utili (valori attesi, valore effettivo, possibili cause / soluzioni, ecc.).

Dovrebbero essere creati nuovi tipi di eccezione quando nessuno dei tipi predefiniti ha senso . Un tipo specifico consente ad altri sviluppatori di rilevare un'eccezione specifica e gestirla. Se lo sviluppatore sapesse come gestire la tua eccezione ma il tipo è Exception , non sarà in grado di gestirlo correttamente.

    
risposta data 23.12.2010 - 13:55
fonte
4

In .NET non sempre throw new Exception("...") (come l'autore della domanda ha mostrato). L'Eccezione è il tipo di eccezione di root e non dovrebbe mai essere gettato direttamente. Invece, lancia uno dei tipi di eccezione .NET derivati o crea la tua eccezione personalizzata che deriva da Exception (o da un altro tipo di eccezione).

Perché non lanciare l'eccezione? Perché lanciare Exception non fa nulla per descrivere la tua eccezione e costringe il tuo codice chiamante a scrivere codice come catch(Exception ex) { ... } che generalmente non è una buona cosa! : -).

    
risposta data 10.10.2016 - 17:48
fonte
2

Le cose che si desidera cercare di "aggiungere" all'eccezione sono quegli elementi di dati che non sono inerenti all'eccezione o allo stack trace. Se questi fanno parte del "messaggio" o devono essere allegati quando registrati è una domanda interessante.

Come hai già notato, l'eccezione ti dice proattivamente, lo stacktrace probabilmente ti dice dove ma il "perché" potrebbe essere più coinvolto (dovrebbe essere, si spera) che non andare semplicemente a guardare una linea o due e dicendo "doh! Naturalmente". Questo è ancora più vero quando si registrano errori nel codice di produzione: sono stato troppo spesso morso da dati errati che sono stati trovati in un sistema live che non esiste nei nostri sistemi di test. Qualcosa di semplice come sapere quale sia l'ID del record nel database che sta causando (o contribuendo) all'errore può far risparmiare parecchio tempo.

Quindi ... O elencati o, per .NET, aggiunti alla raccolta dei dati delle eccezioni registrati (c.f. @Plip!):

  • Parametri (questo può diventare un po 'interessante - non è possibile aggiungere alla raccolta dati se non verrà serializzato e talvolta un singolo parametro può essere sorprendentemente complesso)
  • I dati aggiuntivi restituiti da ADO.NET o da Linq a SQL o simili (anche questo può risultare un po 'interessante!).
  • Qualsiasi altra cosa potrebbe non essere evidente.

Alcune cose, naturalmente, non saprai di aver bisogno finché non le avrai nel rapporto / log iniziale di errore. Alcune cose che non ti rendi conto che puoi ottenere finché non trovi che ne hai bisogno.

    
risposta data 23.12.2010 - 16:55
fonte
0

Quali sono le eccezioni per ?

(1) Dire all'utente che qualcosa è andato storto?

Questa dovrebbe essere l'ultima risorsa, perché il tuo codice dovrebbe intercedere e mostrare loro qualcosa di "più bello" di un'eccezione.

Il messaggio "errore" dovrebbe indicare in modo chiaro e sintetico quale è andato storto e cosa, se non altro, l'utente può fare per recuperare dalla condizione di errore.

es. "Si prega di non premere nuovamente questo pulsante"

(2) Dire a uno sviluppatore quando è andato storto?

Questo è il tipo di cosa che si accede a un file per l'analisi successiva.
Stack Trace dirà allo sviluppatore dove il codice si è rotto; il messaggio dovrebbe, ancora una volta, indicare cosa è andato storto.

(3) Dire ad un gestore di eccezioni (cioè codice) che qualcosa è andato storto?

Il Tipo dell'eccezione deciderà quale gestore di eccezioni avrà a guardarlo e le proprietà definite sull'oggetto Exception consentiranno al gestore di gestirlo.

Il messaggio di Eccezione è totalmente irrilevante .

    
risposta data 11.10.2016 - 13:14
fonte
-3

Non creare nuovi tipi se puoi aiutarlo. Possono causare ulteriore confusione, complessità e portare a più codice da mantenere. Potrebbero rendere il tuo codice esteso. La progettazione di una gerarchia di eccezioni richiede molto tempo e prove. Non è un ripensamento. Spesso è preferibile utilizzare la gerarchia delle eccezioni della lingua incorporata.

Il contenuto del messaggio di eccezione dipende dal destinatario del messaggio - quindi devi metterti nei panni della persona.

Un tecnico di supporto dovrà essere in grado di identificare la fonte dell'errore il più rapidamente possibile. Includere una stringa descrittiva breve più eventuali dati che possono aiutare a risolvere il problema. Includere sempre la traccia dello stack, se possibile, questa sarà l'unica vera fonte di informazioni.

Presentare errori agli utenti generici del tuo sistema dipende dal tipo di errore: se l'utente può risolvere il problema, fornendo ad esempio input diversi, allora è richiesto un messaggio descrittivo conciso. Se l'utente non è in grado di risolvere il problema, è meglio dichiarare che si è verificato un errore e registrare / inviare un errore da supportare (seguendo le linee guida sopra riportate).

Inoltre - non lanciare un grande "HALT ERROR!" icona. È un errore: non è la fine del mondo.

Quindi in sintesi: pensa agli attori e ai casi d'uso del tuo sistema. Mettiti nei panni di quegli utenti. Sii utile. Sii gentile Pensa a questo in anticipo nella progettazione del tuo sistema. Dal punto di vista degli utenti, questi casi eccezionali e il modo in cui il sistema li gestisce sono importanti quanto i casi normali del sistema.

    
risposta data 23.12.2010 - 13:26
fonte

Leggi altre domande sui tag