Come si costruiscono i messaggi di debug nei propri programmi / script?

4

Domanda generale di alto livello per le persone che lavorano regolarmente con il codice.

Stavo riproponendo un vecchio codice JavaScript obsoleto in un'estensione di Chrome e, per aiutarmi a capirlo, ho scritto un breve codice per aiutare (vedi sotto). Inserisco regolarmente messaggi nel mio codice come dbmsg('Begin function loadDefaults()'); o dbmsg('Settings saved to storage'); e il messaggio viene inviato alla console. Ogni volta che ho bisogno di eseguire il debug di qualcosa in cui i messaggi della console standard non aiutano, accendo il debug. Immagino che questo metodo sia piuttosto semplice e possa essere portato su altre lingue.

In quali altri modi crei il debugging nei tuoi programmi? Sono autodidatta, quindi non conosco le pratiche standard, ma sono più interessato ai modi unici che hai trovato per fare questo genere di cose.

/** Debugging Tools
 *
 * Set debugMode to 1 to show error messages, 0 to hide messages.
 */
var debugMode = 1;
function dbmsg(message) {
    if ( debugMode == 1 ) {
        var date = new Date();
        var time = date.getHours() + ':' 
            + date.getMinutes() + ':' 
            + date.getSeconds();
        console.log('DBM - ' + time + ': ' + message);
        if ( chrome.runtime.lastError ) {
            console.log('DBM lastError - ' + chrome.runtime.lastError);
        }
    } else if ( debugMode == 0 ) {
        return;
    }
}
if ( debugMode == 1 ) {
    console.log('Debug mode is currently ON. Each debug message is preceeded with "DBM" and the time the message was generated.');

    // Listen for changes in storage
    chrome.storage.onChanged.addListener(function(changes, namespace) {
        for (key in changes) {
            var storageChange = changes[key];
            dbmsg('Storage key "%s" in namespace "%s" changed. ' +
                  'Old value was "%s", new value is "%s".',
                  key,
                  namespace,
                  storageChange.oldValue,
                  storageChange.newValue);
        }
    });
} 
    
posta bsima 06.12.2013 - 19:56
fonte

2 risposte

2

Il debug tramite la creazione di tracce di esecuzione è una tecnica molto sottovalutata. È uno strumento prezioso per il kit di strumenti dei programmatori. Come ogni strumento, ha la sua area di applicazione; altre volte un debugger di break point è più utile o genera una traccia di stack.

Il debugging by trace è più utile

  • quando nessun altro debugger è disponibile, per vari motivi.
  • quando si interrompe il programma si confonderebbe con i timer.
  • durante il debug di un programma multithreading in cui è necessario vedere l'interleaving dei thread.
  • quando si sviluppa un (sottile) bug su una lunga sequenza di esecuzione.

La differenza è che un debugger del punto di interruzione fornisce un'istantanea dello stato del programma e la traccia mostra ciò che accade nel tempo. Bug che sono difficili da risolvere con uno strumento possono essere più facili da decifrare con un altro strumento.

Il modo di costruire strumenti di traccia varia con la lingua di destinazione. Ciò che è costante sono le doti da fornire. Ecco il mio elenco di funzioni di base

  • In grado di essere attivato e disattivato impostando una bandiera. Il valore del flag può essere hardcoded o impostato in fase di esecuzione tramite il debug del codice.
  • Per i linguaggi compilati l'effetto del codice di debug dovrebbe essere completamente rimosso. Se disattivato, il codice di debug dovrebbe avere l'effetto no sul programma. Anche il debug del codice può avere bug e dà sempre un risultato in termini di prestazioni.
  • Supporta un modo di registrazione che sia stata inserita una funzione o un metodo. Il log dovrebbe contenere il nome del file / nome file, il nome metodo / funzione e ciascun valore argomento.
  • Supporta un metodo di registrazione del metodo o l'uscita della funzione, incluso il valore di ritorno.
  • Supporto per la registrazione di un messaggio arbitrario.
  • Supporta la registrazione su file o console.

