requestid

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 2026 License: Apache-2.0, MIT Imports: 5 Imported by: 0

README

RequestId

Go pkg to propagate a Request-Id header across multiple services.

  • Enables simple tracing capabilities and log grouping.
  • The value can be exposed to end-users in case of an error.
  • The value is a typeid — a Crockford base32-encoded UUIDv7 with a req_ prefix (e.g. req_01kqzn7yhbe37rw9n6aam5nb2j). It is k-sortable and you can extract the embedded timestamp.
  • Unlike OTEL, this package doesn't trace spans or metrics, doesn't require any backend and doesn't pull in large dependencies (gRPC).

Forked from https://github.com/go-chi/traceid.

Example: HTTP middleware

package main

import (
	"github.com/0xPolygon/requestid"
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/go-chi/httplog/v2"
)

func main() {
	r := chi.NewRouter()

	r.Use(requestid.Middleware)
	r.Use(httplog.RequestLogger(logger()))
	r.Use(middleware.Recoverer)

	r.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx := r.Context()

			// Log requestId to request logger.
			id := requestid.FromContext(r.Context())
			httplog.LogEntrySetField(ctx, "requestId", slog.StringValue(id)) // "req_01kqzn7yhbe37rw9n6aam5nb2j"

			next.ServeHTTP(w, r.WithContext(ctx))
		})
	})
}

See example/main.go.

Example: Send HTTP request with Request-Id header

import (
	"github.com/0xPolygon/requestid"
)

func main() {
	// Set Request-Id in context, if not already set from a parent ctx.
	ctx := requestid.NewContext(context.Background())

	// Make a request with the Request-Id header.
	req, _ := http.NewRequestWithContext(ctx, "GET", "http://localhost:3333/proxy", nil)
	requestid.SetHeader(ctx, req)

	resp, err := http.DefaultClient.Do(req)
	// ...
}

Example: Set Request-Id header on all outgoing HTTP requests globally

import (
	"github.com/0xPolygon/requestid"
	"github.com/go-chi/transport"
)

func main() {
	http.DefaultTransport = transport.Chain(
		http.DefaultTransport,
		transport.SetHeader("User-Agent", "my-app/v1.0.0"),
		requestid.Transport,
	)

	// This will automatically send the Request-Id header.
	req, _ := http.NewRequest("GET", "http://localhost:3333/proxy", nil)
	_, _ = http.DefaultClient.Do(req)
}

Get time from Request-Id value

$ go run github.com/0xPolygon/requestid/cmd/requestid req_01kqzn7yhbe37rw9n6aam5nb2j
2024-03-05 14:56:57.477 +0100 CET

You can also mint a new Request-Id:

$ go run github.com/0xPolygon/requestid/cmd/requestid
req_01kqzn7yhbe37rw9n6aam5nb2j

License

Copyright (c) 2024 https://github.com/go-chi authors Copyright (c) 2026 PT Services DMCC

Licensed under either:

as your option.

The SPDX license identifier for this project is MIT OR Apache-2.0.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Header = http.CanonicalHeaderKey("Request-Id")

Header is the HTTP header used to propagate the request ID across services.

View Source
var LogKey string = "requestId"

LogKey is the slog attribute key used to log the request ID.

View Source
var MaxClockDrift = 5 * time.Minute

MaxClockDrift bounds how far the timestamp embedded in an inbound Request-Id may deviate from the server clock. IDs outside this window are discarded and replaced with a freshly minted one.

Defaults to 5 minutes. Set to 0 to disable the drift check.

Functions

func FromContext

func FromContext(ctx context.Context) string

FromContext returns the request ID stored in ctx, or "" if none is set.

func LogHandler

func LogHandler(handler slog.Handler) slog.Handler

LogHandler wraps an slog.Handler so that any log record produced via the *Context variants automatically carries the request ID from ctx.

slog.Log()
slog.DebugContext()
slog.InfoContext()
slog.WarnContext()
slog.ErrorContext()

Example:

ctx := requestid.NewContext(context.Background())
handler := requestid.LogHandler(slog.NewJSONHandler(os.Stdout, nil))
logger := slog.New(handler)
logger.InfoContext(ctx, "message")

This would log

{"time":"2025-04-01T13:17:43.097789397Z","level":"INFO","msg":"message","requestId":"req_01kmfjypewe1wrfeb01wjfxand"}

func Middleware

func Middleware(next http.Handler) http.Handler

Middleware reads the request ID from the inbound Request-Id header, validates it as a typeid (req_…) whose embedded UUIDv7 timestamp is within MaxClockDrift of the server clock, and otherwise mints a fresh one. The (possibly new) ID is echoed on the response and stored in ctx.

func New

func New() string

New mints a fresh request ID in the canonical "req_…" form.

Panics only if the OS clock is unreadable, which is treated as unrecoverable.

func NewContext

func NewContext(ctx context.Context) context.Context

NewContext returns ctx with a request ID attached. If ctx already carries one, it is returned unchanged.

func SetHeader

func SetHeader(ctx context.Context, req *http.Request)

SetHeader writes the request ID from ctx onto req. If ctx has no request ID, a fresh one is minted for the header (but not stored back into ctx).

func Transport

func Transport(next http.RoundTripper) http.RoundTripper

Transport is an http.RoundTripper middleware that sets the Request-Id header on every outbound request, using the ID from ctx or a freshly minted one.

Types

type RequestID

type RequestID = typeid.UUID[reqPrefix]

RequestID is a typeid-encoded UUIDv7 with a fixed "req" prefix.

type RoundTripFunc

type RoundTripFunc func(r *http.Request) (*http.Response, error)

RoundTripFunc, similar to http.HandlerFunc, is an adapter to allow the use of ordinary functions as http.RoundTrippers.

func (RoundTripFunc) RoundTrip

func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error)

Directories

Path Synopsis
cmd
requestid command
Command requestid mints or inspects request IDs.
Command requestid mints or inspects request IDs.

Jump to

Keyboard shortcuts

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