Costruire un Logger Mixin in Sass

In questo tutorial creeremo un mixin "logger", che genera un log flessibile e informativo come CSS quando viene compilato Sass.

La registrazione è il processo di registrazione delle azioni dell'applicazione e dello stato su un'interfaccia secondaria. - Progetto del codice

L'idea

L'altro giorno, il manutentore Neat Reda Lemedan e io stavamo parlando di tutte le cose di Sass e all'improvviso ho visto un interessante mixin dei suoi:

@include -neat-warn ("Qualcosa non va"); 

Gli ho chiesto cosa fa questo mixin e lui mi ha detto che è fondamentalmente un involucro per il @avvisare direttiva di Sass che controlla se l'utente è disposto a stampare avvisi da Neat nella console (basato su una variabile globale).

Così ho pensato a me stesso perché fermarsi qui? e ha iniziato a giocare con l'idea della stessa notte. La mia idea era di costruire un wrapper per entrambi @avvisare e @errore (da Sass 3.4) per aiutare gli sviluppatori di librerie e framework a stampare diversi tipi di messaggi (informazioni, debug, warn, error ...) e tenere traccia di tutti i log.

La mia attuale implementazione fornisce:

  • 5 livelli di registrazione (DEBUG, INFORMAZIONI, AVVISARE, ERRORE e FATALE);
  • un livello minimo al quale il registratore inizia a stampare;
  • una cronologia di tutti i log, che può essere stampata come CSS;
  • un'API amichevole con funzioni facili da usare;
  • un aiuto per saperne di più sui diversi livelli di registrazione.

Come funziona?

Si è rivelato abbastanza semplice. Abbiamo bisogno di una variabile globale che mantenga l'intera configurazione e un mixin che funge da wrapper per le nostre direttive di stampa della console.

Perché vogliamo che la nostra configurazione globale sia personalizzabile (in una certa misura), avvolgiamo la sua dichiarazione in un mixin. Non solo è più conveniente, ma è anche più bello per l'utente finale.

Quindi abbiamo un mixin, chiamiamolo logger che è solo inteso per essere chiamato una volta, creando una mappa globale che mantiene la nostra configurazione. Allora abbiamo il nostro involucro, ceppo, che accetta un livello di registrazione (per esempio AVVISARE o ERRORE) e il messaggio da registrare come argomenti. Questo è praticamente tutto.

Per rendere le cose più convenienti agli sviluppatori, forniremo alcune funzioni abbreviate per registrare diversi livelli. Ad esempio, invece di dover digitare:

@include log ("ERROR", "Non c'è abbastanza unicorno qui."); 

… potremmo avere:

@include ERROR ("Non c'è abbastanza unicorno qui."); 

Quindi finirai con un'API simile a questa:

// Crea un'istanza di un logger // che inizia a stampare i log a livello "INFO". // Ciò significa che i registri "DEBUG" non verranno visualizzati. @include logger ("INFO"); // Registra le cose. @include ERROR ("Non c'è abbastanza unicorno qui."); 

Aggiungeremo anche alcuni mix per aggiungere alcune interessanti funzionalità extra:

  • una stampa di informazioni su ciascun livello di registrazione come promemoria;
  • uno stampa tutti i registri che sono stati registrati nella compilation corrente.

Costruire l'API

Costruttore di logger

Iniziamo con l'inizio, dobbiamo? Il logger costruttore. Questo dovrebbe accettare un singolo parametro: il livello al quale il logger dovrebbe iniziare a stampare i log nella console.

Questo è un modello abbastanza comune per i sistemi di registrazione. Per esempio:

  • Se si desidera solo stampare errori (ERRORE e FATALE), tu scrivi @include logger ("ERRORE").
  • Se vuoi stampare tutto, verrai con @include logger ("ALL"), che è fondamentalmente uguale al livello di registrazione più basso (DEBUG).
  • Se si desidera disabilitare del tutto il logger, si esegue @include logger ("OFF").

Nota: puoi trovare ulteriori informazioni sui livelli di registrazione in questo thread StackOverflow o nella documentazione dei log di Apache.