Altre funzioni avanzate includono

  • Rientro di ciascun messaggio di registro in base all'intensità della chiamata.
  • Prefisso di ogni messaggio di registro con una data e un timestamp.
  • Supporta più file di registro in cui vengono conservati N.
  • Supporto per una dimensione massima del file di registro, eseguendo il wrapping all'inizio o passando a un nuovo file.
  • Supporto per i livelli di registrazione, da nessuno a estremamente dettagliato.
  • Possibilità di selezionare la registrazione per file sorgente / nome classe o nome funzione.

In C o C ++, tutto questo può essere ottenuto con la compilazione condizionale di macro nei file include. Impostando un flag del compilatore, è possibile disattivare completamente le macro senza toccare il codice sorgente, una capacità importante da avere. Il codice può essere molto più compatto in C ++ perché possiamo sfruttare il polimorfismo.

In Java, possiamo implementare il codice di debug in una coppia di file jar, uno con tutti i metodi di debug implementati e l'altro con metodi vuoti. Questo sfrutta le capacità di ottimizzazione della JVM. Ovviamente dovrebbe essere preso in considerazione Log4J .

Nei linguaggi di scripting è possibile combinare le tecniche sopra riportate per ottenere lo stesso effetto. Per html / javascript puoi usare il tag script per includere la sorgente di debugging javascript richiesta. Se la pagina viene generata da PHP, puoi includere in modo condizionale il codice di debug nella pagina.

Le funzionalità di base possono essere costruite in circa 50 righe di codice (come ricordo, dal momento che non sono come la mia macchina da scrivania), e le funzionalità avanzate possono essere altre 100 circa.

Le funzioni di traccia delle funzioni descritte sopra possono essere implementate in Javascript (usando la tua funzione dbmsg ) come (non testato):

function dbcall( /* name, args */ ) {
  var msg = arguments[0] + '( ';
  for (var i = 1; i < arguments.length - 1; i++)
    msg += arguments[i] + ', ';
  msg += arguments[arguments.length - 1] + ' )';
  dbmsg( msg );
}

function dbretn( /* name, value */ ) {
  var msg = arguments[0] + ' returns ';
  msg += arguments[1];
  dbmsg( msg );
}

In una lingua che supporta i macro possiamo impostare una macro che chiama dbretn che fa il ritorno, ma in javascript dobbiamo aggiungere la chiamata davanti a ogni return come in

...
dbretn( 'MyFunction', value );
return value;

Il listener storage.changed che mostri nella domanda è un buon esempio di come tali strutture di debug dovrebbero essere adattate a ciascun ambiente.

    
risposta data 07.12.2013 - 06:45
fonte
1

Ho un pregiudizio casuale contro l'attivazione o la disabilitazione dello "stato di debug" tramite una variabile o costante di runtime. Nella migliore delle ipotesi, potresti avere un'impostazione globale del "log level" che dice come devono essere SEVERE i messaggi per registrarsi, ma anche quello è un dettaglio di implementazione.

La mia effettiva preferenza è che ogni punto di ingresso o il metodo principale ne riferisca l'esistenza a qualche parte dell'applicazione specializzata per la registrazione, e lo faccia di nuovo su errori, avvertenze o risultati significativi. Che cos'è questa parte e come funziona varia in base allo stack specifico che stai utilizzando.

Quando scrivevo gli script T-SQL, ho fatto un uso liberale del comando PRINT .

CREATE PROCEDURE doStuff(@arg1 int, @arg2 char)
  PRINT 'doStuff(' + @arg1 + ', ' + @arg2 + ')'
  ...
ENDPROC

Per JavaScript, ho preso l'abitudine di un semplice wrapper come hai fatto tu, ma ho appena testato la presenza o l'assenza di console e ho lasciato che il browser decidesse se doveva mantenere il log o no.

function log() {
  if(console && console.log) {
    var msg = "";
    for(var a in arguments) {
      if(msg != "") {
        msg += " "
      }
      msg += a;
    }
    console.log(msg)
  }
}

function doStuff(arg1, arg2) {
  log('doStuff(', arg1, ', ', arg2, ')');
}

Con il lavoro che sto facendo ora, una delle aggiunte che ho apportato alla nostra prossima major release è stata una nuova classe di reporting, per tutto questo genere di cose. Ma questo è un progetto lato client in un linguaggio largamente morto e non sarebbe del tutto utile.

    
risposta data 06.12.2013 - 20:36
fonte

Leggi altre domande sui tag