Monolog-formázó
Go-ban az első hideg zuhany gyakran a logolás: a nyers log csomag egysoros, kontextus nélküli kimenete, vagy egy strukturált logger key=value fala, amiben egy stack trace egyetlen escape-elt stringgé lapul. A logx/monolog a logx válasza erre fejlesztés közben: egy konzol-handler, ami aktív formátumként beállítva pontosan úgy néz ki, mint amit PHP-ból ismersz.
Így néz ki
Állítsd be a Format: "monolog"-ot a logx.Config-on (vagy a LOG_FORMAT=monolog-ot, ha a logx.Config a környezetből töltődik), és a konzol így ír:
[2026-07-15 10:23:44] shop.INFO: order placed {"order_id":"o_8412","total":12990}
[2026-07-15 10:23:45] shop.WARNING: payment provider slow, retrying {"attempt":2,"provider":"stripe"} in /home/you/shop/internal/modules/shop/surfaces/api/service/payments.go:114
És amikor egy becsomagolt errs hiba éri el a loggert:
[2026-07-15 10:23:46] shop.ERROR: request failed: shop_out_of_stock: shop: place order: inventory: decrement: insufficient stock {"code":"shop_out_of_stock","method":"POST","path":"/api/v1/shop/orders","status":500}
Stack trace:
#0 /home/you/shop/internal/modules/shop/repository/inventory.go(87): repository.(*InventoryRepository).Decrement()
#1 /home/you/shop/internal/modules/shop/orders.go(41): shop.(*Service).PlaceOrder()
#2 /home/you/shop/internal/modules/shop/surfaces/api/service/orders.go(52): service.(*OrderService).PlaceOrder()
#3 /home/you/shop/internal/modules/shop/surfaces/api/http/handlers.go(61): http.(*Handler).PlaceOrder()
#4 {main}
Ami ebben a pár sorban dolgozik:
- A channel nevezi meg a rekord forrását (jellemzően egy service nevét), több service logját egymás mellett futtatva is látod, ki beszél. A
WARNszint Monolog-hűenWARNING-ként jelenik meg. - A hiba fejléce a
errs-kóddal kezdődik (shop_out_of_stock: ...), így a hibamód a JSON-rész elolvasása nélkül azonosítható; a kód a JSON contextben is ott van. - A stack trace teljes, abszolút útvonalakkal: a legtöbb terminál és editor kattinthatóvá teszi. A frame-ek a hiba keletkezési pontjától felfelé sorakoznak; a dependency-kből (Go module cache-ből) jövő frame-ek halványítva jelennek meg, a runtime-belépési frame-eket a
{main}zárósor helyettesíti. - A hiba-attr nem duplázódik: a
slog.Any("error", err)értéke a fejlécbe és a stack-blokkba kerül, a JSON contextből kimarad. - Szín csak terminálon: a
NO_COLOR-t és aTERM=dumb-ot tiszteletben tartja, pipe-olt kimenetben nincs escape-szemét. - Egy
WARN/ERRORrekorderrsstack nélkül is kap lokációt: a hívási helye kerül a sor végérein file:lineformában.
logx/monolog közelről
A formátum-driver önálló csomag, egyetlen konstruktorral:
import "github.com/gp-system/logx/monolog"
func NewHandler(w io.Writer, level slog.Level, channel string) *Handler
A logx.ConsoleHandler ezt adja vissza, ha a Config.Format "monolog", de bárhova bekötheted, ahol egy slog.Handler elvárt, a logx többi részével vagy anélkül. Amit érdemes tudni róla:
slog-natív. AWith(...)attr-ok és aWithGroup(...)csoportok helyesen, beágyazott JSON-ként jelennek meg a context-részben ("req":{"status":200}).- A stackek forrása az
errs. A handler a rekord első hiba-értékű attr-ját emeli ki; ha az (vagy a wrap-láncában bármi)errshiba, azerrs.FullFramesteljes útvonalú stackje renderelődik. Egy simaerrors.Newhibánál nincs stack-blokk: helyette a log hívási helye kerül a sorba. - Párhuzamos futásban is atomi sorok: a handler klónjai közös mutexen írnak, torn write nincs.
Strukturált hibák a logban: errs nélkül semmi ilyen nincs
A fenti stack trace-eket az errs hibákból nyeri, és ez nem konzol-privilégium: az *errs.Error implementálja a slog.LogValuer-t, így bármely strukturált slog.Handler-ben, nemcsak a logx/monolog-ban, a hiba csoportként bomlik ki:
"error": {
"msg": "shop: place order: inventory: decrement: insufficient stock",
"code": "shop_out_of_stock",
"public": "Egy vagy több tétel nincs elegendő készleten.",
"chain": [ { "msg": "shop: place order", "file": "service/orders.go", "line": 52, "..." : "…" }, "…" ],
"stack": [ { "file": "repository/inventory.go", "line": 87, "..." : "…" }, "…" ]
}
Ugyanaz a hiba tehát dev konzolon PHP-stílusú stack trace, és gépi feldolgozásra kész JSON bármely más handlerben, amibe fanoutolod (a framework OTel log-bridge-e és Sentry-handlere is köztük van, lásd Telemetry: Logolás), mindez egyetlen return err-ből.
Hova lettek a fájl-logok?
Sehova, és ez szándékos: nincs itt single/daily file driver, a logx/monolog arra az io.Writer-re ír, amit a NewHandler-nek átadsz, jellemzően stdoutra. Ez a 12-factor elvet követi: a logok eseményfolyamként a stdout-ra mennek, a gyűjtés-forgatás-őrzés pedig a platform dolga, nem a loggeré. Konténerben a runtime úgyis elkapja a stdout-ot; egy collector, vagy egy sima shell-redirect, viszi tovább onnantól.
Ha lokálisan mégis fájlba akarod fogni: go run ./cmd/myapp 2>&1 | tee myapp.log; pipe-ban a handler automatikusan színek nélkül ír.
Monolog-fogalmak megfelelői
Ha a config/logging.php felől érkezel, így képződnek le a fogalmak:
| Monolog | gpsystem | Hol |
|---|---|---|
| channel neve | amit channel-ként átadsz a NewHandler-nek (a framework az OTEL_SERVICE_NAME-et használja) | a monolog-sor shop.INFO része |
stack driver (több channel egyben) | logx.Fanout | logx: Áttekintés |
channel level küszöb | logx.LevelFilter | logx: Áttekintés |
single / daily file driver | nincs (stdout az egyetlen cél) | lásd fentebb |
formatter (LineFormatter, JSON) | logx.Config.Format (auto / monolog) | logx: Áttekintés |
Log::info('msg', ['ctx' => ...]) | slog.InfoContext(ctx, "msg", slog.Any("ctx", ...)) | app-kód |
Log::withContext([...]) | logger := slog.Default().With(...) | app-kód |
| Sentry / Slack channel a stackben | egy másik handler a fanoutban | a saját wiringed, vagy a framework fanoutja, lásd Telemetry: Logolás |
Kapcsolódó oldalak
- logx: Áttekintés: telepítés,
Config, és aConsoleHandler/LevelFilter/Fanoutkompozíció, amibe ez a formázó bekapcsolódik. - errs: Áttekintés: a hibatípus, amiből ennek a formázónak a stack trace-ei jönnek.
- Telemetry: Logolás: hogyan fanoutolja a framework ezt a handlert az OTel log-bridge és a Sentry capture handler mellé.