← Projecten

Van Graylog naar Loki zonder één applicatie aan te passen

Applicatielogs van een acceptatieomgeving verhuisd van Graylog naar Loki. Geen codewijziging bij de developers, omschakeling via DNS, en onderweg twee bugs gevonden en gepatcht in de GELF-ontvanger van Grafana Alloy.

Aanleiding

Een Graylog-server verzamelde de applicatielogs van de acceptatieomgeving: .NET-services met Serilog, PHP-workers en een paar losse componenten, samen enkele honderden berichten per seconde via GELF. De server was verouderd, stond los van de rest van de observability-stack en kende maar één bewaartermijn voor alles.

De opdracht: alles naar Loki, zodat logs naast metrics en traces in Grafana staan. Met drie randvoorwaarden:

  1. Developers hoeven niets te veranderen. Geen nieuwe logbibliotheek, geen config per applicatie.
  2. Geen extra bewegende delen. Geen Vector of Fluentd ernaast, het moet in de bestaande Alloy.
  3. Developers moeten zelf stromen kunnen toevoegen, zonder tussenkomst van beheer.

Aanpak

Inventariseren. Eerst gemeten wat er binnenkwam: welke inputs, hoeveel berichten per seconde, welke servers. Er bleken precies vijf verzenders te zijn. Het GELF-adres stond niet in code maar verspreid over tientallen supervisor-configs en omgevingsvariabelen.

Ontvangen op de oude poorten. Alloy luistert op dezelfde poortnummers als Graylog, UDP én TCP. Voor GELF over TCP (die Alloy niet kent) gebruikt de config een OpenTelemetry-tcplog-receiver met een null-byte als scheidingsteken. De PHP-kant hoefde daardoor ook niets te veranderen.

Omschakelen via DNS. In plaats van tientallen configs aan te passen, wijst de hostnaam van de oude Graylog-server nu naar de nieuwe ontvanger. Voor de overgang zorgde een tijdelijke hosts-regel op de vijf servers dat het direct werkte, nog voordat de DNS-wijziging was doorgevoerd.

Testen per verzender. Een klein script stuurde vanaf elke server testberichten van verschillende groottes naar elke poort en controleerde of ze in Loki aankwamen. Zo vielen firewallregels en lokale uitgaande filters meteen op, vóór de echte omschakeling.

Twee bugs in de GELF-ontvanger

Na het omzetten van de eerste server kwamen er honderden parse-fouten per seconde binnen. Twee oorzaken:

  1. Afgekapte UDP-pakketten. De GELF-bibliotheek in Alloy maakte een leesbuffer van 1420 bytes, terwijl de Serilog-sink pakketten tot 8 KB stuurt. Alles daarboven werd afgekapt en was daarna corrupte gzip.
  2. "full_message": null. Veel berichten bevatten dat veld met waarde null, en de bibliotheek wees elk bericht af met een ongeldig veldtype.

Beide zijn opgelost met een kleine patch op de Go-bibliotheek, ingebouwd in een eigen Alloy-image. Dat image wordt in GitLab CI gebouwd, gescand met Trivy en pas uitgerold na een end-to-end test met alle berichtgroottes. Resultaat: nul parse-fouten en nul gedropte berichten.

Een bijvangst: de CI-build vulde de schijf van een Kubernetes-node met een buildah-vfs-laag. De pipeline is daarom gesplitst: eerst de binary bouwen in een Go-image, daarna een dunne image-stap.

Zelf stromen beheren

Developers voegen een stroom toe met een merge request op één bestand: welke applicatie, welke labels uit de GELF-velden, of debugberichten bewaard moeten worden. CODEOWNERS bepaalt wie meekijkt. Debugregels worden standaard bewaard, met een opt-out per applicatie.

Privacy

Applicatielogs kunnen persoonsgegevens bevatten. Dezelfde maskeringsregels als voor traces draaien nu ook op de GELF-route. Een valkuil daarbij: de GELF-ontvanger her-encodeert JSON en schrijft & als &, dus de regex moet beide vormen kennen.

Resultaat

  • Alle verzenders binnen één dag omgezet; de oude server kreeg daarna nul berichten en kon uit.
  • Nul aanpassingen in applicatiecode.
  • Logs in Grafana naast metrics en traces, met dashboards per applicatie en alerts op een stilvallende inname.
  • Een runbook per alert en documentatie voor beheer én developers.

Les

Na een naamswijziging moet je álles herstarten wat vóór die wijziging is gestart. De laatste berichten naar de oude server kwamen van queue-workers die al vijftien uur draaiden en het oude adres nog in het geheugen hadden. Een scriptje dat alles herstart wat ouder is dan N uur, loste dat op.