Een logger-mixin bouwen in Sass

In deze zelfstudie bouwen we een "logger" -mixin, die een flexibel, informatief logboek als CSS oplevert wanneer Sass wordt gecompileerd.

Logging is het proces van het registreren van applicatie-acties en staat op een secundaire interface. - Code Project

Het idee

Onlangs spraken aardige onderhouder Reda Lemedan en ik over Sass en plotseling zag ik een interessante mix van zijn:

@include -neat-warn ("Something is wrong"); 

Ik vroeg hem wat deze mixin doet, en hij vertelde me dat het eigenlijk een omslag is voor de @waarschuwen richtlijn van Sass die controleert of de gebruiker bereid is om waarschuwingen van Neat in de console af te drukken (op basis van een globale variabele).

Dus ik dacht bij mezelf waarom zou je daar stoppen?? en begon diezelfde avond met het idee te spelen. Mijn idee was om een ​​verpakking voor beide te bouwen @waarschuwen en @fout (van Sass 3.4) om bibliotheek- en raamwerkontwikkelaars te helpen verschillende soorten berichten af ​​te drukken (info, debug, warn, error ...) en alle logboeken bij te houden.

Mijn huidige implementatie biedt:

  • 5 niveaus van loggen (DEBUG, INFO, WAARSCHUWEN, FOUT en FATAL);
  • een minimum niveau waarop de logger begint met afdrukken;
  • een geschiedenis van alle logs, die kan worden afgedrukt als CSS;
  • een gebruiksvriendelijke API met eenvoudig te gebruiken functies;
  • een helper om meer te leren over verschillende niveaus van loggen.

Hoe werkt het?

Het bleek vrij eenvoudig te zijn. We hebben een globale variabele nodig die de hele configuratie bevat, en een mix die dient als een verpakking voor onze consoleprintinstructies.

Omdat we willen dat onze globale configuratie (tot op zekere hoogte) aanpasbaar is, verpakken we zijn verklaring in een mixin. Dit is niet alleen handiger, maar het is ook leuker voor de eindgebruiker.

Dus we hebben een mix, laten we het noemen houthakker dat is slechts bedoeld om één keer te worden aangeroepen, waardoor een globale kaart wordt gemaakt die onze configuratie bevat. Dan hebben we onze verpakking, logboek, die een logging-niveau accepteert (bijvoorbeeld WAARSCHUWEN of FOUT) en het bericht om als argumenten te loggen. Dat is het eigenlijk wel.

Om het de ontwikkelaars makkelijker te maken, zullen we een aantal stenografische functies aanbieden om verschillende niveaus te loggen. Bijvoorbeeld in plaats van te moeten typen:

@ include log ("ERROR", "Er is hier niet genoeg eenhoorn."); 

… we zouden kunnen hebben:

@ include ERROR ("Er is hier niet genoeg eenhoorn."); 

Dus je zult eindigen met een API die er als volgt uitziet:

// Start een logboek // dat begint met het afdrukken van logboeken op 'INFO'-niveau. // Dit betekent dat de logboeken 'DEBUG' niet worden getoond. @include logger ("INFO"); // Log dingen. @ include ERROR ("Er is hier niet genoeg eenhoorn."); 

We voegen ook enkele mixins toe om een ​​aantal coole extra functies te introduceren:

  • één afdrukinformatie over elk logniveau als herinnering;
  • één die alle logs afdrukt die zijn geregistreerd in de huidige compilatie.

De API bouwen

Logger Constructor

Laten we beginnen met het begin, zullen we? De logger bouwer. Dit zou een enkele parameter moeten accepteren: het niveau waarop de logger logs in de console moet gaan afdrukken.

Dit is een vrij veel voorkomend patroon voor logging-systemen. Bijvoorbeeld:

  • Als u alleen fouten wilt afdrukken (FOUT en FATAL), zou je schrijven @include logger ("ERROR").
  • Als je alles wilt afdrukken, zou je het doen @include logger ("ALL"), wat in feite hetzelfde is als het laagste logniveau (DEBUG).
  • Als u de logger helemaal wilt uitschakelen, voert u uit @include logger ("OFF").

Notitie: u vindt meer informatie over logboekniveaus in deze StackOverflow-thread of in de documentatie van Apache logs.