@mixin logger ($ minimum-level) // Elenco dei livelli disponibili $ livelli: "DEBUG", "INFO", "WARN", "ERROR", "FATAL"; // Assicurati che la stringa data sia maiuscola $ livello minimo: a maiuscole ($ livello minimo); // Se level è 'ALL', vai con il livello più basso di tutti @if $ minimum-level == "ALL" $ livello minimo: nth ($ livelli, 1);  // Se il livello non è valido, arbitrario vai con 'INFO' @if non indice ($ livelli "OFF", $ livello minimo) $ livello minimo: "INFO";  // Crea variabile globale $ logger-configuration: (// Elenco dei livelli disponibili "levels": $ levels, // Elenco dei livelli che sono stampati con "@error" "errors": "FATAL" "ERROR", / / Livello minimo (come indice di '$ levels') per stampare "min": index ($ levels, $ minimum-level), // Indica se il logger è abilitato "enabled": $ minimum-level! = " OFF ", // Una mappa per tenere traccia di tutti i log" history ": (" DEBUG ": ()," INFO ": ()," WARN ": ()," ERROR ": ()," FATAL ": ()))! global;  

Il codice sopra dovrebbe essere per lo più autoesplicativo, ma ho aggiunto alcuni commenti per rendere tutto chiaro. Come puoi vedere, questo mixin non fa molto se non creare una variabile globale. Non così male, vero??

Prima di proseguire, creiamo una piccola funzione di supporto che ci consente di ottenere facilmente un valore da questa mappa globale. Perché sai, digitando map-get ($ logger-configuration, ...) non è divertente da remoto Che dire logger-conf (...) anziché?

@function logger-conf ($ key) @return map-get ($ logger-configuration, $ key);  

Log Wrapper

Ok, passiamo all'attuale ceppo funzione che stampa le cose nella console. Non solo dovrebbe emettere i messaggi dati nella console dell'utente, ma dovrebbe anche aggiornare la cronologia per tenere traccia di ciò che viene registrato (che potrebbe o potrebbe non essere utile).

Sembra difficile. Beh, non preoccuparti, sarà liscio come il burro. Sappiamo già che questo mixin dovrebbe accettare solo due parametri: il livello di registrazione e il messaggio.

@mixin log ($ level, $ message) // Assicurarsi che il livello sia in maiuscolo $ level: to-upper-case ($ level); // A meno che non sia disabilitato, procedi con @if logger-conf ("enabled") // Ottieni l'indice del livello attuale // Ad esempio, 'DEBUG' sarebbe '1' $ index-current-level: index (logger-conf ( "livelli"), $ livello); // Se '$ level' non è valido, // arbitrary torna su 'INFO' @if non $ index-current-level $ level: "INFO";  // Aggiorna cronologia logger @ include logger-update-history ($ level, $ message); // Infine, stampa il messaggio nella console // se il livello attuale è maggiore o uguale al livello minimo. @if $ index-current-level> = logger-conf ("min") $ print: '[' + $ level + '] ::' + $ message; // Stampa come '@error' se è un livello di errore @if index (logger-conf ("errors"), $ level) @error $ print;  // Altrimenti usa '@warn' @else @warn $ print;  

Ora, dobbiamo occuparci dell'aggiornamento della cronologia. Questo è in realtà un po 'più duro, ma una volta che ti sei abituato a mappare la manipolazione, diventa più chiaro.

Prima di guardare il codice, lascia che ti spieghi come funziona la storia. Fondamentalmente, è una mappa in cui le chiavi sono i livelli di registrazione e i valori sono elenchi di messaggi registrati. Ad esempio, potresti avere qualcosa come:

$ _: ("DEBUG": (), "INFO": (), "WARN": ("Dovresti prestare attenzione a questo.", "Questo potrebbe essere migliorato."), "ERRORE": ("Qualcosa è rotto . ")," FATAL ": ()) 

Va bene. Andiamo.

@mixin logger-update-history ($ level, $ message) // Ottieni la mappa della cronologia dalla configurazione $ history: logger-conf ("history"); // Ottieni un elenco cronologico per il livello attuale $ cronologia del livello attuale: map-get ($ history, $ level); // Aggiungi il nuovo log all'elenco $ current-level-history: append ($ current-level-history, $ message); // Crea una variabile temporanea contenente la nuova mappa della cronologia $ logger-history: map-merge ($ history, ($ level: $ current-level-history)); // Aggiornare la mappa della cronologia dalla configurazione con la nostra variabile temporanea $ logger-configuration: map-merge ($ logger-configuration, ("history": $ logger-history))! Global;  

Coinvolge alcune linee ostili, ma quando spieghi ogni riga individualmente, tutto ha senso alla fine.

Abbiamo finito qui, ma abbiamo parlato dell'aggiunta di funzioni stenografiche. Facciamolo ora prima che dimentichiamo:

@mixin FATAL ($ message) @include log ("FATAL", $ message);  @mixin ERROR ($ message) @include log ("ERROR", $ message);  @mixin WARN ($ message) @ include log ("WARN", $ message);  @mixin INFO ($ message) @include log ("INFO", $ message);  @mixin DEBUG ($ message) @ include log ("DEBUG", $ message);  

