Hoe we OpenTelemetry-logs in Rails hebben geconfigureerd

Onlangs moesten we observabiliteit toevoegen aan een Rails-project zonder vastgelegd te worden aan één specifieke vendor. OpenTelemetry was de natuurlijke keuze. Het genereert drie soorten telemetriegegevens, genaamd signals: logs, metrics en traces. Elk signaal is onafhankelijk, waardoor je er één kunt adopteren zonder de andere te gebruiken.

Al deze signalen worden verzonden in een standaard, vendor-agnostisch formaat dat bekend staat als OTLP (OpenTelemetry Protocol). Omdat dit formaat een industriestandaard is, kunnen we in de toekomst van backend wisselen (zoals Datadog, New Relic of Grafana Cloud) zonder enige applicatiecode te herschrijven.

In dit artikel leggen we uit hoe we de OpenTelemetry Ruby SDK hebben geconfigureerd om logs rechtstreeks naar onze vendor, Grafana Cloud, te exporteren. We bespreken ook enkele problemen die we onderweg zijn tegengekomen en de upstream-fixes die we hebben bijgedragen.

Direct exporteren naar de vendor

Een standaard OpenTelemetry-implementatie maakt gebruik van een OpenTelemetry Collector die naast je applicatie draait. De Collector is een apart proces, meestal een sidecar-container of een service op dezelfde host. Je applicatie verzendt telemetrie naar de Collector via het lokale netwerk, waarna de Collector deze gegevens buffert en doorstuurt naar de vendor.

Om onze infrastructuur eenvoudig te houden, hebben we de Collector omzeild en logs rechtstreeks vanuit de Ruby SDK naar Grafana Cloud geëxporteerd. De Scout APM logging gem gebruikt een vergelijkbare aanpak en verzendt logs direct vanuit de app.

Omdat ons logvolume klein is, volstaat de ingebouwde batching van de SDK. Een Collector blijft echter de betere keuze als buffering, sampling of scrubbing buiten de applicatie om nodig is.

De Ruby SDK voor logs instellen

Voeg de volgende gems toe aan je Gemfile:

gem "opentelemetry-sdk"
gem "opentelemetry-logs-sdk"
gem "opentelemetry-exporter-otlp"
gem "opentelemetry-exporter-otlp-logs"
gem "opentelemetry-instrumentation-all"
gem "opentelemetry-instrumentation-logger"

OpenTelemetry is zeer modulair, waardoor elke gem een specifieke verantwoordelijkheid heeft:

  • opentelemetry-sdk: Het kernframework van OpenTelemetry (traces en het configuratie-instappunt).
  • opentelemetry-logs-sdk: Voegt ondersteuning toe voor het logging-signaal (dat gescheiden is van traces).
  • opentelemetry-exporter-otlp: Exporteert traces over het netwerk via OTLP.
  • opentelemetry-exporter-otlp-logs: Exporteert logs over het netwerk via OTLP.
  • opentelemetry-instrumentation-all: Bundelt de instrumentatie-gems, inclusief Rails, Rack en Active Record.
  • opentelemetry-instrumentation-logger: Koppelt aan de standaard Ruby Logger, zodat logberichten worden omgezet in OpenTelemetry log-records.

Nadat deze gems zijn geïnstalleerd, moet de exporter weten waar de gegevens naartoe verzonden moeten worden. Stel het eindpunt van je vendor en het authenticatietoken in als omgevingsvariabelen (de SDK detecteert automatisch standaard OTLP-exporter omgevingsvariabelen):

OTEL_EXPORTER_OTLP_ENDPOINT="https://your-vendor.com/otlp"
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <your-token>"

Ten slotte moeten we de SDK initialiseren zodat deze begint met het verzamelen en exporteren van gegevens. Maak hiervoor een initializer aan (config/initializers/opentelemetry.rb):

return if ENV["OTEL_EXPORTER_OTLP_ENDPOINT"].blank?

OpenTelemetry::SDK.configure do |c|
  c.service_name = "our-rails-app"
  c.use_all
end

Hier activeert c.use_all elke instrumentatie die is geladen, wat betekent dat zowel de gems in opentelemetry-instrumentation-all als opentelemetry-instrumentation-logger worden gebruikt.

Lokaal testen

Nu de SDK is geconfigureerd, kun je dit testen door OTELLOGSEXPORTER=console in te stellen. Dit print de log-records in je terminal in plaats van ze naar de vendor te sturen, wat de snelste manier is om te bevestigen dat je configuratie werkt voordat je deze implementeert.

Problemen die we hebben gevonden en opgelost in de Ruby SDK

Toen we logs naar Grafana Cloud exporteerden, ontdekten we twee problemen waarbij de Ruby SDK anders reageerde dan andere taal-SDK's en de OpenTelemetry-specificatie.

1. Exporter negeerde het basispad

Sommige vendor-backends vereisen dat OTLP-gegevens worden verzonden naar een eindpunt met een specifiek basispad, in ons geval /otlp voor Grafana Cloud. De exporter negeerde dit echter bij het toevoegen van het signaalpad.

  • Eindpunt: https://your-vendor.com/otlp
  • Verwacht: https://your-vendor.com/otlp/v1/logs
  • Werkelijkheid: https://your-vendor.com/v1/logs

We hebben dit gemeld in issue #2157 en opgelost in PR #2158, wat is uitgebracht in opentelemetry-exporter-otlp-logs v0.5.1.

2. Afhandeling van HTTP 204-responses

Grafana Cloud retourneert 204 No Content na het verwerken van logs, maar de exporter behandelde alleen 200 OK als een succesvolle verzending. De exports lukten feitelijk wel, maar onze applicatie logde elke verzending als een fout.

We hebben dit gemeld in issue #2043 en opgelost in PR #2044, wat is uitgebracht in opentelemetry-exporter-otlp-logs v0.4.0.

Volgende stappen: gestructureerde logging

Nu de logs succesvol worden geëxporteerd, is de volgende logische stap gestructureerde logging. Dat is te veel om hier te behandelen, maar de railssemanticlogger gem is een goed startpunt. Wellicht behandelen we de volledige OpenTelemetry-setup hiervoor in een toekomstig bericht.