omnilogslog

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 15 Imported by: 0

README

omnilog-slog

A log/slog handler that forwards records to omnilog as OTLP/HTTP protobuf, authenticated with your tenant's API key. Built on the official otelslog bridge and otlploghttp exporter — this package just wires the two together with omnilog's endpoint and auth convention, the same way omnilog's README shows for a plain OpenTelemetry JS setup.

Levels, attributes, and (if the logging call carries a context with an active span) trace/span correlation are handled by the underlying bridge — nothing bespoke to omnilog's wire format lives here.

Install

go get github.com/pjgrenyer/omnilog-slog

Usage

package main

import (
	"context"
	"log/slog"
	"os"

	omnilogslog "github.com/pjgrenyer/omnilog-slog"
)

func main() {
	ctx := context.Background()

	handler, shutdown, err := omnilogslog.NewHandler(
		ctx,
		os.Getenv("OMNILOG_URL"),     // https://<api-id>.execute-api.eu-west-2.amazonaws.com/prod
		os.Getenv("OMNILOG_API_KEY"),
		omnilogslog.WithServiceName("my-service"),
	)
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := shutdown(ctx); err != nil {
			slog.Error("omnilogslog: shutdown", "error", err)
		}
	}()

	logger := slog.New(handler)
	logger.Info("hello from go", "env", "prod")

	// Logging with a context that carries an active span attaches that
	// span's trace/span id to the record, so it correlates with the trace
	// in omnilog:
	// logger.InfoContext(ctx, "handled request", "route", "/health")
}

OMNILOG_URL is the base API Gateway invoke URL (no /v1/logs suffix — NewHandler appends it, tolerating a trailing slash), matching the convention used elsewhere in omnilog's docs and the demo feed script. It must be https://NewHandler rejects an http:// URL up front, since the API key is sent as a plain header. Pass omnilogslog.WithAllowInsecureHTTP() to point at a local collector during development.

Records are batched and exported asynchronously; call the shutdown func (typically via defer) before the process exits so the last batch flushes. Export is fire-and-forget: if every batch fails (a bad API key, the network being down) the application isn't notified beyond stderr noise, other than whatever error shutdown returns for the final flush. Pass omnilogslog.WithErrorHandler(func(error) { ... }) to observe earlier failures too — note this installs a process-wide OpenTelemetry error handler, so it also catches errors from any other OTel component in the same process.

By default every level, including slog.LevelDebug, is exported. Pass omnilogslog.WithMinLevel(slog.LevelInfo) (or any other threshold) to drop lower-severity records before they're batched.

Why a separate repo

The main omnilog repo is private. Go's toolchain resolves go get import paths against real VCS repos, so a package meant to be a one-line go get for any external service needs a public home — keeping it inside the private repo would mean every consumer configuring GOPRIVATE and git credentials just to add a log handler, which defeats the point. This repo holds only the handler; everything else about omnilog (ingest, storage, query, UI) stays in the private repo.

Development

go build ./...
go vet ./...
go test ./...

Documentation

Overview

Package omnilogslog provides a log/slog handler that forwards records to an omnilog tenant's ingest endpoint as OTLP/HTTP protobuf, authenticated with the tenant's API key.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewHandler

func NewHandler(ctx context.Context, baseURL, apiKey string, opts ...Option) (handler slog.Handler, shutdown func(context.Context) error, err error)

NewHandler builds an slog.Handler that batches records and forwards them to baseURL+"/v1/logs" as OTLP/HTTP protobuf, authenticated with apiKey via the x-api-key header. baseURL is the omnilog API Gateway invoke URL (without a trailing "/v1/logs"), e.g. the OMNILOG_URL convention used elsewhere in omnilog docs. A trailing slash on baseURL is tolerated.

baseURL must be an absolute https:// URL unless WithAllowInsecureHTTP is given. The exporter's HTTP client is also configured to never follow redirects, since Go's http.Client does not strip custom headers such as the API key when following a cross-origin redirect.

If the handler is called with a context carrying an active OpenTelemetry span, the resulting log record's TraceID/SpanID are populated automatically so the log correlates with that trace.

Export is asynchronous and best-effort: if omnilog rejects every batch (for example because of a bad API key), the application is not notified beyond stderr noise unless WithErrorHandler is supplied; shutdown's returned error only reflects the final flush.

The returned shutdown func flushes any batched records and must be called before the process exits, typically via defer.

Example
package main

import (
	"context"
	"log/slog"

	omnilogslog "github.com/pjgrenyer/omnilog-slog"
)

func main() {
	ctx := context.Background()

	handler, shutdown, err := omnilogslog.NewHandler(
		ctx,
		"https://api-id.execute-api.eu-west-2.amazonaws.com/prod",
		"omnilog-api-key",
		omnilogslog.WithServiceName("my-service"),
	)
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := shutdown(ctx); err != nil {
			panic(err)
		}
	}()

	logger := slog.New(handler)
	logger.Info("hello from go", "env", "prod")
}

Types

type Option

type Option func(*config)

Option configures NewHandler.

func WithAllowInsecureHTTP

func WithAllowInsecureHTTP() Option

WithAllowInsecureHTTP allows baseURL to use the http:// scheme. Without this option, NewHandler rejects a non-https baseURL, since the API key is sent as a plain header with no other protection against eavesdropping. This is intended for pointing at a local collector during development.

func WithErrorHandler

func WithErrorHandler(fn func(error)) Option

WithErrorHandler registers fn to be called whenever a batch of records fails to export, for example because omnilog rejects the API key or a network error occurs. Export happens asynchronously after the logging call returns, so without this option such failures are only visible as stderr noise (or via the last flush's error from shutdown).

fn is installed as the process-wide OpenTelemetry error handler via otel.SetErrorHandler, so it will also receive errors from any other OTel components configured in the same process.

func WithMinLevel

func WithMinLevel(level slog.Level) Option

WithMinLevel sets the minimum slog.Level exported to omnilog; records below this level are dropped before they reach the batching/export pipeline. Defaults to slog.LevelDebug, meaning every record is exported.

func WithResourceAttributes

func WithResourceAttributes(attrs map[string]string) Option

WithResourceAttributes sets additional OTel Resource attributes beyond service.name — e.g. env, availability_zone, region — attached to every log row sent through the handler, the same host/process-wide way a real OTel Collector's resource processor would, or the way Datadog's host tags work. omnilog surfaces these as its queryable "tags" (see the ?tag=k:v Read API filter), alongside whatever record-scoped attributes each slog call itself carries via its key/value pairs.

If attrs contains a "service.name" key, it overrides WithServiceName — matching the underlying OTel resource construction's own last-write-wins behavior on a duplicate key, rather than silently dropping one or the other.

func WithServiceName

func WithServiceName(name string) Option

WithServiceName sets the OTel resource's service.name attribute, which omnilog attaches to every log row sent through the handler. Defaults to "unknown-service" if not set.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL