Snilld

Loguru i praksis: idempotent, struktureret og driftssikker logging til Python

En hands-on tutorial viser, hvordan Loguru kan samles til en idempotent, struktureret og produktionsparat loggingpipeline med rotation, kompression, JSON-output og beskyttelse mod duplikerede handlers – også i tråde og processer. Vi gennemgår hvorfor det betyder noget i DevOps og MLOps, hvor opsætningen stadig kan knække, og hvordan man får det ind i CI/CD, observability og privacy-arbejdet uden drama.

1. juni 2026 Peter Munkholm

En praktisk tutorial fra 31. maj 2026 viser trin for trin, hvordan man bygger en robust Loguru-opsætning i Python, der kan køres igen og igen uden at drukne i dublerede handlers og rodede filer. Hele pakken ligger i et Colab-klar flow og kan relativt let flyttes ind i en container. Vi kiggede den igennem, kørte små tests og holdt den op mod vores erfaringer fra drift af AI-pipelines. Noget fungerer forbavsende godt – andet kræver omtanke.

Hvis du sidder med DevOps eller MLOps-ansvar, kender du smerten: En service genstarter, og pludselig skriver fem handlers til samme fil. Alerts skriger. Grafen hopper. Ingen tør slette noget af frygt for at miste spor. Tutorialen går efter præcis det problem: idempotens i logging, så init-kode kan køre igen uden bivirkninger. Det lyder småt, men i praksis er det forskellen på ro og panik en mandag morgen.

Hvorfor det her rammer direkte i værktøjskassen

Teams bygger ofte features hurtigere, end de bygger logging. Det går, indtil træningsjobs og batch-pipelines skalerer op med tråde og processer. Idempotent, struktureret logging reducerer støj, gør alarmer skarpere og forkorter tiden fra fejl til fix. I vores egne klientprojekter har manglende idempotens givet fordoblede logfiler og falske alerts på CPU-peak, selvom intet var galt. Det koster tid og nerver.

Det overraskende er, hvor ligetil byggestenene i Loguru er. Ikke magi – bare konkrete patterns, man limer ind i bootstrap-koden for at få ensartet struktur på tværs af apps og jobs. JSON-logs, rotation, kompression og retention ligger som små, forståelige funktioner. Det hjælper markant, når man kæmper med sporbarhed på modelruns.

Nærbillede af en komprimeret, gzippet logrulle i en vægholder; sløret mærkning, cyan‑rimlys og indigo skygger signalerer rotation og retention i drift.

Hurtigt overblik over komponenterne

Tutorialen går systematisk frem: en global patcher, der tilføjer metadata som env og en sekvens-tæller til hvert record. En læsbar console-formatter. En in-memory sink til hurtige asserts og testbarhed. Størrelsesbaseret filrotation, gzip-kompression og en simpel retention-funktion, der holder de seneste filer. Dertil interception af standardbibliotekets logging, så tredjepartspakker ikke skyder sidelæns. Og eksempler med async, tråde og multiprocessing.

Pointen er ikke at opfinde et nyt observability-system, men at gøre Python-logging forudsigelig, struktureret og genkørbar i et moderne setup. Vi har selv stået i et mødelokale i Aarhus med en kunde og måttet starte med at tælle antallet af aktive handlers pr. proces. Det burde man slet ikke bruge tid på.

Idempotens og genkørbarhed uden duplikerede handlers

Handler-duplication opstår, fordi init-kode ofte lever flere steder: i main, i en Celery worker, i en notebooks setup-celle. Tutorialens mønster bruger en klar init-sekvens, hvor man rydder eksisterende sinks og kun tilføjer de planlagte. Det gør opsætningen genkørbar. Vi kørte en hurtig check, hvor vi loggede handler-count før og efter init, og fik samme tal hver gang – ingen skjulte ekstra-sinks, heller ikke efter flere imports.

Idempotens handler også om konventioner: deterministisk filnavn pr. miljø, en ensartet level-strategi og ét sted at samle formatters. Her gør global patcher-tricket arbejdet. Hvert record får fx seq og env automatisk. Når noget fejler klokken 02.14, er det guld at kunne søge “env=staging seq between 12000 and 13000”.

Banner

Struktureret logging og kontekst der kan arbejdes med

Tutorialen viser JSON-output og brug af extra-felter – ikke til pynt, men for at gøre logs maskinvenlige. Callable formatters og mulighed for at binde kontekst på loggeren gør det let at hæfte app, version, run_id og model_tag på records. Læg trace_id på, når gatewayen er klar. I AI-pipelines betyder det, at man kan følge en feature fra data ingestion til batch inferens uden at krydse fingre.

Vi har i praksis bundet model_name og dataset_hash på hver logger i træningsprocesser. Det lyder banalt, men det gjorde, at vi kunne korrelere en pludselig F1-dykning til et nyt datasæt-udtræk på få minutter. Den slags detaljer glemmer man ikke.