Questo è tutto. Un'ultima cosa che potremmo fare, ma non è veramente obbligatoria, sta testando se logger è stato incluso prima di provare a usare la mappa globale. Non solo evitiamo errori stupidi, ma potremmo anche rendere facoltativa l'istanza del logger eseguendola al volo.

@mixin log ($ level, $ message) // Verifica se esiste la variabile globale 'logger-configuration'. // Se non lo fa, significa che il 'logger' non è stato incluso, // quindi lo includiamo, impostando arbitrariamente il livello minimo su 'INFO'. @if non esiste una variabile globale ("logger-configuration") @include logger ("INFO");  @if logger-conf ("enabled") // ... 

Aggiunta di extra

Inizieremo con il primo (e meno utile) di entrambi i mixaggi extra, l'helper. È davvero un gadget a questo punto poiché tutto ciò che fa è stampare una regola CSS con i livelli di registrazione come selettori e le spiegazioni come valori.

Questo è inteso per dare un aiuto agli sviluppatori quando non sanno veramente quale livello di registrazione dovrebbero usare. Potrebbe essere stato scritto come commento, ma ho voluto provare questo aiuto stampante thingie.

@mixin logger-help // Aprire un nuovo logger di selezione 'logger-help'-help OFF: "Disabilita il logger."; FATAL: "Errori gravi che causano la cessazione anticipata."; ERRORE: "Altri errori di runtime o condizioni impreviste."; WARN: "Uso di API obsolete, uso scadente dell'API, errori" quasi "," + "altre situazioni di runtime non desiderabili o impreviste, ma non necessariamente errate."; INFO: "Interessanti eventi di runtime (avvio / arresto)."; DEBUG: "Informazioni dettagliate sul flusso attraverso il sistema.";  

Lo usi in questo modo:

@include logger-help; 

... e si compila come:

logger-help OFF: "Disabilita il logger."; FATAL: "Errori gravi che causano la cessazione anticipata."; ERRORE: "Altri errori di runtime o condizioni impreviste."; WARN: "Uso di API deprecate, uso scadente dell'API, errori" quasi ", altre situazioni di runtime indesiderabili o impreviste, ma non necessariamente errate."; INFO: "Interessanti eventi di runtime (avvio / arresto)."; DEBUG: "Informazioni dettagliate sul flusso attraverso il sistema."; 

Niente di speciale. L'altro mixin extra è molto più interessante. Usa la cronologia per stampare tutti i registri che sono stati registrati durante la compilazione.

@mixin logger-print-logs // Aprire un nuovo logger di registrazione 'logger-logs' -log // Loop sulla storia @each $ level, $ logging-logger-conf ("history") // Controllare se il livello di registrazione corrente da ciclo // deve essere visualizzato o meno in base al livello minimo // e alla lunghezza del suo valore (nessun registro, nessuna stampa). @if index (logger-conf ("levels"), $ level)> = logger-conf ("min") e length ($ logs)> 0 // Passa in rassegna i log registrati e li stampa. @each $ accedi a $ logs # $ livello: $ log;  

Ancora una volta, uso semplice:

@include logger-print-logs; 

... quale output (basato sul nostro esempio precedente):

logger-logs WARN: "Dovresti prestare attenzione a questo."; WARN: "Questo potrebbe essere migliorato."; ERRORE: "Qualcosa è rotto";  

Esempio

// Crea un'istanza di un nuovo logger con 'INFO' come livello minimo per la registrazione. // Se non incluso, verrà automaticamente eseguito nel primo registro. @include logger ("INFO"); // Guida del logger (facoltativo, ovviamente) @ include logger-help; // Log stuff @ include INFO ("Ehi, guarda qua"); @ include INFO ("Porta gli unicorni!"); @include WARN ("Amico, fai attenzione"); // Questo non è stampato ma è ancora tracciato nei log. @include DEBUG ("Debug and stuff."); // Cronologia delle uscite (opzionale) particolarmente utile per il debugging @include logger-print-logs; 

Pensieri finali

Come puoi vedere, il codice è piuttosto leggero alla fine, in più la maggior parte della sua massa sono i commenti. Penso che fornisca una bella API pulita che aiuti a tenere traccia di ciò che viene registrato in un determinato progetto.

Questo è uno strumento rivolto agli sviluppatori di librerie e framework. Se ti capita di essere uno, per favore fai un tentativo se pensi che possa essere utile, e dammi il tuo feedback.

Sentiti libero di prendere il mixin da GitHub, o giocare con esso direttamente su SassMeister.