@mixin logger ($ minimum-level) // Lijst met beschikbare niveaus $ niveaus: "DEBUG", "INFO", "WARN", "ERROR", "FATAL"; / / Zorg ervoor dat de gegeven string $ minimum-niveau in hoofdletters is: naar hoofdletter ($ minimum-level); // Als het niveau 'ALL' is, volg dan het laagste niveau van alle @if $ minimum-level == "ALL" $ minimum-level: nth ($ levels, 1);  // Als het niveau ongeldig is, ga dan arbitrair met 'INFO' @ anders indexeer niet ($ niveaus "UIT", $ minimum-niveau) $ minimum-niveau: "INFO";  // Creëer globale variabele $ logger-configuratie: (// Lijst met beschikbare niveaus "niveaus": $ niveaus, // Lijst van niveaus die zijn afgedrukt met '@error' "errors": "FATAL" "ERROR", / / Minimum niveau (als een index van '$ niveaus') om "min" af te drukken: index ($ niveaus, $ minimum-niveau), // Of de logger is ingeschakeld "enabled": $ minimum-level! = " UIT ", // Een kaart om alle logboeken bij te houden" geschiedenis ": (" DEBUG ": ()," INFO ": ()," WARN ": ()," ERROR ": ()," FATAL ": () ) ) !globaal;  

De bovenstaande code zou grotendeels vanzelfsprekend moeten zijn, maar ik heb een aantal opmerkingen toegevoegd om alles duidelijk te maken. Zoals je ziet, doet deze mixin niet veel, behalve een globale variabele maken. Dat is niet zo erg?

Voordat we verder gaan, maken we een kleine hulpfunctie waarmee we gemakkelijk een waarde uit deze globale kaart kunnen halen. Omdat je weet, typen map-get ($ logger-configuratie, ...) is niet op afstand plezierig. Hoe zit het met logger-conf (...) in plaats daarvan?

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

Log Wrapper

Oké, laten we doorgaan naar het echte logboek functie die dingen in de console afdrukt. Niet alleen zou het de gegeven berichten in de gebruikersconsole moeten uitvoeren, maar het zou ook de geschiedenis moeten bijwerken om bij te houden wat er wordt gelogd (wat al dan niet nuttig zou kunnen zijn).

Dat klinkt moeilijk. Nou, maak je geen zorgen, het wordt zo glad als boter. We weten al dat deze mixin slechts twee parameters moet accepteren: het logging-niveau en het bericht.

@mixin-log ($ -niveau, $ -boodschap) // zorg ervoor dat het niveau $ -niveau in hoofdletters is: naar hoofdletter ($ -niveau); // Tenzij het is uitgeschakeld, gaat u verder naar @if logger-conf ("enabled") // De huidige index van het niveau ophalen // Bijvoorbeeld, 'DEBUG' zou '1' $ index-current-level zijn: index (logger-conf ( "niveaus"), $ niveau); // Als '$ level' ongeldig is, valt // arbitrair terug op 'INFO' @ anders dan $ index-current-niveau $ level: "INFO";  // Update logger-historie @ inclusief logger-update-geschiedenis ($ niveau, $ bericht); // Tot slot, print bericht in console // als het huidige niveau groter is dan of gelijk is aan het minimumniveau. @if $ index-current-level> = logger-conf ("min") $ print: '[' + $ level + '] ::' + $ bericht; // Druk het af als '@error' als het een error level @if index is (logger-conf ("errors"), $ level) @error $ print;  // Anders gebruiken '@warn' @else @warn $ print;  

Nu moeten we ons bezighouden met het bijwerken van de geschiedenis. Dit is eigenlijk een beetje moeilijker, maar zodra je gewend bent om manipulatie in kaart te brengen, wordt het duidelijker.

Voordat ik de code bekijk, moet ik uitleggen hoe de geschiedenis werkt. Kortom, het is een kaart waarbij sleutels de logging-niveaus zijn en waarden lijsten zijn van gelogde berichten. U kunt bijvoorbeeld zoiets hebben als:

$ _: ("DEBUG": (), "INFO": (), "WARN": ("Je moet hierop letten.", "Dit kan verbeterd worden."), "ERROR": ("Er is iets kapot . ")," FATAL ": ()) 

Oke. Laten we gaan.

@mixin logger-update-history ($ level, $ message) // Krijg history map van configuration $ history: logger-conf ("history"); // Krijg geschiedenislijst voor huidig ​​niveau $ huidige-niveau-geschiedenis: map-get ($ geschiedenis, $ niveau); // Voeg het nieuwe log toe aan de lijst $ current-level-history: add ($ current-level-history, $ message); // Maak een tijdelijke variabele met de nieuwe historiekaart $ logger-history: map-merge ($ history, ($ level: $ current-level-history)); // Update de history map uit de configuratie met onze tijdelijke variabele $ logger-configuratie: map-merge ($ logger-configuratie, ("history": $ logger-history))! Global;  

Het betreft nogal wat onvriendelijke lijnen, maar als je elke regel afzonderlijk uitlegt, is het uiteindelijk logisch.

We zijn hier klaar, maar we hebben gesproken over het toevoegen van stenografische functies. Laten we dat nu doen voordat we vergeten:

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

