or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

apidiff.mdconstraints.mdebnf.mderrors.mdevent.mdgorelease.mdindex.mdio-i2c.mdio-spi.mdjsonrpc2.mdmaps.mdmmap.mdmodgraphviz.mdrand.mdshiny.mdslices.mdslog.mdstats.mdsumdb.mdtrace.mdtxtar.mdtypeparams.mdutf8string.md
tile.json

event.mddocs/

golang.org/x/exp/event

golang.org/x/exp/event is an experimental event system that provides low-cost tracing, metrics, and structured logging. It offers a unified observability interface that doesn't tie libraries to specific APIs or applications to specific export formats. The package is designed for minimal overhead when no exporter is used, making it safe to leave instrumentation calls in libraries.

Note: This package is highly experimental and in a state of flux. It is public only to allow for collaboration on the design and implementation, and may be changed or removed at any time.

Package Information

  • Package Name: golang.org/x/exp/event
  • Package Type: Go (golang.org/x/exp - experimental)
  • Language: Go
  • Installation: go get golang.org/x/exp/event

Core Imports

import (
    "golang.org/x/exp/event"
    "golang.org/x/exp/event/adapter"
    "golang.org/x/exp/event/keys"
    "golang.org/x/exp/event/severity"
)

Basic Usage

package main

import (
    "context"
    "golang.org/x/exp/event"
)

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

    // Log an informational message
    event.Log(ctx, "application started")

    // Create a traced span
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()

    ctx = event.Start(ctx, "process_request")
    defer event.End(ctx)

    // Log with labels
    event.Log(ctx, "processing data",
        event.String("user_id", "12345"),
        event.Int64("duration_ms", 150),
    )
}

Core Concepts

Events

Events are the fundamental unit of observability in this package. They combine metadata (ID, parent ID, source, timestamp, kind) with user-supplied labels. The event system supports different kinds of events: log events, metric events, span start/end events, and annotations.

Labels

Labels are named values that carry information about events. They support multiple data types (bool, bytes, duration, float64, int64, uint64, string, interface{}) and are used to add context to events.

Exporters

Exporters handle the delivery of events to observability backends. They receive events synchronously from the event call site, so they should return quickly. Exporters can be set on the context or registered as a default exporter.

Metrics

The package provides several metric types for recording measurements:

  • Counter: Cumulative metric
  • IntDistribution: Distribution of int64 values
  • FloatGauge: Single floating-point value that may change
  • DurationDistribution: Distribution of time.Duration values

Core Event API

Main Functions

func Annotate(ctx context.Context, labels ...Label)

Annotate adds labels to the current context without creating a new event.

func End(ctx context.Context, labels ...Label)

End records the end of a span started with Start().

func Error(ctx context.Context, msg string, err error, labels ...Label)

Error logs an error message with the provided error and labels.

func Log(ctx context.Context, msg string, labels ...Label)

Log records a log event with the message and labels.

func Logf(ctx context.Context, msg string, args ...interface{})

Logf records a log event with a formatted message (printf-style).

func Start(ctx context.Context, name string, labels ...Label) context.Context

Start creates a new traced span and returns a context with the span attached. The caller must call End() on the returned context or parent context.

func WithExporter(ctx context.Context, e *Exporter) context.Context

WithExporter returns a context with the exporter attached. The exporter is called synchronously from the event call site, so it should return quickly.

func SetDefaultExporter(e *Exporter)

SetDefaultExporter sets an exporter that is used if no exporter can be found on the context.

func RegisterHelper(v interface{})

RegisterHelper records a function as being an event helper that should not be used when capturing the source information on events. v should be either a string or a function pointer. If v is a string it is of the form Space.Owner.Name where Owner and Name cannot contain '/' and Name also cannot contain '.'.

Event Type and Methods

type Event struct {
    ID     uint64           // Unique identifier for this event
    Parent uint64           // ID of the parent event for this event
    Source Source           // Source of event; if empty, set by exporter to import path
    At     time.Time        // Time at which the event is delivered to the exporter
    Kind   Kind             // Type of event (LogKind, MetricKind, StartKind, EndKind)
    Labels []Label          // User-supplied labels associated with the event
}

Event holds the information about an event that occurred. It combines the event metadata with the user supplied labels.

func New(ctx context.Context, kind Kind) *Event

New prepares a new event. This is intended to avoid allocations in the steady state case, to do this it uses a pool of events. Events are returned to the pool when Deliver is called. Failure to call Deliver will exhaust the pool and cause allocations. It returns nil if there is no active exporter for this kind of event.

func (ev *Event) Clone() *Event

Clone makes a deep copy of the Event. Deliver can be called on both Events independently.

func (ev *Event) Deliver() context.Context

Deliver the event to the exporter that was found in New. This also returns the event to the pool, it is an error to do anything with the event after it is delivered.

func (ev *Event) Find(name string) Label

Find searches for a label by name within the event.

func (ev *Event) Trace()

Trace records this event as a trace.

Exporter Type

type Exporter struct {
    // Has unexported fields.
}

Exporter synchronizes the delivery of events to handlers.

func NewExporter(handler Handler, opts *ExporterOptions) *Exporter

NewExporter creates an Exporter using the supplied handler and options. Event delivery is serialized to enable safe atomic handling.

type ExporterOptions struct {
    // If non-nil, sets zero Event.At on delivery.
    Now func() time.Time

    // Disable some event types, for better performance.
    DisableLogging     bool
    DisableTracing     bool
    DisableAnnotations bool
    DisableMetrics     bool

    // Enable automatically setting the event Namespace to the calling package's
    // import path.
    EnableNamespaces bool
}

ExporterOptions provides configuration for Exporter behavior.

Handler Interface

type Handler interface {
    // Event is called with each event.
    Event(context.Context, *Event) context.Context
}

Handler is the type for something that handles events as they occur.

Source Type

type Source struct {
    Space string  // Namespace for the source
    Owner string  // Owner/package name
    Name  string  // Function or event name
}

Source represents the origin of an event.

Kind Type

type Kind int

Kind represents the type of event.

func NewKind(name string) Kind

NewKind creates a new event kind with the given name.

func (k Kind) String() string

String returns the string representation of the Kind.

Event Kind Constants

const (
    LogKind
    MetricKind
    StartKind
    EndKind
)

Standard event kinds.

Labels API

Label Type

type Label struct {
    Name string
    // Has unexported fields.
}

Label is a named value.

Label Creation Functions

func Bool(name string, b bool) Label

Bool returns a new Label for a boolean value.

func Bytes(name string, data []byte) Label

Bytes returns a new Label for a byte slice.

func Duration(name string, d time.Duration) Label

Duration returns a new Label for a time.Duration value.

func Float64(name string, f float64) Label

Float64 returns a new Label for a floating point number.

func Int64(name string, u int64) Label

Int64 returns a new Label for a signed integer.

func String(name string, s string) Label

String returns a new Label for a string value.

func Uint64(name string, u uint64) Label

Uint64 returns a new Label for an unsigned integer.

func Value(name string, value interface{}) Label

Value returns a Label for the supplied interface{} value.

Label Methods

func (v Label) Bool() bool

Bool returns the bool from a value that was set with Bool. It will panic for any value for which IsBool is not true.

func (v Label) Bytes() []byte

Bytes returns the value as a bytes array.

func (v Label) Duration() time.Duration

Duration returns the time.Duration value.

func (l Label) Equal(l2 Label) bool

Equal reports whether two labels are equal.

func (v Label) Float64() float64

Float64 returns the float64 from a value that was set with Float64. It will panic for any value for which IsFloat64 is not true.

func (l Label) HasValue() bool

HasValue returns true if the value is set to any type.

func (v Label) Int64() int64

Int64 returns the int64 from a value that was set with Int64. It will panic for any value for which IsInt64 is not true.

func (v Label) Interface() interface{}

Interface returns the value. This will never panic, things that were not set using SetInterface will be unpacked and returned anyway.

func (v Label) IsBool() bool

IsBool returns true if the value was built with Bool.

func (v Label) IsBytes() bool

IsBytes returns true if the value was built with Bytes.

