Help us shape the future of Go! Take the 2026 Go Developer Survey

telemetry

package
v1.9.0 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package telemetry holds authsome's tracing and metric plumbing: the tracer every authsome span is started on, the helpers that mark a span failed without ever putting error text on it, the bounded values metric labels take, and the instruments recorded through a go-utils metrics sink.

Nothing here reads a request body, a credential or an identifier. Callers pass enums and counts; the helpers refuse to take anything else.

Index

Constants

View Source
const (
	ClassTimeout    = "timeout"
	ClassCanceled   = "canceled"
	ClassDNS        = "dns"
	ClassTLS        = "tls"
	ClassConnection = "connection"
	ClassOther      = "other"
)

Transport failure classes. These are the only values CallClass returns.

View Source
const (
	MetricSignIns              = "authsome_signin_attempts_total"
	MetricMFAVerifications     = "authsome_mfa_verifications_total"
	MetricSessionsRefreshed    = "authsome_sessions_refreshed_total"
	MetricPasswordHashDuration = "authsome_password_hash_duration_seconds"
	MetricAPIKeyAuths          = "authsome_apikey_authentications_total"  // #nosec G101 -- a metric name, not a credential
	MetricOAuth2Tokens         = "authsome_oauth2_tokens_requested_total" // #nosec G101 -- a metric name, not a credential
	MetricProviderDuration     = "authsome_provider_request_duration_seconds"
	MetricRetentionRows        = "authsome_retention_rows_deleted_total"
	MetricRetentionSweeps      = "authsome_retention_sweeps_attempted_total"
	MetricRetentionJobs        = "authsome_retention_jobs_processed_total"
	MetricControlsDegraded     = "authsome_controls_degraded_total"
	MetricRequestsThrottled    = "authsome_requests_throttled_total"
	MetricRateLimiterErrors    = "authsome_ratelimiter_errors_total"
	MetricAuditRecordsFailed   = "authsome_audit_records_failed_total"
	MetricEvents               = "authsome_events_total"
)

Metric names as authsome records them. Forge's Prometheus export puts its namespace in front, so authsome_signin_attempts_total scrapes as forge_authsome_signin_attempts_total.

View Source
const (
	AttrResult            = attribute.Key("authsome.result")
	AttrAuthMethod        = attribute.Key("authsome.auth.method")
	AttrMFARequired       = attribute.Key("authsome.mfa.required")
	AttrMFAVerified       = attribute.Key("authsome.mfa.verified")
	AttrSessionFormat     = attribute.Key("authsome.session.format")
	AttrRefreshVia        = attribute.Key("authsome.refresh.via")
	AttrPasswordAlgorithm = attribute.Key("authsome.password.algorithm")
	AttrPasswordOperation = attribute.Key("authsome.password.operation")
	AttrPluginName        = attribute.Key("authsome.plugin.name")
	AttrPluginHook        = attribute.Key("authsome.plugin.hook")
	AttrProviderKind      = attribute.Key("authsome.provider.kind")
	AttrMessageChannel    = attribute.Key("authsome.message.channel")
	AttrRetentionRemoved  = attribute.Key("authsome.retention.removed")
	AttrRetentionKind     = attribute.Key("authsome.retention.kind")
	AttrRetentionJobs     = attribute.Key("authsome.retention.jobs")
	AttrRetentionJobKind  = attribute.Key("authsome.retention.job_kind")
	AttrRetentionProvider = attribute.Key("authsome.retention.provider")
	AttrRetentionOutcome  = attribute.Key("authsome.retention.outcome")
	AttrJWKSSets          = attribute.Key("authsome.jwks.sets")
	AttrJWKSFailed        = attribute.Key("authsome.jwks.failed")

	// The OTel semantic convention keys the client spans use.
	AttrErrorType      = attribute.Key("error.type")
	AttrExceptionType  = attribute.Key("exception.type")
	AttrHTTPMethod     = attribute.Key("http.request.method")
	AttrHTTPStatusCode = attribute.Key("http.response.status_code")
	AttrServerAddress  = attribute.Key("server.address")
	AttrServerPort     = attribute.Key("server.port")
)

Span attribute keys. Every value is an enum, a count or a boolean. None of them carries an ID, an address, a credential or text a caller supplied.

View Source
const (
	MethodPassword          = "password"
	MethodPasskey           = "passkey"
	MethodSocial            = "social"
	MethodSSO               = "sso"
	MethodMagicLink         = "magic_link"
	MethodPhone             = "phone"
	MethodEmailVerification = "email_verification"
	MethodOther             = "other"
)

Values of the method label on authsome_signin_attempts_total and of authsome.auth.method on the issue session span. AuthMethod folds every raw method string into one of these.

