Perché molti messaggi di eccezione non contengono dettagli utili?

213

Sembra che ci sia una certa quantità di accordo che i messaggi di eccezione dovrebbero essere contiene dettagli utili .

Perché molte eccezioni comuni ai componenti di sistema non contengono dettagli utili?

Alcuni esempi:

  • .NET List accesso indice ArgumentOutOfRangeException fa non indica il valore dell'indice che è stato provato e non è valido, né indica l'intervallo consentito.
  • Fondamentalmente tutti i messaggi di eccezione dalla libreria standard MSVC C ++ sono completamente inutili (nello stesso modo di cui sopra).
  • Eccezioni Oracle in .NET, che ti dicono (parafrasato) "TABLE OR VIEW not found", ma non quale uno.

Quindi, a me sembra che per la maggior parte i messaggi di eccezione non contengano dettagli sufficienti per essere utili. Le mie aspettative sono fuori linea? Sto usando le eccezioni errate che nemmeno me ne accorgo? O forse la mia impressione è sbagliata: la maggior parte delle eccezioni do fornisce effettivamente dettagli utili?

    
posta Martin Ba 13.04.2015 - 16:21
fonte

9 risposte

202

Le eccezioni non contengono dettagli utili perché il concetto di eccezioni non è ancora maturato nella disciplina dell'ingegneria del software, quindi molti programmatori non le comprendono completamente e quindi non le trattano correttamente.

Sì, IndexOutOfRangeException dovrebbe contenere l'indice preciso fuori intervallo, nonché l'intervallo che era valido al momento in cui è stato generato, ed è spregevole per conto dei creatori del runtime .NET che non è così. Sì, l'eccezione table or view not found di Oracle dovrebbe contenere il nome della tabella o della vista che non è stata trovata e, ancora una volta, il fatto che non sia spregevole per conto di chiunque sia responsabile di ciò.

In gran parte, la confusione deriva dall'idea errata originaria secondo cui le eccezioni dovrebbero contenere messaggi leggibili dall'uomo, il che a sua volta deriva dalla mancanza di comprensione di quali eccezioni riguardano, quindi è un circolo vizioso.

Poiché le persone pensano che l'eccezione debba contenere un messaggio leggibile dall'uomo, credono che qualsiasi informazione trasportata dall'eccezione debba anche essere formattata nel messaggio leggibile dall'uomo, e quindi sono annoiati a scrivere tutte le informazioni umane- codice di costruzione dei messaggi leggibile, o hanno paura che farlo potrebbe rivelare una quantità sconsigliabile di informazioni a qualunque sguardo indiscreto possa vedere il messaggio. (I problemi di sicurezza citati da altre risposte.)

Ma la verità è che non dovrebbero preoccuparsene perché l'eccezione dovrebbe non contenere un messaggio leggibile. Le eccezioni sono cose che solo i programmatori dovrebbero mai vedere e / o gestire. Se c'è mai la necessità di presentare le informazioni di errore a un utente, questo deve essere fatto ad un livello molto alto, in un modo sofisticato, e nella lingua dell'utente, che, statisticamente parlando, è improbabile che sia inglese.

Quindi, per noi programmatori, il "messaggio" dell'eccezione è il nome classe dell'eccezione e qualsiasi altra informazione pertinente all'eccezione dovrebbe essere copiata in variabili membro (finale / sola lettura) dell'oggetto eccezione. Preferibilmente, ogni singolo piccolo pezzo immaginabile. In questo modo, nessun messaggio deve (o dovrebbe) essere generato, e quindi nessun occhio indiscreto può vederlo.

Per rispondere alla preoccupazione espressa da Thomas Owens in un commento qui sotto:

Sì, certo, a un certo livello, creerà un messaggio di registro relativo all'eccezione. Ma vedi già il problema con quello che stai dicendo: da una parte, un messaggio di log di eccezioni senza una traccia di stack è inutile, ma d'altra parte, non vuoi permettere all'utente di vedere l'intera traccia dello stack di eccezioni. Di nuovo, il nostro problema qui è che la nostra prospettiva è distorta dalle pratiche tradizionali. I file di registro sono stati tradizionalmente in testo normale, il che poteva andar bene mentre la nostra disciplina era ancora agli inizi, ma forse non più: se esiste un problema di sicurezza, il file di registro deve essere binario e / o crittografato.

Indipendentemente dal testo binario o semplice, il file di log dovrebbe essere considerato come uno stream in cui l'applicazione serializza le informazioni di debug. Tale flusso sarebbe riservato agli occhi dei programmatori e il compito di generare informazioni di debug per un'eccezione dovrebbe essere semplice come serializzare l'eccezione nel flusso del log di debug. In questo modo, osservando il log si vede il nome della classe di eccezione (che, come ho già detto, è per tutti gli scopi pratici "il messaggio",) ciascuna delle variabili membro di eccezione che descrivono tutto ciò che è pertinente- e-pratico-da-includere-in-un-log e l'intera traccia dello stack. Nota come la formattazione di un messaggio di eccezione leggibile dall'uomo è vistosamente mancante da questo processo.

P.S.

Alcuni altri miei pensieri su questo argomento possono essere trovati in questa risposta: Come scrivere un messaggio di eccezione

P.P.S.

Sembra che molte persone siano state escluse dal mio suggerimento sui file di log binari, quindi ho modificato la risposta ancora una volta per rendere ancora più chiaro che ciò che sto suggerendo qui è not che il file di log dovrebbe essere binario, ma che il file di log potrebbe essere binario, se necessario.

    
risposta data 13.04.2015 - 18:13
fonte
45

Why is it that many common exceptions from system components do not contain useful details?

Nella mia esperienza, ci sono una serie di motivi per cui le eccezioni non contengono informazioni utili. Mi aspetto che questo tipo di motivi si applichi anche ai componenti del sistema, ma non lo so per certo.

  • Le persone focalizzate sulla sicurezza vedono le eccezioni come una fonte di fuga di informazioni (per esempio ). Poiché il comportamento predefinito delle eccezioni è quello di visualizzare tali informazioni all'utente, i programmatori a volte commettono errori di prudenza.
  • In C ++, ho sentito argomenti contro l'allocazione della memoria nei blocchi catch (dove almeno alcuni dei contesti per fare bugie sui messaggi). Quella allocazione è difficile da gestire e, peggio, può causare un'eccezione di memoria insufficiente - spesso bloccando la tua app o perdendo memoria. È difficile formattare le eccezioni senza allocare memoria, e questa pratica potrebbe essere migrata tra le lingue come fanno i programmatori.
  • Non lo sanno. Voglio dire, ci sono sono scenari in cui il codice ha l'idea no che cosa è andato storto. Se il codice non lo sa, non te lo può dire.
  • Ho lavorato in luoghi in cui i problemi di localizzazione impedivano di inserire nel sistema solo stringhe in inglese, anche per eccezioni che sarebbero state lette solo dal personale di supporto in lingua inglese.
  • Alcuni luoghi, ho visto eccezioni usate più come affermazioni. Sono lì per fornire un messaggio chiaro e strong durante lo sviluppo che qualcosa non viene fatto, o un cambiamento è stato fatto in un posto, ma non un altro. Questi sono spesso abbastanza unici che un buon messaggio verrebbe duplicato o semplicemente confuso.
  • Le persone sono pigre, i programmatori più della maggior parte. Trascorriamo molto meno tempo sul percorso eccezionale rispetto al percorso felice, e questo è un effetto collaterale.

Are my expectations out of line? Am I using Exceptions wrong that I even notice this?