func (v Label) IsDuration() bool

IsDuration returns true if the value was built with Duration.

func (v Label) IsFloat64() bool

IsFloat64 returns true if the value was built with Float64.

func (v Label) IsInt64() bool

IsInt64 returns true if the value was built with Int64.

func (v Label) IsString() bool

IsString returns true if the value was built with String.

func (v Label) IsUint64() bool

IsUint64 returns true if the value was built with Uint64.

func (v Label) String() string

String returns the value as a string. This does not panic if v's Kind is not String, instead, it returns a string representation of the value in all cases.

func (v Label) Uint64() uint64

Uint64 returns the uint64 from a value that was set with Uint64. It will panic for any value for which IsUint64 is not true.

Metrics API

Metric Interface

type Metric interface {
    Name() string
    Options() MetricOptions
}

Metric represents a kind of recorded measurement.

MetricOptions Type

type MetricOptions struct {
    // A string that should be common for all metrics of an application or
    // service. Defaults to the import path of the package calling
    // the metric construction function (NewCounter, etc.).
    Namespace string

    // Optional description of the metric.
    Description string

    // Optional unit for the metric. Defaults to UnitDimensionless.
    Unit Unit
}

MetricOptions provides configuration for metrics.

Unit Type

type Unit string

Unit is a unit of measurement for a metric.

const (
    UnitDimensionless Unit = "1"
    UnitBytes         Unit = "By"
    UnitMilliseconds  Unit = "ms"
)

Standard metric units.

Counter Type

type Counter struct {
    // Has unexported fields.
}

A Counter is a metric that counts something cumulatively.

func NewCounter(name string, opts *MetricOptions) *Counter

NewCounter creates a counter with the given name.

func (c *Counter) Name() string

Name returns the name of the counter.

func (c *Counter) Options() MetricOptions

Options returns the MetricOptions for the counter.

func (c *Counter) Record(ctx context.Context, v int64, labels ...Label)

Record delivers a metric event with the given metric, value and labels to the exporter in the context.

IntDistribution Type

type IntDistribution struct {
    // Has unexported fields.
}

An IntDistribution records a distribution of int64s.

func NewIntDistribution(name string, opts *MetricOptions) *IntDistribution

NewIntDistribution creates a new IntDistribution with the given name.

func (d *IntDistribution) Name() string

Name returns the name of the distribution.

func (d *IntDistribution) Options() MetricOptions

Options returns the MetricOptions for the distribution.

func (d *IntDistribution) Record(ctx context.Context, v int64, labels ...Label)

Record converts its argument into a Value and records a metric event.

FloatGauge Type

type FloatGauge struct {
    // Has unexported fields.
}

A FloatGauge records a single floating-point value that may go up or down.

func NewFloatGauge(name string, opts *MetricOptions) *FloatGauge

NewFloatGauge creates a new FloatGauge with the given name.

func (g *FloatGauge) Name() string

Name returns the name of the gauge.

func (g *FloatGauge) Options() MetricOptions

Options returns the MetricOptions for the gauge.

func (g *FloatGauge) Record(ctx context.Context, v float64, labels ...Label)

Record converts its argument into a Value and records a metric event.

DurationDistribution Type

type DurationDistribution struct {
    // Has unexported fields.
}

A DurationDistribution records a distribution of durations.

func NewDuration(name string, opts *MetricOptions) *DurationDistribution

NewDuration creates a new Duration with the given name.

func (d *DurationDistribution) Name() string

Name returns the name of the distribution.

func (d *DurationDistribution) Options() MetricOptions

Options returns the MetricOptions for the distribution.

func (d *DurationDistribution) Record(ctx context.Context, v time.Duration, labels ...Label)

Record converts its argument into a Value and records a metric event.

Metric Constants

const (
    MetricKey      = interfaceKey("metric")
    MetricVal      = "metricValue"
    DurationMetric = interfaceKey("durationMetric")
)

Special metric-related constants.

Keys Subpackage

The keys subpackage provides strongly-typed key accessors for Labels. Each key type has a From() method to extract values and an Of() method to create labels.

