Dove dovrebbero essere generati e stampati gli errori dell'interfaccia utente? Registra errori?

3

Funzione A() chiama B() chiama C() , che rileva un errore. Quale funzione scrive nel registro e quale funzione segnala all'utente?

La mia prima inclinazione è che tutte le funzioni debbano scrivere nel registro e che la funzione interna dell'interfaccia utente debba essere responsabile della trasmissione dell'errore all'utente. Tuttavia, la correzione deve essere eseguita in C() , quindi forse invece di avere un file di log super lungo che richiederà un po 'di tempo per analizzare, dovrei semplicemente avere la funzione che ha incontrato il log degli errori l'errore e i parametri utilizzati per chiamarlo.

Allo stesso modo, non sono troppo convinto che la funzione più interna dell'interfaccia utente debba generare l'errore (considerare le app CLI, al contrario delle app Web o GUI). Quella era più una decisione istintiva, quindi qualsiasi comprensione della engineering soluzione sarebbe stata apprezzata.

    
posta dotancohen 23.07.2013 - 11:24
fonte

2 risposte

3

Poiché esistono due segmenti di pubblico distinti per il report degli errori, con esigenze di informazioni completamente diverse, è logico che l'errore venga segnalato a più livelli.

Da un lato, ci sono gli sviluppatori che devono sapere esattamente dove è stato rilevato l'errore e tutti i dettagli nitidi dell'errore stesso. Poiché gli sviluppatori sono anche quelli che analizzano i file di log, ha più senso produrre un log per un errore non appena viene rilevato, dove la maggior parte delle informazioni rilevanti per gli sviluppatori è ancora a portata di mano e può essere incorporata nel log.

D'altra parte, gli utenti finali dell'applicazione non si preoccupano veramente di cosa è andato storto. Vogliono solo sapere se la funzione che hanno richiesto è riuscita e in caso contrario, cosa possono fare loro per farlo funzionare la volta successiva. Questa informazione ha un livello di astrazione più elevato rispetto al solito codice di rilevamento degli errori e in genere non è nemmeno nota al livello in cui viene rilevato per la prima volta l'errore.
Ad esempio, se si verifica un errore durante il salvataggio di un file, il codice responsabile della scrittura dei dati sul disco non può sapere se l'azione di salvataggio è stata attivata automaticamente o tramite richiesta esplicita dall'utente, ma si tratta di informazioni importanti al momento di decidere come informare l'utente finale del problema.

In sintesi, i report degli errori dovrebbero essere generati al livello in cui le informazioni sono al livello di astrazione giusto per fornire un rapporto significativo e utile al pubblico previsto del report.

Inoltre, i testi leggibili dall'uomo destinati agli utenti finali dovrebbero essere generati nei componenti dell'interfaccia utente dell'applicazione per garantire che solo l'interfaccia utente sia interessata se è necessario supportare lingue aggiuntive per gli utenti finali.

    
risposta data 23.07.2013 - 12:11
fonte
1

Accesso

Vorrei iniziare con la registrazione. Beh, sono sicuro che lo sapete, ci sono vari livelli di registrazione che la maggior parte delle librerie supportano: Debug, Info, Warning ed Error.

Il mio approccio è che l'output di Debug dovrebbe mostrare tutte le informazioni che io, il programmatore, vorremmo vedere. L'utente che usa il tuo programma probabilmente non vorrà vedere che hai inserito un metodo specifico nella GUI, ma lo farei in caso di crash. Se non hai modo di recuperare la traccia dello stack del crash, la prossima cosa migliore è stampare sul log la voce e l'uscita di ogni metodo nel tuo programma con il debug a livello di log.

Nel tuo esempio, questo significa un log con il seguente output:

DEBUG <timestamp>:  ClassA::A() : Entering A
DEBUG <timestamp>:  ClassB::B() : Entering B
DEBUG <timestamp>:  ClassC::C() : Entering C
DEBUG <timestamp>:  ClassC::C() : Exiting C   // These don't show
DEBUG <timestamp>:  ClassB::B() : Exiting B   // in the case of 
DEBUG <timestamp>:  ClassA::A() : Exiting A   // an error in C.

Informazioni sul livello del registro sono informazioni che ritengo importanti per la funzionalità del programma. Supponiamo per gli scopi di questo esempio che l'obiettivo di A sia eseguire un'operazione utilizzando un database selezionato, B è un mezzo per visualizzare le connessioni di database esistenti e creare nuove connessioni di database e C è l'informazione specifica relativa a un nuovo database e un modo per testare questa connessione.

I messaggi di registro del livello informazioni pertinenti per me sarebbero:

INFO <timestamp>:  ClassC::C() : Saved database connection "localhost".
INFO <timestamp>:  ClassA::A() : Performing transaction using database connection "localhost".
INFO <timestamp>:  ClassA::A() : Transaction complete using database connection "localhost".

Si noti che questi sono messaggi che probabilmente vorrai mostrare all'utente nel programma (anche se un po 'prolisso). Per me questa è la differenza tra Debug e Info: anche se l'utente potrebbe voler vedere queste informazioni. Ovviamente anche gli avvisi e gli errori di registrazione devono essere registrati e mostrati all'utente.

WARNING <timestamp>:  ClassC::C() : Connection test for "localhost" failed.

Quello che faccio di solito nei miei programmi è un parametro facoltativo che determina a quale livello di registrazione dovrei presentare i messaggi all'utente con un valore predefinito di Info. Se l'utente non desidera un'interfaccia dettagliata, può impostarlo su Avviso e visualizzare solo avvisi ed errori e, ad esempio, è possibile utilizzarlo per generare informazioni di debug. In tal caso, assicurati di fornire un indicatore alternativo che un determinato compito è terminato (il pulsante di esecuzione diventa disabilitato durante l'elaborazione e successivamente viene nuovamente abilitato, ad esempio).

Eccezioni

Detto questo, come approccio le eccezioni? Dipende dal contesto dell'errore. Se ClassC gestisce la creazione di nuove connessioni al database e testando la connessione, genera un'eccezione, vorrei gestire quell'eccezione sql all'interno di ClassC poiché mi aspetto potenziali problemi. Si noti che gestirò solo un'eccezione sql. Se il problema fosse un altro che non mi aspettavo, scivolerebbe nella classe C e di conseguenza passerei a ClassB. ClassB può decidere di rilevare le eccezioni relative ai driver mancanti, poiché non è del tutto inatteso e non vorrei che tali problemi venissero rilevati a livelli inferiori.

Tuttavia, supponendo che non lo sia, colpiamo ClassA, il cui ruolo nel chiamare ClassB era consentire all'utente la possibilità di creare nuove connessioni al database. Se un'eccezione colpisce ClassA, è successo qualcosa di grave. In tali casi, ciò che faccio di solito è di rilevare qualsiasi eccezione, scrivere l'errore e il suo stacktrace (se possibile) nel log, mostrare un messaggio di errore imprevisto all'utente, quindi rilanciare l'eccezione. Secondo la mia opinione personale, è irresponsabile cogliere tutte le eccezioni senza essere pronti a ripensarla, dato che è corretto che gli errori imprevisti siano ancora generati.

Il punto importante da notare qui è il livello di responsabilità. Le eccezioni che mi aspetto sono gestite localmente mentre eccezioni inaspettate nel contesto della mia classe sono autorizzate a risalire la catena. Nel peggiore dei casi, assicurarsi di provare a registrare l'errore e mostrare un messaggio di errore all'utente. Combina questo con la registrazione di precisione e nella maggior parte dei casi sarai sicuro di riuscire a tracciare i tuoi passi fino al luogo dell'errore.

    
risposta data 23.07.2013 - 12:05
fonte

Leggi altre domande sui tag