Un po '? Voglio dire, le eccezioni dovrebbero avere dei bei messaggi, ma sono anche eccezioni . Dovresti passare il tempo a progettare il codice per evitare condizioni eccezionali, o scrivere codice per gestire condizioni eccezionali (ignorando il messaggio), non usandoli come una sorta di meccanismo di feedback interattivo durante la codifica. È inevitabile utilizzarli per scopi di debug, ma nella maggior parte delle situazioni che dovrebbero essere ridotti al minimo. Che ti accorga di questo problema mi preoccupa che non stia facendo un buon lavoro nel prevenirli.

    
risposta data 13.04.2015 - 17:54
fonte
12

Non ho un eccesso di esperienza in C #, o C ++ in particolare, ma posso dirti questo: eccezioni scritte dallo sviluppatore 9 volte su 10 sono più utili di qualsiasi eccezione generica che tu possa trovare, punto.

Idealmente sì, un'eccezione generica indicherà esattamente il motivo per cui si è verificato l'errore e sarai in grado di risolverlo facilmente - ma realisticamente, in applicazioni di grandi dimensioni con più classi che possono generare un'ampia varietà di diversi tipi di eccezioni , o lo stesso tipo di eccezioni stesso , è sempre più prezioso scrivere il tuo output per il ritorno degli errori piuttosto che fare affidamento sul messaggio predefinito.

È così che dovrebbe essere, perché come molte persone hanno sottolineato, alcune applicazioni non vogliono per lanciare un messaggio di errore che non vogliono che l'utente veda, per ragioni di sicurezza o per evitare di confonderli.

Invece, dovresti anticipare nel tuo progetto quali tipi di errori potrebbero essere lanciati nella tua applicazione (e ci saranno sempre errori) e scrivere messaggi che catturano gli errori che ti aiutano a identificare il problema.

Questo non ti aiuterà sempre, perché non puoi sempre anticipare quale messaggio di errore sarà utile, ma è il primo passo per capire meglio la tua applicazione a lungo termine.

    
risposta data 13.04.2015 - 19:13
fonte
7

La domanda si sta chiedendo esplicitamente perché così tante eccezioni generate da "componenti di sistema" (ovvero classi di librerie standard) non contengono dettagli utili.

Sfortunatamente, la maggior parte degli sviluppatori non scrive i componenti principali nelle librerie standard, né i documenti di progettazione dettagliati o altre motivazioni progettuali sono necessariamente rese pubbliche. In altre parole, potremmo non saperlo con certezza.

Tuttavia, ci sono due punti chiave da tenere a mente sul motivo per cui le informazioni dettagliate sulle eccezioni potrebbero non essere desiderabili o importanti:

  1. È possibile utilizzare un'eccezione chiamando il codice in qualsiasi modo: una libreria standard non può posizionare vincoli su come viene utilizzata l'eccezione. In particolare, può essere visualizzato all'utente. Considera un indice di array fuori limite: ciò potrebbe fornire informazioni utili a un utente malintenzionato. Il progettista del linguaggio non ha idea di come un'applicazione userà l'eccezione generata o anche quale tipo di applicazione è (ad esempio, app web o desktop), quindi lasciare le informazioni potrebbe essere più sicuro dal punto di vista della sicurezza.

  2. Le eccezioni non dovrebbero essere visualizzate all'utente. Invece, visualizza un messaggio di errore amichevole e registra l'eccezione in una posizione che un utente malintenzionato non ha accesso (se applicabile). Una volta identificato l'errore, uno sviluppatore dovrebbe eseguire il debug attraverso il codice, controllando i frame dello stack e i percorsi logici. A questo punto, lo sviluppatore ha più informazioni di quante un'eccezione potrebbe mai sperare di avere.

risposta data 13.04.2015 - 17:41
fonte
4

Prima di tutto, lasciami scoppiare una bolla dicendo che anche se il messaggio diag è caricato con informazioni che ti portano esattamente alla riga di codice e al sottocomando in 4 secondi, è probabile che gli utenti non lo scrivano mai o lo trasmettano a la gente di supporto e ti verrà detto "Beh, ha detto qualcosa su una violazione ... non so che sembrava complicato!"