Tekniker under installation af en sidecar‑log‑agent i et lille servicecloset; grøn stabile lys på enhed, amber alarm i nærheden, deep indigo og cyan behandling.

Konkurrence og concurrency uden at snuble

Tutorialen berører tråde, async og multiprocessing. Vores vurdering: Loguru er stærk på tråde og async og acceptabel på multiprocessing, hvis man konfigurerer korrekt. Den dokumenterede vej er at bruge enqueue-flaget, så logrecords sendes via en kø til en enkelt forbrugende proces. Det reducerer lock-kampe og gør rotation mere sikker. Uden enqueue kan filrotation i flere processer skabe race conditions. Vi har set det ske – det er ikke sjovt.

Vigtigt: “Multiprocessing-safe” er ikke en binær sandhed. Under høj belastning og på NFS-mounts kan man stadig ramme kantcases. GitHub-issues på Loguru diskuterer det nuanceret, og praktiske erfaringer matcher. I produktion anbefaler vi at centralisere skrivning via en sidecar eller agent – eller som minimum sikre én dedikeret log-skriverproces med enqueue og backpressure-mekanismer.

Rotation, kompression og retention i driftens virkelighed

Tutorialen viser størrelsesbaseret rotation med en lille MAX_BYTES-grænse, gzip-komprimering af afsluttede filer og en keep-latest-retention-funktion. Det er en solid skabelon. I drift skal den oversættes til politikker: Hvor længe skal rå JSON ligge lokalt? Hvad sendes videre til central log? Hvad gør I, når compliance kræver 180 dages retention på audit-traces men kun 14 dage på debug?

Kompression sparer lager, men koster CPU. Aggressiv rotation øger I\/O og fragmentering. En enkel SLO hjælper: maks 5 procent CPU på kompression i spidslast og et mål om 50–70 procent komprimeringsratio på tekst-logs. Det er ikke finmålt videnskab, men det holder maskinerne rolige, især når flere træningsjobs ruller samtidig.

Interception af standard logging og tredjepartsbiblioteker

Et stærkt trin i tutorialen er en InterceptHandler, der sluger standardbibliotekets logging og kanaliserer alt til Loguru. Det mindsker risikoen for, at libs skriver med hver deres formatter og niveau. Fordelen ses især i data-stakke, hvor requests, boto3 eller sklearn ellers ville levere hver sin stil. Vi kørte en hurtig test med en ekstern logger og kunne se records lande pænt i memory-sinken med samme struktur og kontekst som vores egen kode.

Pas på én ting: Hvis man intercepter uden at rydde eksisterende handlers på de pågældende loggere, får man netop de dubletter, man prøver at undgå. Idempotens-checks bør derfor inkludere standardloggere og root – ikke kun Loguru.

Koncept: før/efter opsætning af idempotent logging — kaotisk amber‑alarmer til venstre, rolig grøn drift til højre; deep indigo/cyan tone.

CI\/CD, central observability og modelmonitorering

Det vigtige er, hvor hurtigt det kan gøres testbart. Læg en lille smoke-test i CI, der bootstrapper logging, logger et par records og asserter, at handler-count og memory-sink indeholder præcis de forventede niveauer. Mål tid per log og filstørrelse pr. N records. Gem artefakterne i jobbet. Så fanger I regressioner i logging ligesom i kode.

I drift skal JSON-logs videre til noget centralt. Et udbredt mønster er at lade Loguru skrive lokalt med rotation\/kompression og lade en agent løfte filerne videre til Elasticsearch, OpenSearch, Splunk eller cloud logging via Vector, Fluent Bit eller Fluentd. Det giver både hurtig lokal fejlsøgning og konsistent søgbarhed i platformen. For modelmonitorering binder I logfelter til dashboards: run_id, model_version, data_slice samt latens og fejltyper. En håndfuld velvalgte felter er nok.

Privacy, redaction og niveaustyring

Tutorialen rører ikke for alvor ved anonymisering. I AI-miljøer er det ikke valgfrit. Vi anbefaler en formatter, der kører redaction på kendte mønstre som e-mails, tokens og CPR-lignende sekvenser, før records rammer disk. Kombinér med et governance-lag, der begrænser DEBUG i produktion og sikrer, at PII kun må logges i hashed form i sikre miljøer. Rå kundeoplysninger i en gz-fil, der ender i et åbent S3-bucket, er et mareridt.

Banner

Niveaustyring skal også være bevidst. Et par enkle regler hjælper: INFO for forretningshændelser, DEBUG for udviklingsdetaljer, WARNING for recoverable fejl, ERROR for tab af funktion, CRITICAL for systemisk risiko. Og lad være med at logge stacktraces på INFO. Det sker oftere, end man tror.

Performance og hvad I skal måle

Der mangler officielle tal i tutorialen for throughput og latency. Fair nok i en guide – men i produktion skal I måle. Start simpelt: logs per sekund, gennemsnitlig skrivningstid per record, komprimeringsratio, tid brugt på rotation og CPU-andel til logging. Mål både med og uden enqueue. Mål med én og med fire processer. Skriv resultaterne ind i jeres performance-rapport, ikke i en løs wiki-side, der drukner.

Vi så i en intern test, at enqueue gav en lille latenstigning, men stabiliserede I\/O under parallel belastning. Til gengæld faldt risikoen for fil-locks. Det bytteforhold er ofte rigtigt i produktion, hvor forudsigelighed trumfer rå hastighed.

En konkret implementeringstjekliste

Her er vores korte, ikke-symmetriske tjekliste, som vi bruger ved onboarding. Den er skåret af erfaring, ikke for pynt:

  • Ryd alle eksisterende sinks og standardhandlers ved init. Tæl dem før og efter.
  • Bind env, app, version, run_id og evt. model_tag via global patcher. Hold felterne faste.
  • Aktivér JSON-sink til fil. Brug size-rotation og gzip. Test at rotation ikke trigges samtidigt i flere processer.
  • Slå enqueue til, når flere processer logger til samme destination. Verificér backpressure.
  • Intercepter standard logging. Ryd dubletter på root og kendte tredjepartsloggere.
  • Indfør redaction i formatter. En lille funktion dækker de fleste lækager.
  • Mål throughput og latenser i CI. Gem artefakter. Altid.
  • Send filer videre via agent, ikke fra applikationsprocessen, når belastningen er høj.

    Tradeoffs og hvornår I skal vælge noget andet

    Loguru er fremragende som applikationsnær logger med stærk DX og gode formatteringsmuligheder. Men den er ikke en log-transport. Når datamængden stiger, og I vil have fleksibel buffering, filtrering og routing, så kig på centraliserede agenter som Vector eller Fluent Bit. De er bygget til at flytte logs stabilt, genlæse fra disk efter nedbrud og håndtere backoff og retries. Loguru kan og bør blive i applikationen, men ikke stå alene, når I ruller bredt ud.

    Cloud-native logging kan også være klogt i fx GKE eller EKS med managed backends. Her vejer man driftssimplicitet op mod behovet for strukturering i appen. Tommelfingerregel: behold struktureret JSON i appen uanset hvad. Det gør jer mindre afhængige af downstream-parsers og mere robuste ved platforms-skift.

    Det vi validerede og hvor vi er i tvivl

    Vi har læst tutorialen og bekræftet, at den dækker idempotent opsætning, structured og contextual logging, global patcher, callable formatters, in-memory sink, JSON-filer, rotation, gzip, retention, async, tråde, multiprocessing-eksempel og interception af standard logging. Vi har kørt små lokale tests på idempotens og interception med en memory-sink, inklusiv en simpel “has_level”-assert. Det opførte sig stabilt.

    Usikkerhederne ligger primært i multiprocessing under høj belastning og på delte filsystemer. Tutorialen nævner sikkerhed i multiprocess-scenarier, men vi har ikke set reproducerbare benchmarks i kilden. Loguru-dokumentationen anbefaler kø-baseret skrivning med enqueue for sikkerhed; det matcher vores erfaring. Vi linker til docs og issues, så man kan dykke ned i kantcases.

    Fra Colab til container uden at miste værdi

    Colab er praktisk til at lære patterns. Produktion kræver Docker og orkestrering. Oversættelsen er lige til: læg init-koden i en dedikeret bootstrap-funktion, montér en skrivevolumen til logs, brug agent til transport, og sæt varianter af retention pr. miljø via env-variabler. Vi har set teams lykkes ved at lade samme kodebase køre i både batch-jobs og API-tjenester med identisk logging-init. Ensartethed vinder hver gang.

    Én praktisk note om images: sørg for, at gzip og rotator er tilgængelige i samme image som appen for at undgå PATH-overraskelser. Vi har set en alpine-optimering, der manglede gzip og efterlod ukomprimerede filer i uger. Småting, stor regning.

    Konklusion i øjenhøjde

    Kort sagt: Tutorialen leverer en brugbar, idempotent Loguru-opsætning med strukturerede logs, rotation, kompression og interception. Den dækker det meste af det, man har brug for i en rigtig Python-app, og koden er klar. Multiprocessing kræver stadig omtanke og helst enqueue eller en ekstern agent. Retention og privacy er jeres ansvar, men patternsne gør det let at bygge ordentlig governance ovenpå.

    Det, der bliver hængende, er hvor hurtigt man får reel observability-gevinst ved at standardisere felter og sikre idempotens. Det virker banalt, men effekten i drift er stor – især når man står midt i en hændelse.

Kilder

    Gør brugeroplevelsen bedre.
    Hvilket firma arbejder du for?