Přihlášení do systému Go: Výběr systému a jeho použití

Jste v jazyce Go relativně noví. Pravděpodobně jej používáte k psaní webové aplikace nebo serveru a potřebujete vytvořit soubor protokolu. Provedete tedy rychlé vyhledávání na webu a zjistíte, že v go existuje spousta možností pro vytváření logů. Jak víte, kterou z nich vybrat? Tento článek vás vybaví odpovědí na tuto otázku.

Podíváme se na vestavěný balík log a určíme, pro jaké projekty se hodí, a pak prozkoumáme další řešení logování, která jsou v ekosystému Go rozšířená.

Co logovat

Nemusím vám říkat, jak je logování důležité. Logy používá každá produkční webová aplikace, aby pomohly vývojářům a provozu:

  • Vyhledat chyby v kódu aplikace
  • Odhalit problémy s výkonem
  • Provádět postmortální analýzu výpadků a bezpečnostních incidentů

Data, která budete skutečně logovat, budou záviset na typu aplikace, kterou vytváříte. Většinou budete mít určitou variaci následujících údajů:

  • Časové razítko, kdy k události došlo nebo kdy byl protokol vygenerován
  • Úrovně protokolu, jako je ladění, chyba nebo informace
  • Kontextová data, která pomohou pochopit, co se stalo, a umožní snadnou reprodukci situace

Co nezaznamenávat

Obecně byste neměli zaznamenávat žádnou formu citlivých obchodních údajů nebo osobně identifikovatelných informací. Patří sem mimo jiné:

  • Jména
  • IP adresy
  • Čísla kreditních karet

Tato omezení mohou činit protokoly méně užitečnými z inženýrského hlediska, ale zvyšují bezpečnost vaší aplikace. V mnoha případech mohou předpisy jako GDPR a HIPAA zakazovat protokolování osobních údajů.

Představení balíčku logů

Standardní knihovna Go má vestavěný balíček log, který poskytuje většinu základních funkcí logování. Nemá sice úrovně logování (jako je ladění, varování nebo chyba), ale i tak poskytuje vše, co potřebujete k nastavení základní strategie logování.

Tady je nejzákladnější příklad logování:

package mainimport "log"func main() { log.Println("Hello world!")}

Výše uvedený kód vypíše text „Hello world!“ do standardní chyby, ale obsahuje také datum a čas, což se hodí pro filtrování zpráv protokolu podle data.

2019/12/09 17:21:53 Hello world!

Ve výchozím nastavení balíček log tiskne do výstupního proudu standardní chyby (stderr), ale můžete jej přimět k zápisu do místních souborů nebo do libovolného cíle, který podporuje rozhraní io.Writer. Ke zprávě protokolu také přidává časové razítko bez další konfigurace.

Zápis do souboru

Potřebujete-li ukládat zprávy protokolu do souboru, můžete tak učinit vytvořením nového souboru nebo otevřením existujícího souboru a nastavením jako výstup protokolu. Zde je příklad:

package mainimport ( "log" "os")func main() { // If the file doesn't exist, create it or append to the file file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { log.Fatal(err) } log.SetOutput(file) log.Println("Hello world!")}

Při spuštění kódu se do souboru logs.txt.

2019/12/09 17:22:47 Hello world!

Jak již bylo zmíněno, protokoly můžete v podstatě vypisovat do libovolného cíle, který implementuje rozhraní io.Writer, takže máte velkou volnost při rozhodování, kam zprávy v aplikaci protokolovat.

Vytváření vlastních loggerů

Ačkoli balík log implementuje předdefinovaný logger, který zapisuje na standardní chybu, můžeme vytvářet vlastní typy loggerů pomocí metody log.New().

Při vytváření nového loggeru je třeba předat tři argumenty do log.New():

  • out: Jakýkoli typ, který implementuje rozhraní io.Writer, do kterého se budou zapisovat data protokolu
  • prefix: Řetězec, který se připojí na začátek každého řádku protokolu
  • flag: Sada konstant, které nám umožňují definovat, které vlastnosti protokolování se mají zahrnout do každého záznamu protokolu generovaného loggerem (více o tom v další části)

Tuto vlastnost můžeme využít k vytvoření vlastních loggerů. Zde je příklad, který implementuje loggery Info, Warning a Error:

package mainimport ( "log" "os")var ( WarningLogger *log.Logger InfoLogger *log.Logger ErrorLogger *log.Logger)func init() { file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { log.Fatal(err) } InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) WarningLogger = log.New(file, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile) ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)}func main() { InfoLogger.Println("Starting the application...") InfoLogger.Println("Something noteworthy happened") WarningLogger.Println("There is something you should know about") ErrorLogger.Println("Something went wrong")}

Po vytvoření nebo otevření souboru logs.txt v horní části funkce init pak inicializujeme tři definované loggery zadáním výstupního cíle, prefixového řetězce a příznaků logu.

Ve funkci main jsou loggery využity voláním funkce Println, která zapíše nový záznam do souboru protokolu. Po spuštění tohoto programu bude do souboru logs.txt zapsáno následující:

INFO: 2019/12/09 12:01:06 main.go:26: Starting the application...INFO: 2019/12/09 12:01:06 main.go:27: Something noteworthy happenedWARNING: 2019/12/09 12:01:06 main.go:28: There is something you should know aboutERROR: 2019/12/09 12:01:06 main.go:29: Something went wrong

Všimněte si, že v tomto příkladu zapisujeme do jediného souboru, ale pro každý logger můžete použít samostatný soubor tak, že při vytváření loggeru předáte jiný soubor.

Příznaky loggeru