Typed Key Types

type Bool string

Bool represents a key for boolean values.

func (k Bool) From(l event.Label) bool

From can be used to get a value from a Label.

func (k Bool) Of(v bool) event.Label

Of creates a new Label with this key and the supplied value.

type Error string

Error represents a key for error values.

func (k Error) From(l event.Label) error

From can be used to get a value from a Label.

func (k Error) Of(v error) event.Label

Of creates a new Label with this key and the supplied value.

type Float32 string

Float32 represents a key for 32-bit floating point values.

func (k Float32) From(l event.Label) float32

From can be used to get a value from a Label.

func (k Float32) Of(v float32) event.Label

Of creates a new Label with this key and the supplied value.

type Float64 string

Float64 represents a key for 64-bit floating point values.

func (k Float64) From(l event.Label) float64

From can be used to get a value from a Label.

func (k Float64) Of(v float64) event.Label

Of creates a new Label with this key and the supplied value.

type Int string

Int represents a key for int values.

func (k Int) From(l event.Label) int

From can be used to get a value from a Label.

func (k Int) Of(v int) event.Label

Of creates a new Label with this key and the supplied value.

type Int8 string

Int8 represents a key for 8-bit signed integer values.

func (k Int8) From(l event.Label) int8

From can be used to get a value from a Label.

func (k Int8) Of(v int8) event.Label

Of creates a new Label with this key and the supplied value.

type Int16 string

Int16 represents a key for 16-bit signed integer values.

func (k Int16) From(l event.Label) int16

From can be used to get a value from a Label.

func (k Int16) Of(v int16) event.Label

Of creates a new Label with this key and the supplied value.

type Int32 string

Int32 represents a key for 32-bit signed integer values.

func (k Int32) From(l event.Label) int32

From can be used to get a value from a Label.

func (k Int32) Of(v int32) event.Label

Of creates a new Label with this key and the supplied value.

type Int64 string

Int64 represents a key for 64-bit signed integer values.

func (k Int64) From(l event.Label) int64

From can be used to get a value from a Label.

func (k Int64) Of(v int64) event.Label

Of creates a new Label with this key and the supplied value.

type String string

String represents a key for string values.

func (k String) From(l event.Label) string

From can be used to get a value from a Label.

func (k String) Of(v string) event.Label

Of creates a new Label with this key and the supplied value.

type Tag string

Tag represents a key for tagging labels that have no value. These are used when the existence of the label is the entire information it carries, such as marking events to be of a specific kind, or from a specific package.

func (k Tag) New() event.Label

New creates a new Label with this key.

type UInt string

UInt represents a key for unsigned integer values.

func (k UInt) From(l event.Label) uint

From can be used to get a value from a Label.

func (k UInt) Of(v uint) event.Label

Of creates a new Label with this key and the supplied value.

type UInt8 string

UInt8 represents a key for 8-bit unsigned integer values.

func (k UInt8) From(l event.Label) uint8

From can be used to get a value from a Label.

func (k UInt8) Of(v uint8) event.Label

Of creates a new Label with this key and the supplied value.

type UInt16 string

UInt16 represents a key for 16-bit unsigned integer values.

func (k UInt16) From(l event.Label) uint16

From can be used to get a value from a Label.

func (k UInt16) Of(v uint16) event.Label

Of creates a new Label with this key and the supplied value.

type UInt32 string

UInt32 represents a key for 32-bit unsigned integer values.

func (k UInt32) From(l event.Label) uint32

From can be used to get a value from a Label.

func (k UInt32) Of(v uint32) event.Label

Of creates a new Label with this key and the supplied value.

type UInt64 string

UInt64 represents a key for 64-bit unsigned integer values.

func (k UInt64) From(l event.Label) uint64

From can be used to get a value from a Label.

func (k UInt64) Of(v uint64) event.Label

Of creates a new Label with this key and the supplied value.

type Value string

Value represents a key for untyped values.

func (k Value) From(l event.Label) interface{}

From can be used to get a value from a Label.

func (k Value) Of(v interface{}) event.Label

Of creates a new Label with this key and the supplied value.

Severity Subpackage

