stats

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package stats defines the Observer interface for codec and adapter lifecycle events.

go-codex exposes two levels of observability:

Codec-level (ValidationObserver)

Use ValidationObserver when you call codecs directly without an adapter — for example, validating config files, parsing binary protocols, or any non-HTTP/MQTT use case. Implement just ValidationObserver.RecordValidationError and call ReportErrors after each codex.Codec.Decode:

type MyObserver struct{}
func (o *MyObserver) RecordValidationError(location, constraint, field string) {
    // increment Prometheus counter, emit log, etc.
}

val, err := appConfigCodec.Decode(rawData)
stats.ReportErrors(&MyObserver{}, "config", err)

Adapter-level (Observer)

Use the full Observer interface when wiring to an adapter. It embeds ValidationObserver and adds transport-specific hooks for HTTP and MQTT:

nethttp.Register(mux, route, handler, nethttp.Options{Observer: obs})

adaptermqtt.SubscribeHandler(ctx, ch, fn, adaptermqtt.SubscribeOptions{Observer: obs})
adaptermqtt.Publish(ctx, client, ch, qos, retained, msg, vars,
    adaptermqtt.PublishOptions{Observer: obs})

NoopObserver is a zero-cost default used when no observer is configured.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConstraintName

func ConstraintName(err error) string

ConstraintName extracts the constraint identifier from a field-level error:

func ReportErrors

func ReportErrors(obs ValidationObserver, location string, err error)

ReportErrors walks err and calls obs.RecordValidationError for every codec validation failure it finds. location identifies the data source (e.g. "body", "query", "payload", "config"). A no-op if err is nil or contains no recognisable validation errors.

Handled error types:

For all three, the walker recurses into the wrapped cause so nested errors (e.g. a KeyError whose cause is a ValidationErrors) are fully reported. Any other wrapped error is unwrapped and recursed into silently.

Example
package main

import (
	"fmt"

	"github.com/DaniDeer/go-codex/codex"
	"github.com/DaniDeer/go-codex/stats"
	"github.com/DaniDeer/go-codex/validate"
)

func main() {
	// ValidationObserver: implement to receive per-field validation events.
	type simpleObserver struct{ count int }
	obs := &simpleObserver{}

	recordFn := func(location, constraintName, field string) {
		obs.count++
	}

	// Wrap in a ValidationObserver for ReportErrors.
	type wrapObs struct{ fn func(string, string, string) }
	w := &wrapObs{fn: recordFn}

	// Example: decode a struct with two failing fields.
	type Config struct {
		Port  int
		Level string
	}
	portCodec := codex.Int().Refine(validate.RangeInt(1, 65535))
	levelCodec := codex.String().Refine(validate.OneOf("debug", "info", "warn", "error"))
	configCodec := codex.Struct[Config](
		codex.RequiredField("port", portCodec,
			func(c Config) int { return c.Port },
			func(c *Config, v int) { c.Port = v },
		),
		codex.OptionalField("level", levelCodec,
			func(c Config) string { return c.Level },
			func(c *Config, v string) { c.Level = v },
		),
	)

	_, err := configCodec.Decode(map[string]any{"port": float64(99999), "level": "verbose"})
	_ = w // suppress unused warning
	stats.ReportErrors(stats.NoopObserver{}, "config", err)

	// err is a structured codex.ValidationErrors — all fields collected at once.
	fmt.Println(err != nil)
}
Output:
true

Types

type NoopObserver

type NoopObserver struct{}

NoopObserver discards all events. It satisfies Observer, ValidationObserver, PipelineObserver, and SecurityObserver and is the zero-cost default used when no observer is configured.

func (NoopObserver) RecordApply

func (NoopObserver) RecordApply(_, _ string, _ bool, _ time.Duration)

func (NoopObserver) RecordPublish

func (NoopObserver) RecordPublish(_ string, _ bool, _ time.Duration)

func (NoopObserver) RecordRequest

func (NoopObserver) RecordRequest(_, _ string, _ int, _ time.Duration)

func (NoopObserver) RecordSecurityRejection

func (NoopObserver) RecordSecurityRejection(_, _ string)

func (NoopObserver) RecordSubscribe

func (NoopObserver) RecordSubscribe(_ string, _ bool, _ time.Duration)

func (NoopObserver) RecordValidationError

func (NoopObserver) RecordValidationError(_, _, _ string)

type Observer

type Observer interface {
	ValidationObserver

	// RecordRequest is called after every HTTP request completes.
	// method is uppercase (GET, POST, …), path is the route pattern (e.g.
	// "/users/{id}", not the concrete URL), statusCode is the HTTP status
	// written, duration is the total handler time including encode/decode.
	RecordRequest(method, path string, statusCode int, duration time.Duration)

	// RecordSubscribe is called after every MQTT message is fully processed
	// by a SubscribeHandler. topic is the concrete incoming topic (not a
	// template), success is false when decode or the application handler
	// failed, duration is total processing time.
	RecordSubscribe(topic string, success bool, duration time.Duration)

	// RecordPublish is called after every Publish call completes.
	// topic is the resolved publish topic (after BuildTopic if vars were
	// provided), success is false when encode failed, the broker returned an
	// error, or the context was cancelled. duration covers encode + broker
	// acknowledgement wait.
	// Note: RecordPublish is not called when BuildTopic itself fails (the
	// message never reached the encode/publish stage).
	RecordPublish(topic string, success bool, duration time.Duration)
}

Observer receives lifecycle events emitted by codec adapters. It embeds ValidationObserver for per-field validation errors and adds transport-specific hooks for HTTP and MQTT. All methods must be safe for concurrent use.

type PipelineObserver

type PipelineObserver interface {
	// RecordApply is called after every forge.Function*.Apply completes.
	// name is the function name, version is its version string, success is false
	// when any validation or computation step returned an error, duration is the
	// total Apply time (input validation + computation + output validation).
	RecordApply(name, version string, success bool, duration time.Duration)
}

PipelineObserver receives lifecycle events from forge.Function*.Apply calls. It is a separate interface from Observer because forge is a domain computation layer, not a transport adapter. Implement alongside Observer when you want telemetry for both transport and KPI computation in the same process.

type SecurityObserver

type SecurityObserver interface {
	// RecordSecurityRejection is called when a security check rejects a request
	// or message. location is the route path (HTTP) or topic (MQTT). scheme is
	// the first declared security scheme name for the operation.
	RecordSecurityRejection(location, scheme string)
}

SecurityObserver is an optional extension to Observer for security rejection events. Adapters type-assert the configured Observer to SecurityObserver before calling RecordSecurityRejection, so implementing this interface is purely additive — existing Observer implementations need not change.

type MyObserver struct{ ... }
func (o *MyObserver) RecordSecurityRejection(location, scheme string) {
    // increment a Prometheus counter, emit a log line, etc.
}

type ValidationObserver

type ValidationObserver interface {
	// RecordValidationError is called for each field that fails codec
	// validation. location identifies the data source (e.g. "body", "query",
	// "payload", "config"), constraintName is the constraint identifier (e.g.
	// "minLen(3)", "non-negative-int", "email", "type-mismatch", "required"),
	// field is the field or parameter name from the structured error.
	RecordValidationError(location, constraintName, field string)
}

ValidationObserver is the codec-level observability hook. Implement this interface when using codecs directly (without an adapter) to receive per-field validation error events. Use ReportErrors to extract codex.ValidationErrors from a decode error and call [RecordValidationError] for each failing field.

Observer embeds ValidationObserver for use with adapters.

Jump to

Keyboard shortcuts

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