Dat is het. Een laatste ding dat we zouden kunnen doen, maar het is niet echt verplicht, is testen of houthakker is opgenomen voordat u de algemene kaart probeert te gebruiken. We voorkomen niet alleen domme fouten, maar we kunnen de logger-instantiatie ook optioneel maken door het meteen te doen.

@mixin-logboek ($ -niveau, $ -bericht) // Test of de globale variabele 'logger-configuratie' bestaat. // Als dit niet het geval is, betekent dit dat 'logger' niet is opgenomen, // dus nemen we het op, het willekeurig instellen van het min-niveau op 'INFO'. @if not global-variable-exists ("logger-configuration") @include logger ("INFO");  @if logger-conf ("ingeschakeld") // ... 

Extra's toevoegen

We beginnen met het eerste (en minst nuttige) van beide extra mixins, de helper. Het is echt een gadget op dit punt, omdat het alleen maar een CSS-regel afdrukt met logboekniveaus als selectors en uitleg als waarden.

Dit is bedoeld om ontwikkelaars wat hulp te bieden als ze niet echt weten welk logniveau ze moeten gebruiken. Het had als commentaar kunnen worden geschreven, maar ik wilde dit helpen met printerdingen.

@mixin logger-help // Open een nieuwe 'logger-help' selector logger-help OFF: "Schakel de logger uit."; FATAL: "Ernstige fouten die voortijdige beëindiging veroorzaken."; FOUT: "Andere runtime-fouten of onverwachte omstandigheden."; WARN: "Gebruik van verouderde API's, slecht API-gebruik, 'bijna' fouten, + + andere runtime-situaties die ongewenst of onverwacht zijn, maar niet noodzakelijk verkeerd."; INFO: "Interessante runtime-gebeurtenissen (opstarten / afsluiten)."; DEBUG: "Gedetailleerde informatie over de stroom door het systeem.";  

Je gebruikt het als volgt:

@ inclusief logger-help; 

... en het compileert als:

logger-help UIT: "Schakel de logger uit."; FATAL: "Ernstige fouten die voortijdige beëindiging veroorzaken."; FOUT: "Andere runtime-fouten of onverwachte omstandigheden."; WARN: "Gebruik van verouderde API's, slecht gebruik van API, 'bijna' fouten, andere runtime-situaties die ongewenst of onverwacht zijn, maar niet noodzakelijk verkeerd."; INFO: "Interessante runtime-gebeurtenissen (opstarten / afsluiten)."; DEBUG: "Gedetailleerde informatie over de stroom door het systeem."; 

Niets speciaals. De andere extra mix is ​​veel interessanter. Het gebruikt de geschiedenis om alle logs af te drukken die tijdens de compilatie zijn geregistreerd.

@mixin logger-print-logs // Open een nieuwe 'logger-logs' selector logger-logs // Loop over de geschiedenis @each $ niveau, $ logs in logger-conf ("geschiedenis") // Controleer of het huidige logniveau van lus // moet worden weergegeven of niet op basis van het minimumniveau // en de lengte van de waarde (geen logboek, geen afdruk). @if index (logger-conf ("levels"), $ level)> = logger-conf ("min") en lengte ($ logs)> 0 // Loop over de geregistreerde logs en druk ze af. @each $ log in $ logs # $ level: $ log;  

Nogmaals, eenvoudig gebruik:

@ inclusief logger-print-logs; 

... die zou uitvoeren (op basis van ons eerdere voorbeeld):

logger-logs WARN: "Let hier goed op."; WARN: "Dit kan worden verbeterd."; FOUT: "Er is iets gebroken";  

Voorbeeld

// Start een nieuwe logger met 'INFO' als het minimale logniveau. // Als dit niet wordt opgenomen, gebeurt dit automatisch in het eerste logboek. @include logger ("INFO"); // Logger-help (optioneel, uiteraard) @ inclusief logger-help; // Logboek @ bevatten INFO ("Hé, kijk eens."); @inclusief INFO ("Breng de eenhoorns binnen!"); @include WARN ("Dude, let op"); // Deze wordt niet afgedrukt, maar wordt nog steeds bijgehouden in logboeken. @include DEBUG ("Debug and stuff."); // Uitvoerhistorie (optioneel), vooral nuttig voor foutopsporing @omvat logger-print-logs; 

Laatste gedachten

Zoals u kunt zien, is de code uiteindelijk vrij licht, plus de meeste opmerkingen. Ik denk dat het een mooie, schone API biedt die helpt bij het bijhouden van wat er in een bepaald project wordt vastgelegd.

Dit is hulpmiddel gericht op bibliotheek- en raamwerkontwikkelaars. Als je toevallig een bent, probeer dit alsjeblieft als je denkt dat het nuttig kan zijn, en geef me je feedback.

Voel je vrij om de mixin van GitHub te pakken, of speel ermee rechtstreeks op SassMeister.