The severity subpackage provides severity level support for logging events, aligned with OpenTelemetry's severity levels.

Level Type

type Level uint64

Level represents a severity level of an event. The basic severity levels are designed to match the levels used in open telemetry. Smaller numerical values correspond to less severe events (such as debug events), larger numerical values correspond to more severe events (such as errors and critical events).

The following severity level ranges are defined:

  • 1-4: TRACE - A fine-grained debugging event. Typically disabled in default configurations.
  • 5-8: DEBUG - A debugging event.
  • 9-12: INFO - An informational event. Indicates that an event happened.
  • 13-16: WARN - A warning event. Not an error but is likely more important than an informational event.
  • 17-20: ERROR - An error event. Something went wrong.
  • 21-24: FATAL - A fatal error such as application or system crash.
const (
    Trace   = Level(1)
    Debug   = Level(5)
    Info    = Level(9)
    Warning = Level(13)
    Error   = Level(17)
    Fatal   = Level(21)
    Max     = Level(24)
)

Standard severity levels.

const Key = "level"

Key is the label key for severity levels.

Level Methods

func From(t event.Label) Level

From can be used to get a severity level value from a Label.

func (l Level) Class() Level

Class returns the class/range of this severity level.

func (l Level) Label() event.Label

Label creates a label for the severity level.

func (l Level) Log(ctx context.Context, msg string, labels ...event.Label)

Log records a log event with this severity level.

func (l Level) Logf(ctx context.Context, msg string, args ...interface{})

Logf records a log event with this severity level and a formatted message.

func (l Level) String() string

String returns the string representation of the severity level.

Adapters

The event package provides adapters for integration with popular logging frameworks:

Adapter: gokit

package gokit // import "golang.org/x/exp/event/adapter/gokit"

func NewLogger() log.Logger

NewLogger returns a go-kit logger backed by the event system.

Adapter: logfmt

package logfmt // import "golang.org/x/exp/event/adapter/logfmt"

const TimeFormat = "2006/01/02 15:04:05"

type Handler struct {
    Printer
    // Has unexported fields.
}

func NewHandler(to io.Writer) *Handler

NewHandler returns a handler that prints events to the supplied writer in logfmt format on a single line.

type Printer struct {
    QuoteValues       bool
    SuppressNamespace bool
    // Has unexported fields.
}

func (p *Printer) Event(w io.Writer, ev *event.Event)
func (p *Printer) Ident(w io.Writer, s string)
func (p *Printer) Label(w io.Writer, l event.Label)

Adapter: logr

package logr // import "golang.org/x/exp/event/adapter/logr"

func NewLogger(ctx context.Context, nameSep string) logr.Logger

NewLogger returns a logr.Logger implementation backed by the event system.

Adapter: logrus

package logrus // import "golang.org/x/exp/event/adapter/logrus"

func NewFormatter() logrus.Formatter

NewFormatter returns a logrus Formatter for events. Can be used to format the global logger:

logrus.SetFormatter(elogrus.NewFormatter())
logrus.SetOutput(io.Discard)

Or a Logger instance:

logger.SetFormatter(elogrus.NewFormatter())
logger.SetOutput(io.Discard)

Adapter: zap

package zap // import "golang.org/x/exp/event/adapter/zap"

func NewCore(ctx context.Context) zapcore.Core

NewCore returns a zapcore.Core implementation for events. To use globally:

zap.ReplaceGlobals(zap.New(NewCore(ctx)))

Adapter: stdlib

The stdlib adapter provides integration with the Go standard library logging (no additional functions exported).

Adapter: zerolog

The zerolog adapter provides integration with zerolog (no additional functions exported).

OpenTelemetry Integration

The otel subpackage provides handlers for OpenTelemetry integration:

package otel // import "golang.org/x/exp/event/otel"

type MetricHandler struct {
    // Has unexported fields.
}

func NewMetricHandler(m metric.Meter) *MetricHandler

MetricHandler is an event.Handler for OpenTelemetry metrics. Its Event method handles Metric events and ignores all others.