View Source
const (
	SignInOK              = "ok"
	SignInInvalid         = "invalid"
	SignInLocked          = "locked"
	SignInBanned          = "banned"
	SignInUnverified      = "unverified"
	SignInPasswordExpired = "password_expired"
	SignInMFARequired     = "mfa_required"
	SignInRateLimited     = "rate_limited"
	SignInDenied          = "denied"
	SignInError           = "error"
)

Values of the result label on authsome_signin_attempts_total.

SignInInvalid covers a wrong password and an identifier nobody holds alike. Nothing authsome records tells those two apart.

View Source
const (
	MFAFlowSignIn = "signin"
	MFAFlowVerify = "verify"

	MFAMethodTOTP     = "totp"
	MFAMethodSMS      = "sms"
	MFAMethodRecovery = "recovery"

	MFAOK        = "ok"
	MFAInvalid   = "invalid"
	MFAReplayed  = "replayed"
	MFAExpired   = "expired"
	MFAExhausted = "exhausted"
	MFAError     = "error"
)

Values of the flow, method and result labels on authsome_mfa_verifications_total.

View Source
const (
	RefreshOK             = "ok"
	RefreshInvalid        = "invalid"
	RefreshExpired        = "expired"
	RefreshBanned         = "banned"
	RefreshReplayDetected = "replay_detected"
	RefreshDPoPRejected   = "dpop_rejected"
	RefreshError          = "error"
)

Values of the result label on authsome_sessions_refreshed_total and of authsome.result on the refresh session span.

View Source
const (
	RefreshViaRefreshToken = "refresh_token"
	RefreshViaClient       = "oauth2_client"
	RefreshViaSessionToken = "session_token"
	RefreshViaSessionID    = "session_id"
)

Values of authsome.refresh.via on the refresh session span: which credential the caller presented.

View Source
const (
	PasswordHash   = "hash"
	PasswordVerify = "verify"

	AlgorithmArgon2id = "argon2id"
	AlgorithmBcrypt   = "bcrypt"
)

Values of the operation and algorithm labels on authsome_password_hash_duration_seconds.

View Source
const (
	APIKeyOK        = "ok"
	APIKeyInvalid   = "invalid"
	APIKeyExpired   = "expired"
	APIKeyThrottled = "throttled"
	APIKeyDenied    = "denied"
	APIKeyError     = "error"
)

Values of the result label on authsome_apikey_authentications_total and of authsome.result on the authenticate api key span.

APIKeyInvalid covers a malformed key, an unknown prefix and a wrong secret alike, so the counter cannot be used to learn which prefixes exist.

View Source
const (
	GrantAuthorizationCode = "authorization_code"
	GrantRefreshToken      = "refresh_token"
	GrantClientCredentials = "client_credentials"
	GrantDeviceCode        = "device_code"
	GrantTokenExchange     = "token_exchange"
	GrantOther             = "other"

	// OAuth2Rejected is any other refusal: a bad, used or expired code, a
	// refresh token that failed, a malformed request, a bad DPoP proof.
	// OAuth2Pending is a device-code poll answered authorization_pending
	// or slow_down, which is the flow working as designed.
	OAuth2OK            = "ok"
	OAuth2InvalidClient = "invalid_client"
	OAuth2Rejected      = "rejected"
	OAuth2Pending       = "pending"
	OAuth2Error         = "error"
)

Values of the grant_type and result labels on authsome_oauth2_tokens_requested_total.

View Source
const (
	ProviderSocial       = "social"
	ProviderOIDC         = "oidc"
	ProviderSAMLMetadata = "saml_metadata"
	ProviderJWKS         = "jwks"
	ProviderSMS          = "sms"
	ProviderEmail        = "email"
	ProviderCaptcha      = "captcha"
	ProviderBreach       = "breach"
	ProviderCRM          = "crm"
	ProviderWebhook      = "webhook"
)

Values of the kind label on authsome_provider_request_duration_seconds and of authsome.provider.kind on the client spans.

View Source
const (
	ProviderOK       = "ok"
	Provider4xx      = "4xx"
	Provider5xx      = "5xx"
	ProviderTimeout  = "timeout"
	ProviderCanceled = "canceled"
	ProviderError    = "error"
)

Values of the result label on authsome_provider_request_duration_seconds.

View Source
const (
	RetentionSessions             = "sessions"
	RetentionVerifications        = "verifications"
	RetentionPasswordResets       = "password_resets"
	RetentionRevokedRefreshTokens = "revoked_refresh_tokens"
	RetentionCeremonyState        = "ceremony_state"
	RetentionPlugins              = "plugins"
)

Values of the kind label on authsome_retention_rows_deleted_total and of authsome.retention.kind on the sweep failed event.

View Source
const (
	SweepOK      = "ok"
	SweepError   = "error"
	SweepSkipped = "skipped"
)

Values of the result label on authsome_retention_sweeps_attempted_total.