Ho scritto software e ho supportato i risultati di altri software per più di 30 anni e, personalmente, l'attuale qualità di un messaggio di eccezione non ha praticamente nulla a che fare con la sicurezza, indipendentemente da come i risultati finali si sono verificati per adattarsi il loro modello dell'universo, molto più a che fare con il fatto che così tanti nel nostro settore sono stati originariamente autodidatti, e non hanno mai incluso lezioni di comunicazione. Forse se avessimo FORZATO tutti i nuovi programmatori in una posizione di manutenzione per un paio di anni in cui dovevano risolvere il problema, avrebbero compreso l'importanza di almeno una qualche forma di precisione.

Recentemente in una ricostruzione di un'applicazione, è stata presa una decisione in base alla quale i codici di ritorno rientrano in uno dei tre gruppi:

  • Da 0 a 9 sarebbe un successo attraverso il successo con ulteriori informazione.
  • da 10 a 99 non sarebbero letali (recuperabili) errori e
  • Da 101 a 255 potrebbero essere errori fatali.

(100 erano per qualche motivo esclusi)

In ogni particolare flusso di lavoro, il nostro pensiero è stato riutilizzato o il codice generico utilizza i ritorni generici (> 199) per errori fatali, lasciandoci con 100 errori fatali possibili per un flusso di lavoro. Con una leggera differenziazione dei dati nel messaggio, errori come quelli non trovati potrebbero tutti utilizzare lo stesso codice e differenziarli con i messaggi.

Quando il codice tornava dagli appaltatori, non crederesti alla nostra sorpresa quando praticamente OGNI SINGOLO ERRORE FATALE era il codice di ritorno 101.

Tutto ciò che ho considerato penso che la risposta alla tua domanda sia che i messaggi sono così privi di significato perché, una volta creati originariamente, dovevano essere segnaposto a cui nessuno era tornato. Alla fine la gente ha capito come risolvere i problemi non a causa dei messaggi ma DISPITE loro.

Da quel momento, la gente autodidatta non ha mai avuto un buon esempio di cosa dovrebbe contenere un'eccezione. Aggiungete che più utenti non leggono i messaggi, tanto meno provate a passarlo per supportare (ho visto messaggi di errore che erano tagliati e incolla dall'usato che venivano poi redatti prima di essere inviati con il commento successivo che sembravano molte informazioni e non avrei potuto desiderare tutto, quindi ne hanno rimosso a caso un bel po '.

E ammettiamolo, con troppi (non tutti ma troppi) della prossima generazione di programmatori, se è più lavoro e non aggiunge flash, non ne vale la pena ...

Ultima nota: se un messaggio di errore include un errore / codice di ritorno, mi sembra che da qualche parte nei moduli eseguiti ci dovrebbe essere una riga che legge qualcosa come "se la condizione restituisce il valore del codice" e la condizione dovrebbe dirti perché il codice di ritorno si è verificato. Questo mi sembra un approccio logico semplice, ma per la vita di me, basta provare a convincere Microsoft a dirti cosa è successo quando un aggiornamento di Windows non è riuscito su CODE 80241013 o qualche altro identificatore univoco. Un po 'triste, non è vero?

    
risposta data 14.04.2015 - 08:52
fonte
3

Mentre sono d'accordo sul fatto che le eccezioni dovrebbero contenere quante più informazioni possibili, o almeno essere meno generiche. Nella tabella non trovato, il nome della tabella sarebbe carino.

Ma tu sai di più su ciò che stavi cercando di fare nel posto nel codice in cui hai ricevuto l'eccezione. Anche se spesso non puoi fare molto per correggere la situazione quando qualcosa va storto in una libreria fuori dal tuo controllo, puoi aggiungere molte più informazioni utili su quale situazione è andata storta.

Nel caso di una tabella non trovata, non ti sarà di grande aiuto se ti viene detto che la tabella che non può essere trovata si chiama STUDENTI, perché non hai una tabella simile, e quella stringa non si trova da nessuna parte nel tuo codice.