func (m *MetricHandler) Event(ctx context.Context, e *event.Event) context.Context
type TraceHandler struct {
    // Has unexported fields.
}

func NewTraceHandler(t trace.Tracer) *TraceHandler
func (t *TraceHandler) Event(ctx context.Context, ev *event.Event) context.Context

Testing Support

The eventtest subpackage supports testing event-based code:

package eventtest // import "golang.org/x/exp/event/eventtest"

func NewContext(ctx context.Context, tb testing.TB) context.Context

NewContext returns a context you should use for the active test. You must use this context or a derived one anywhere you want telemetry to be correctly routed back to the test it was constructed with.

type CaptureHandler struct {
    Got []event.Event
}

func NewCapture() (context.Context, *CaptureHandler)
func (h *CaptureHandler) Event(ctx context.Context, ev *event.Event) context.Context
func (h *CaptureHandler) Reset()

CaptureHandler captures events for testing.

func CmpOptions() []cmp.Option
func ExporterOptions() *event.ExporterOptions
func RunBenchmark(b *testing.B, ctx context.Context, hooks Hooks)
func TestAllocs(t *testing.T, f func(io.Writer) context.Context, hooks Hooks, expect int)
func TestBenchmark(t *testing.T, f func(io.Writer) context.Context, hooks Hooks, expect string)

Testing support functions and types.

Usage Examples

Basic Logging

package main

import (
    "context"
    "golang.org/x/exp/event"
)

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

    // Simple log
    event.Log(ctx, "server started")

    // Log with labels
    event.Log(ctx, "request received",
        event.String("method", "GET"),
        event.String("path", "/api/users"),
    )

    // Log with formatted message
    event.Logf(ctx, "processed %d items", 42)
}

Tracing Spans

func processRequest(ctx context.Context, id string) error {
    // Create a traced span
    ctx = event.Start(ctx, "processRequest")
    defer event.End(ctx)

    // Log within the span
    event.Log(ctx, "processing started",
        event.String("request_id", id),
    )

    // ... do work ...

    event.Log(ctx, "processing completed")
    return nil
}

Recording Metrics

import (
    "context"
    "golang.org/x/exp/event"
)

var (
    requestCount = event.NewCounter("http_requests_total", &event.MetricOptions{
        Description: "Total number of HTTP requests",
    })

    processingTime = event.NewDuration("http_request_duration_ms", &event.MetricOptions{
        Description: "HTTP request processing time",
        Unit:        event.UnitMilliseconds,
    })
)

func handleRequest(ctx context.Context) {
    start := time.Now()

    // Record counter
    requestCount.Record(ctx, 1, event.String("method", "GET"))

    // ... handle request ...

    // Record duration
    processingTime.Record(ctx, time.Since(start))
}

Using Typed Keys

import (
    "context"
    "golang.org/x/exp/event"
    "golang.org/x/exp/event/keys"
)

var (
    userIDKey keys.Int64 = "user_id"
    emailKey keys.String = "email"
)

func processUser(ctx context.Context, userID int64, email string) {
    event.Log(ctx, "processing user",
        userIDKey.Of(userID),
        emailKey.Of(email),
    )
}

Using Severity Levels

import (
    "context"
    "golang.org/x/exp/event"
    "golang.org/x/exp/event/severity"
)

func logWithSeverity(ctx context.Context) {
    severity.Info.Log(ctx, "normal operation")
    severity.Warning.Log(ctx, "potential issue detected")
    severity.Error.Logf(ctx, "failed to process item: %v", someError)
}

Setting Up an Exporter

import (
    "context"
    "log"
    "golang.org/x/exp/event"
    "golang.org/x/exp/event/adapter/logfmt"
)

func main() {
    // Create a simple handler that prints to stdout
    handler := logfmt.NewHandler(os.Stdout)

    // Create an exporter with the handler
    exporter := event.NewExporter(handler, &event.ExporterOptions{
        EnableNamespaces: true,
    })

    // Set as default exporter
    event.SetDefaultExporter(exporter)

    // Or use with context
    ctx := context.Background()
    ctx = event.WithExporter(ctx, exporter)

    event.Log(ctx, "hello world")
}