View Source
const (
	CRMHubSpot = "hubspot"
	CRMGeneric = "generic"
	CRMOther   = "other"

	JobDone       = "done"
	JobRetry      = "retry"
	JobDead       = "dead"
	JobDeferred   = "deferred"
	JobSuppressed = "suppressed"
)

Values of the provider and outcome labels on authsome_retention_jobs_processed_total.

View Source
const (
	ControlRateLimit       = "ratelimit"
	ControlIdentifierLimit = "identifier_limit"
	ControlSettings        = "settings"
	ControlPasswordHistory = "password_history"
	ControlBreachCheck     = "breach_check"
)

Values of the control label on authsome_controls_degraded_total: a security control that could not run and let the request through.

View Source
const (
	LimiterRoute      = "route"
	LimiterIdentifier = "identifier"
)

Values of the limiter label on authsome_requests_throttled_total and authsome_ratelimiter_errors_total.

View Source
const ScopeName = "github.com/xraph/authsome"

ScopeName is the instrumentation scope on every span authsome starts.

Variables

This section is empty.

Functions

func AuthMethod

func AuthMethod(raw string) string

AuthMethod folds the raw auth method an IssueSessionRequest carries into the bounded set above. "social:github" and "sso:acme-okta" lose the part after the colon, which names a provider or an SSO connection, and a "+mfa" suffix is dropped because the second factor has its own counter. Anything unrecognised, including a custom plugin's own method name, is "other".

func Bounded

func Bounded(s string) string

Bounded folds a hook action, resource or outcome into a label value. The built-in values are dotted lower-case constants; anything that is not (too long, or carrying characters a constant never would) is "other", so a plugin that builds one from input cannot grow the series without bound.

func CallClass

func CallClass(ctx context.Context, err error) string

CallClass sorts a transport failure under ctx into timeout, canceled, dns, tls, connection or other. A deadline that has passed or a canceled ctx decides the class whatever err says, since http.Client's own timeout can surface as an error that is neither a context error nor a net.Error.

func Detach

func Detach(ctx context.Context) context.Context

Detach returns ctx without the span it carries, keeping its values and its cancellation.

A goroutine that outlives the call that started it must not keep that call's span: forge runs every extension's Start inside a "start <ext>" span, and grove opens a db.client span under any valid span context, even an ended one. Without this every query an idle loop makes would land in a boot trace that finished at startup.

func Fail

func Fail(span trace.Span, class string, err error)

Fail marks span failed with class as its error.type and status description, and adds an exception event carrying only the Go type of err's innermost error. err's text never reaches the span: store, driver and provider errors quote the values they were given.

func FailErr

func FailErr(span trace.Span, err error)

FailErr is Fail with the error's own type as its class.

func InstrumentClient

func InstrumentClient(c *http.Client, ep Endpoint, m *Metrics) *http.Client

InstrumentClient returns a copy of c whose transport is c's own wrapped in ProviderTransport. A nil c stands for http.DefaultClient. The copy keeps c's timeout, redirect policy and cookie jar.

func ProviderTransport

func ProviderTransport(base http.RoundTripper, ep Endpoint, m *Metrics) http.RoundTripper

ProviderTransport wraps base (http.DefaultTransport when nil) so every request it sends is measured, and traced under the span in the request's context.

The client span is named after the HTTP method alone and carries http.request.method, authsome.provider.kind, server.address and server.port (unless ep.HideHost), and http.response.status_code. It never carries the URL, its path or its query, or any header. A 4xx or 5xx answer, or a transport failure, marks it failed with error.type set to the status code or to timeout, canceled, dns, tls, connection or other. The error's text never reaches it. With no span in the request's context it records the duration and starts no span, so a call made outside any request does not mint a trace of its own.

The request goes out with exactly the headers its caller set: no traceparent, tracestate or baggage. Providers are third parties, and an inbound request's baggage is not theirs to see.

m may be nil, in which case only the span is recorded.

func Start

func Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span)

Start starts a span on the global tracer provider, which forge.New installs. The tracer is looked up on every call, so a provider installed after the engine was built still gets the spans.

func StartRoot

func StartRoot(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span)

StartRoot starts the root span of one unit of background work. Pass it a ctx that went through Detach, so nothing the unit does before or after the span lands in a trace that ended long ago.

func TypeOf

func TypeOf(err error) string

TypeOf names an error by class: a context error by its package name, anything else by the Go type of the innermost error in its chain. A joined error reports its own type.

Types

type Endpoint

type Endpoint struct {
	// Kind is the provider kind, one of the Provider* constants. It is
	// recorded as authsome.provider.kind and as the kind label.
	Kind string

	// HideHost keeps server.address and server.port off the span. Set it
	// when a customer supplies the URL (an SSO issuer, a SAML metadata URL,
	// a webhook or a generic CRM endpoint): the host is then theirs, not a
	// vendor's.
	HideHost bool
}

