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