Ma se rilevi l'eccezione e la rilancia con l'istruzione SQL che stavi cercando di eseguire, starai meglio, poiché si scopre che hai provato ad inserire un record con il campo del nome che è Robert '); DROP TABLE STUDENTS; (C'è sempre un xkcd!)

Quindi per combattere le eccezioni meno che informative: try-catch-rethrow con più informazioni specifiche per ciò che stavi cercando di fare.

Probabilmente dovrei aggiungere, perché questa sia una risposta più precisa al perché nella domanda, che un probabile motivo per cui l'attenzione dei produttori delle biblioteche non è stata rivolta a rendere i messaggi di eccezione migliori, è che essi Non so perché qualcosa è stato provato e fallito, quella logica è nel codice chiamante.

    
risposta data 14.04.2015 - 10:52
fonte
3

Per dare una risposta leggermente diversa: il codice incriminato è stato probabilmente fatto per le specifiche:

  • La funzione riceve X e restituisce Y
  • Se X non è valido, genera l'eccezione Z

Aggiungi la pressione per fornire esattamente le specifiche (per paura di essere rifiutati in revisione / test) nel minor tempo possibile e con il minimo sforzo, allora hai la tua ricetta per un'eccezione di libreria completamente compatibile e inutile.

    
risposta data 14.04.2015 - 23:12
fonte
3

Le eccezioni hanno un costo specifico per linguaggio e implementazione.

Ad esempio, le eccezioni C ++ sono necessarie per distruggere tutti i dati viventi tra il lancio del frame di chiamata e la cattura del frame di chiamata, e questo è costoso. Quindi, i programmatori non vogliono usare molto le eccezioni.

In Ocaml, il lancio delle eccezioni è quasi veloce come un C setjmp (il suo costo non dipende dal numero di frame di chiamata attraversati), quindi gli sviluppatori possono usarlo molto (anche per non eccezionale, molto comune casi). Al contrario, le eccezioni C ++ sono abbastanza pesanti, quindi probabilmente non le userete molto come fareste con Ocaml.

Un tipico esempio è una ricerca ricorsiva o un'esplorazione che può essere "fermata" all'interno di una ricorsione piuttosto profonda (ad esempio, trovare una foglia in un albero o una funzione di unificazione). In alcuni linguaggi è più veloce (quindi più idiomatico) propagare quella condizione ad ogni chiamante. In altre lingue, lanciare un'eccezione è più veloce.

Quindi, a seconda della lingua (e delle abitudini degli sviluppatori che la usano), un'eccezione potrebbe portare molti dettagli utili, o al contrario essere usata come un rapido salto non locale e portare solo i dati molto utili.

    
risposta data 14.04.2015 - 09:03
fonte
-2

Che cosa ti fa pensare che il valore dell'indice o l'intervallo richiesto o il nome della tabella sia un dettaglio utile, per un'eccezione?

Le eccezioni non sono un meccanismo di gestione degli errori; sono un meccanismo recupero .

Il punto di eccezione è quello di raggiungere il livello di codice che può gestire l'eccezione. Ovunque sia quel livello, o hai le informazioni necessarie, o non è rilevante. Se ci sono informazioni di cui hai bisogno, ma non hai accesso immediato , non gestirai l'eccezione al livello appropriato.

L'unica volta in cui riesco a pensare a dove le informazioni extra potrebbero essere utili è al livello più alto assoluto della tua applicazione in cui ti blocchi ed emetti un dump di errore; ma non è il lavoro dell'eccezione accedere, compilare e archiviare queste informazioni.

Non sto dicendo che puoi semplicemente "lanciare una nuova eccezione" ovunque e chiamarla buona, è possibile scrivere eccezioni. Ma includere informazioni irrilevanti non è necessario per usarle correttamente.

    
risposta data 15.04.2015 - 13:13
fonte

Leggi altre domande sui tag