Endpoint describes a third-party call for ProviderTransport. Nothing in it comes from the request URL, which is why a span can't leak the password hash prefix in a breach-check path, an account SID in an SMS path, or a token in a query string.

type Metrics

type Metrics struct {
	// contains filtered or unexported fields
}

Metrics holds authsome's instruments. They are declared once, when the engine is built. A nil *Metrics records nothing, which is what an engine built without WithMetricsSink gets; every method is safe to call on it.

func From

func From(engine any) *Metrics

From returns the instruments of an engine handed over as a plugin.Engine, or nil when the engine records none (or is a test stub without the method). It is how authsome's own plugins reach the sink without the plugin.Engine interface growing a method every stub would need.

func NewMetrics

func NewMetrics(m gometrics.Metrics) *Metrics

NewMetrics declares authsome's instruments in m. A nil m returns nil.

func (*Metrics) APIKeyAuth

func (t *Metrics) APIKeyAuth(result string)

APIKeyAuth counts one API key authentication.

func (*Metrics) AuditRecordFailed

func (t *Metrics) AuditRecordFailed()

AuditRecordFailed counts an audit event the trail refused.

func (*Metrics) ControlDegraded

func (t *Metrics) ControlDegraded(control string)

ControlDegraded counts a security control that could not run and let the request through.

func (*Metrics) Event

func (t *Metrics) Event(action, resource, outcome string)

Event counts one event on the hook bus. The values go through Bounded.

func (*Metrics) MFAVerification

func (t *Metrics) MFAVerification(flow, method, result string)

MFAVerification counts one second-factor code check.

func (*Metrics) OAuth2Token

func (t *Metrics) OAuth2Token(grantType, result string)

OAuth2Token counts one request at the OAuth2 token endpoint.

func (*Metrics) PasswordHash

func (t *Metrics) PasswordHash(operation, algorithm string, took time.Duration)

PasswordHash records how long one password hash or verify took.

func (*Metrics) ProviderRequest

func (t *Metrics) ProviderRequest(kind, result string, took time.Duration)

ProviderRequest records how long one third-party call took.

func (*Metrics) RateLimiterError

func (t *Metrics) RateLimiterError(limiter string)

RateLimiterError counts a rate limiter that could not answer.

func (*Metrics) Refresh

func (t *Metrics) Refresh(result string)

Refresh counts one session refresh.

func (*Metrics) RetentionJob

func (t *Metrics) RetentionJob(provider, outcome string)

RetentionJob counts one CRM outbox job reaching an outcome.

func (*Metrics) RetentionRowsDeleted

func (t *Metrics) RetentionRowsDeleted(kind string, n int64)

RetentionRowsDeleted adds n to the rows the retention sweep removed.

func (*Metrics) RetentionSweep

func (t *Metrics) RetentionSweep(result string)

RetentionSweep counts one retention sweep.

func (*Metrics) SignIn

func (t *Metrics) SignIn(method, result string)

SignIn counts one sign-in attempt. method goes through AuthMethod.

func (*Metrics) StartSignIn

func (t *Metrics) StartSignIn(method string) *SignInAttempt

StartSignIn begins counting an attempt under method, one of the Method* constants.

func (*Metrics) Throttled

func (t *Metrics) Throttled(limiter string)

Throttled counts a request a rate limiter refused.

type SignInAttempt

type SignInAttempt struct {
	// contains filtered or unexported fields
}

SignInAttempt counts one sign-in through a plugin's verify or callback handler exactly once. Start it at the top of the handler and defer Done with the handler's error. Call HandOff right before IssueSession, which counts the attempt itself from then on; call Refuse where the handler answers a refusal without returning an error (a redirect carrying the error, say), so Done doesn't read the nil error as success.

A nil *SignInAttempt (from a nil *Metrics) does nothing.

func (*SignInAttempt) Done

func (a *SignInAttempt) Done(err error)

Done records the attempt unless it was handed off. Without a Refuse it reads the result off err: nil is ok, and an HTTP error's status sorts it into invalid (400, 401, 404), denied (403), rate_limited (429) or error.

func (*SignInAttempt) HandOff

func (a *SignInAttempt) HandOff()

HandOff leaves the count to IssueSession.

func (*SignInAttempt) Refuse

func (a *SignInAttempt) Refuse(result string)

Refuse sets the result Done records, whatever error it is given.

Directories

Path Synopsis
Package telemetrytest holds the helpers authsome's telemetry tests share: a real Forge metrics registry with its Prometheus scrape, and a span recorder installed as the global tracer provider.
Package telemetrytest holds the helpers authsome's telemetry tests share: a real Forge metrics registry with its Prometheus scrape, and a span recorder installed as the global tracer provider.

Jump to

Keyboard shortcuts

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