Domanda

COME chiese Jeff Atwood:"Qual è la tua filosofia di registrazione?Tutto il codice dovrebbe essere disseminato di .logthis() E .logthat() chiamate?Oppure in qualche modo inserisci la registrazione dopo il fatto?"

È stato utile?

Soluzione

La mia filosofia di registrazione può essere facilmente riassunta in quattro parti:

Controllo o registrazione della logica aziendale

Registra le cose che devono essere registrate.Ciò deriva dai requisiti dell'applicazione e può includere la registrazione di ogni modifica apportata a qualsiasi database (come in molte applicazioni finanziarie) o la registrazione degli accessi ai dati (come potrebbe essere richiesto nel settore sanitario per soddisfare le normative di settore)

Poiché questo fa parte dei requisiti del programma, molti non lo includono nelle loro discussioni generali sul logging, tuttavia vi è una sovrapposizione in queste aree e per alcune applicazioni è utile considerare insieme tutte le attività di logging.

Registrazione del programma

Messaggi che aiuteranno gli sviluppatori a testare ed eseguire il debug dell'applicazione e a seguire più facilmente il flusso di dati e la logica del programma per comprendere dove potrebbero verificarsi errori di implementazione, integrazione e altri.

In generale questa registrazione viene attivata e disattivata in base alle esigenze per le sessioni di debug.

Registrazione delle prestazioni

Aggiungi la registrazione successiva secondo necessità per trovare e risolvere i colli di bottiglia delle prestazioni e altri problemi del programma che non causano il fallimento del programma, ma ne migliorano il funzionamento.Si sovrappone alla registrazione del programma in caso di perdite di memoria e di alcuni errori non critici.

Registrazione di sicurezza

Registrazione delle azioni dell'utente e delle interazioni con sistemi esterni in cui la sicurezza è un problema.Utile per determinare in che modo un utente malintenzionato ha violato un sistema dopo un attacco, ma può anche collegarsi a un sistema di rilevamento delle intrusioni per rilevare attacchi nuovi o in corso.

Altri suggerimenti

Lavoro con sistemi in tempo reale critici per la sicurezza e la registrazione è spesso l'unico modo per individuare bug rari che si verificano solo ogni 53 martedì quando c'è la luna piena, se capisci cosa intendo.Questo ti rende ossessivo riguardo all'argomento, quindi adesso mi scuso se comincio ad avere la bava alla bocca.

Progetto sistemi in grado di registrare praticamente tutto, ma non attivo tutto per impostazione predefinita.Le informazioni di debug vengono inviate a una finestra di dialogo di debug nascosta che le contrassegna e le visualizza in una casella di riepilogo (limitata a circa 500 righe prima della cancellazione) e la finestra di dialogo mi consente di interromperle, salvarle automaticamente in un file di registro o deviarle a un debugger collegato come DBWin32.Questo diversivo mi consente di vedere l'output di debug di più applicazioni tutte ben serializzate, il che a volte può salvarmi la vita.I file di registro vengono automaticamente eliminati ogni N giorni.IO usato per utilizzare livelli di registrazione numerici (più alto è il livello, più acquisizioni):

  • spento
  • solo errori
  • di base
  • dettagliato
  • qualunque cosa

ma questo è troppo rigido: mentre ti avvicini a un bug è molto più efficiente essere in grado di concentrarti sull'accesso esattamente su ciò di cui hai bisogno senza dover guadare tonnellate di detriti e potrebbe trattarsi di un particolare tipo di transazione o operazione che causa l'errore.Se ciò richiede di accendere tutto, stai solo rendendo il tuo lavoro più difficile.Hai bisogno di qualcosa di più grana.

Quindi ora sto passando alla registrazione basata su un sistema di flag.Tutto ciò che viene registrato ha un flag che descrive in dettaglio il tipo di operazione e c'è una serie di caselle di controllo che mi consentono di definire cosa viene registrato.In genere l'elenco è simile a questo:

#define DEBUG_ERROR          1
#define DEBUG_BASIC          2
#define DEBUG_DETAIL         4
#define DEBUG_MSG_BASIC      8
#define DEBUG_MSG_POLL       16
#define DEBUG_MSG_STATUS     32
#define DEBUG_METRICS        64
#define DEBUG_EXCEPTION      128
#define DEBUG_STATE_CHANGE   256
#define DEBUG_DB_READ        512
#define DEBUG_DB_WRITE       1024
#define DEBUG_SQL_TEXT       2048
#define DEBUG_MSG_CONTENTS   4096

Questo sistema di registrazione viene fornito con la build di rilascio, attivato e salvato su file per impostazione predefinita.È troppo tardi per scoprire che avresti dovuto accedere DOPO che si è verificato il bug, se il bug si verifica in media solo una volta ogni sei mesi e non hai modo di riprodurlo.

Il software in genere viene fornito con ERROR, BASIC, STATE_CHANGE ed EXCEPTION attivati, ma questo può essere modificato sul campo tramite la finestra di dialogo di debug (o un'impostazione del registro/ini/cfg, dove queste cose vengono salvate).

Oh e una cosa: il mio sistema di debug genera un file al giorno.Le tue esigenze potrebbero essere diverse.Assicurati però che il codice di debug venga avviato ogni file con la data, la versione del codice in esecuzione e, se possibile, qualche indicatore per l'ID cliente, la posizione del sistema o altro.Puoi ottenere un miscuglio di file di registro provenienti dal campo e hai bisogno di qualche registrazione di cosa proviene da dove e quale versione del sistema stavano utilizzando che è effettivamente nei dati stessi, e non puoi fidarti del cliente /field engineer per dirti quale versione hanno - potrebbero semplicemente dirti quale versione PENSANO di avere.Peggio ancora, potrebbero segnalare la versione exe presente sul disco, ma la vecchia versione è ancora in esecuzione perché si sono dimenticati di riavviare dopo la sostituzione.Chiedi al tuo codice di dirtelo da solo.

Questo è il mio cervello scaricato...

Penso che aggiunga sempre, sempre, sempre la registrazione quando si verifica un'eccezione, incluso il messaggio e l'analisi completa dello stack.Oltre a ciò, penso che sia piuttosto soggettivo se usi o meno i log spesso o meno...

Spesso provo ad aggiungere la registrazione solo in punti critici dove ciò che sto registrando dovrebbe colpire molto raramente, altrimenti si verifica il problema, come ha menzionato, dei registri che diventano troppo grandi...questo è il motivo per cui la registrazione dei casi di errore è la cosa ideale da registrare sempre (ed è fantastico poter vedere quando questi casi di errore vengono effettivamente riscontrati in modo da poter esaminare ulteriormente il problema).

Altre cose utili da registrare sono se hai delle asserzioni e le tue asserzioni falliscono, quindi registrale...ad esempio, questa query dovrebbe contenere meno di 10 risultati, se è più grande potrebbe esserci un problema, quindi registrala.Naturalmente, se un'istruzione log finisce per riempire i log, probabilmente è un suggerimento per metterla a una sorta di livello di "debug" o per modificare o rimuovere l'istruzione log.Se i tronchi diventano troppo grandi, spesso finirai per ignorarli.

Adotto quello che considero un approccio tradizionale;alcune registrazioni, circondate da definizioni condizionali.Per le build di produzione, disattivo le definizioni.

Scelgo di registrare deliberatamente mentre procedo, poiché ciò significa che i dati di registro sono significativi:

  • A seconda del framework di registrazione è possibile aggiungere informazioni su livello/gravità/categoria in modo che i dati di registro possano essere filtrati
  • Puoi assicurarti che sia presente il giusto livello di informazioni, né troppe, né troppo poche
  • Quando scrivi il codice sai quali sono le cose più importanti e puoi quindi assicurarti che vengano registrate

L'utilizzo di una qualche forma di iniezione di codice, strumento di profilazione o di tracciamento per generare log molto probabilmente genererebbe log dettagliati e meno utili in cui sarebbe più difficile approfondire.Potrebbero tuttavia essere utili come aiuto per il debug.

Inizio affermando molte condizioni nel mio codice (in C#, utilizzando System.Diagnostics.Assert), ma aggiungo la registrazione solo dove trovo, durante il debug o sotto stress il sistema, che ho davvero bisogno di avere un modo per seguire ciò che sta accadendo all'interno del mio codice senza avere un debugger collegato in modo permanente.

Altrimenti, preferisco utilizzare la funzionalità di Visual Studio per inserire tracce nel codice come punti di interruzione speciali (ad es.inserisci un punto di interruzione e fai clic con il pulsante destro del mouse, quindi seleziona "Quando viene colpito..." e gli dici cosa visualizzare in quel caso).Non è necessario ricompilare ed è facile abilitare/disabilitare le tracce al volo.

Se stai scrivendo un programma che verrà utilizzato da molte persone, è meglio avere un qualche tipo di meccanismo per scegliere cosa verrà registrato e cosa no.Un argomento a favore delle funzioni .logthis() è che in alcuni casi possono essere un eccellente sostituto dei commenti incorporati (se eseguite correttamente).

Inoltre, ti aiuta a restringere ESATTAMENTE il punto in cui si sta verificando un errore.

Registrateli tutti e lasciate che Grep li sistemi.

Sono d'accordo con Adam, ma prenderei anche in considerazione la possibilità di registrare cose di interesse o cose che puoi dimostrare come risultati come una sorta di prova del loro verificarsi.

Definisco una varietà di livelli e passo in un'impostazione con config/invocation.

Se hai davvero bisogno di accedere al tuo sistema, i tuoi test sono scadenti o almeno incompleti e non molto approfonditi.Tutto nel tuo sistema dovrebbe essere il più possibile una scatola nera.Nota come le classi principali come String non necessitano di registrazione, il motivo principale è che sono molto ben testate e funzionano come dettagliato.Niente sorprese.

Utilizzo la registrazione come un modo per restringere i problemi che non si riproducono nei nostri test unitari e tanto meno ripetendo gli stessi passaggi forniti dall'utente:quei rari problemi che si manifestano solo su hardware molto remoti (e talvolta, anche se molto raramente, anche causati da un problema tecnico di driver o librerie di terze parti fuori dal nostro controllo).

Sono d'accordo con il commento secondo cui tutto questo dovrebbe essere rilevato dalla nostra procedura di test, ma è difficile trovare un milione di codebase LOC che richieda codice di livello molto basso e critico per le prestazioni per soddisfare tali requisiti.Non lavoro con software mission-critical, ma lavoro nel settore della grafica dove spesso dobbiamo fare di tutto, dall'implementazione di allocatori di memoria all'utilizzo del codice GPU su SIMD.

Anche con codice molto modulare, poco accoppiato o addirittura completamente disaccoppiato, le interazioni del sistema possono portare a input e output molto complessi con comportamenti che variano tra le piattaforme dove occasionalmente abbiamo quel caso limite che sfugge ai nostri test.Le scatole nere modulari possono essere molto semplici, ma le interazioni tra loro possono diventare molto complesse e portare a casi limite imprevisti occasionali.

Come esempio di un caso in cui la registrazione mi ha salvato il sedere, una volta ho avuto questo strano utente con un prototipo di macchina Intel che si stava bloccando.Abbiamo elencato i requisiti minimi per le macchine che dovrebbero supportare SSE 4, ma questa particolare macchina soddisfaceva tali requisiti minimi e continuava a non supportare le estensioni Streaming SIMD oltre SSE 3 nonostante fosse una macchina a 16 core.Scoprirlo rapidamente è stato possibile guardando il suo log che mostrava esattamente il numero di riga in cui venivano utilizzate le istruzioni SSE 4.Nessuno di noi nel nostro team è riuscito a riprodurre il problema e tanto meno un singolo altro utente che ha partecipato alla verifica della segnalazione.Idealmente avremmo dovuto scrivere codice per versioni SIMD precedenti o almeno eseguire alcune ramificazioni e controlli per assicurarci che l'hardware supportasse i requisiti minimi, ma volevamo fare un presupposto fermo comunicato attraverso i requisiti hardware minimi per semplicità ed economia.Qui, forse, è discutibile che siano stati i nostri requisiti minimi di sistema ad avere il "problema tecnico".

Dato il modo in cui utilizzo la registrazione qui, tendiamo a ottenere log abbastanza grandi.Tuttavia, l'obiettivo non è la leggibilità: ciò che in genere è importante è l'ultima riga di un registro inviato con un rapporto quando l'utente riscontra un arresto anomalo di qualche tipo che nessuno di noi nel team (per non parlare di pochi altri utenti al mondo) può riprodursi.

Tuttavia, un trucco che utilizzo regolarmente per evitare un eccessivo spam nei log è che spesso è ragionevole presumere che un pezzo di codice che viene eseguito una volta con successo lo farà anche successivamente (non una garanzia assoluta, ma spesso un presupposto ragionevole).Quindi spesso impiego a log_once tipo di funzione per funzioni granulari per evitare il sovraccarico di pagare il costo della registrazione ogni volta che viene chiamato.

Non cospargo gli output dei log ovunque (potrei farlo se avessi tempo).In genere li riservo maggiormente per le aree che sembrano le più pericolose:codice che invoca shader GLSL, ad es.(I fornitori di GPU variano notevolmente in termini di capacità e anche di come compilano il codice), codice che utilizza gli intrinseci SIMD, codice di livello molto basso, codice che inevitabilmente deve fare affidamento su comportamenti specifici del sistema operativo, codice di basso livello che fa ipotesi sulla rappresentazione dei POD (es:codice che presuppone 8 bit per byte) - il tipo di casi in cui allo stesso modo vorremmo cospargere molte asserzioni e controlli di integrità, oltre a scrivere il maggior numero di test unitari.In genere questo è sufficiente e la registrazione mi ha salvato il sedere molte volte laddove altrimenti avrei affrontato un problema non riproducibile e avrei dovuto affrontare il problema alla cieca, richiedendo molte iterazioni e tentativi di soluzione per l'unico utente al mondo che potrebbe riprodurre il problema.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top