Konstanty příznaků loggeru můžete použít k obohacení zprávy loggeru o další kontextové informace, jako je soubor, číslo řádku, datum a čas. Například předání zprávy „Něco se pokazilo“ prostřednictvím loggeru s níže uvedenou kombinací příznaků:

log.Ldate|log.Ltime|log.Lshortfile

vypíše

2019/12/09 12:01:06 main.go:29: Something went wrong

Naneštěstí není možné ovlivnit pořadí, v jakém se zobrazují, ani formát, ve kterém jsou prezentovány.

Zavedení logovacích rámců

Použití balíčku log je skvělé pro lokální vývoj, kdy je důležitější získat rychlou zpětnou vazbu než generovat bohaté strukturované logy. Mimo to bude většinou lepší použít logovací framework.

Hlavní výhodou použití logovacího frameworku je, že pomáhá standardizovat data logů. To znamená, že:

  • Je snazší číst data protokolů a porozumět jim.
  • Je snazší shromažďovat protokoly z několika zdrojů a předávat je centrální platformě k analýze.

Kromě toho je protokolování v podstatě vyřešeným problémem. Proč znovu vynalézat kolo?

Výběr logovacího frameworku

Rozhodnout se, který framework použít, může být problém, protože na výběr je několik možností.

Dva nejoblíbenější logovací frameworky pro Go se zdají být beglog a logrus. Popularita glogu je překvapivá, protože nebyl několik let aktualizován. logrus je lépe udržovaný a používá se v populárních projektech, jako je Docker, takže se na něj zaměříme.

Začínáme s logrusem

Instalace logrusu je stejně jednoduchá jako spuštění níže uvedeného příkazu v terminálu:

go get "github.com/Sirupsen/logrus"

Jednou ze skvělých věcí na logrusu je, že je zcela kompatibilní s balíčkem log standardní knihovny, takže můžete všude nahradit import logu log "github.com/sirupsen/logrus" a bude to prostě fungovat!

Upravme náš předchozí příklad „hello world“, který používal balík log, a místo něj použijme logrus:

package mainimport ( log "github.com/sirupsen/logrus")func main() { log.Println("Hello world!")}

Spuštěním tohoto kódu získáme výstup:

INFO Hello world!

Snadnější už to být nemůže!

Logování v JSON

logrus se dobře hodí pro strukturované logování v JSON, které – vzhledem k tomu, že JSON je dobře definovaný standard – umožňuje externím službám snadno analyzovat vaše logy a také relativně jednoduše přidávat kontext ke zprávě logu pomocí polí, jak je uvedeno níže:

package mainimport ( log "github.com/sirupsen/logrus")func main() { log.SetFormatter(&log.JSONFormatter{}) log.WithFields( log.Fields{ "foo": "foo", "bar": "bar", }, ).Info("Something happened")}

Vygenerovaným výstupem logu bude objekt JSON, který obsahuje zprávu, úroveň logu, časovou značku a obsažená pole.

{"bar":"bar","foo":"foo","level":"info","msg":"Something happened","time":"2019-12-09T15:55:24+01:00"}

Pokud nemáte zájem o výstup protokolů ve formátu JSON, vězte, že pro logrus existuje několik formátovačů třetích stran, které si můžete prohlédnout na jeho stránce Github. Pokud chcete, můžete si dokonce napsat vlastní formátovač.

Úrovně logů

Na rozdíl od standardního balíčku logů podporuje logrus úrovně logů.

logrus má sedm úrovní protokolu: Trace, Debug, Info, Warn, Error, Fatal a Panic. Závažnost jednotlivých úrovní se s postupujícím seznamem zvyšuje.

log.Trace("Something very low level.")log.Debug("Useful debugging information.")log.Info("Something noteworthy happened!")log.Warn("You should probably take a look at this.")log.Error("Something failed but I'm not quitting.")// Calls os.Exit(1) after logginglog.Fatal("Bye.")// Calls panic() after logginglog.Panic("I'm bailing.")

Nastavením úrovně protokolování v logru můžete zaznamenávat jen ty záznamy, které potřebujete v závislosti na prostředí. Ve výchozím nastavení bude logrus zaznamenávat vše, co má úroveň Info nebo vyšší (Warn, Error, Fatal nebo Panic).

package mainimport ( log "github.com/sirupsen/logrus")func main() { log.SetFormatter(&log.JSONFormatter{}) log.Debug("Useful debugging information.") log.Info("Something noteworthy happened!") log.Warn("You should probably take a look at this.") log.Error("Something failed but I'm not quitting.")}

Při spuštění výše uvedeného kódu se zobrazí následující výstup:

{"level":"info","msg":"Something noteworthy happened!","time":"2019-12-09T16:18:21+01:00"}{"level":"warning","msg":"You should probably take a look at this.","time":"2019-12-09T16:18:21+01:00"}{"level":"error","msg":"Something failed but I'm not quitting.","time":"2019-12-09T16:18:21+01:00"}

Všimněte si, že zpráva úrovně Debug nebyla vypsána. Chcete-li ji zahrnout do logů, nastavte log.Level na hodnotu log.DebugLevel:

log.SetLevel(log.DebugLevel)

Zabalit

V tomto příspěvku jsme prozkoumali použití vestavěného balíčku logů a zjistili jsme, že by se měl používat pouze pro triviální aplikace nebo při vytváření rychlého prototypu. Pro vše ostatní je použití hlavního logovacího frameworku nutností.

Podívali jsme se také na způsoby, jak zajistit, aby informace obsažené v logách byly konzistentní a snadno analyzovatelné, zejména při jejich agregaci na centralizované platformě.

Děkujeme za přečtení!

Leave a Reply