instrumentation

package
v0.0.118 Latest Latest
Warning

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

Go to latest
Published: Apr 25, 2026 License: BSD-3-Clause Imports: 23 Imported by: 0

Documentation

Overview

Package instrumentation provides comprehensive OpenTelemetry instrumentation for the inboxfewer MCP server.

This package enables production-grade observability through:

  • OpenTelemetry metrics for HTTP requests, OAuth operations, and Google API calls
  • Distributed tracing for request flows and API calls
  • Prometheus metrics export via /metrics endpoint on dedicated port
  • OTLP export support for modern observability platforms

Metrics

The package exposes the following metric categories:

Server/HTTP Metrics:

  • http_requests_total: Counter of HTTP requests by method, path, and status
  • http_request_duration_seconds: Histogram of HTTP request durations
  • active_sessions: Gauge of active user sessions

Google API Metrics:

  • google_api_operations_total: Counter of Google API operations by service, operation, status
  • google_api_operation_duration_seconds: Histogram of Google API operation durations

OAuth Authentication Metrics:

  • oauth_auth_total: Counter of OAuth authentication events by result
  • oauth_token_refresh_total: Counter of token refresh attempts by result

MCP Tool Metrics:

  • mcp_tool_invocations_total: Counter of MCP tool invocations by tool name and status
  • mcp_tool_duration_seconds: Histogram of MCP tool execution durations

Tracing

Distributed tracing spans are created for:

  • HTTP request handling
  • MCP tool invocations (tool.<name>)
  • Google API calls (google.<service>.<operation>)
  • OAuth token operations

Configuration

Instrumentation can be configured via environment variables:

  • INSTRUMENTATION_ENABLED: Enable/disable instrumentation (default: true)
  • METRICS_EXPORTER: Metrics exporter type (prometheus, otlp, stdout, default: prometheus)
  • TRACING_EXPORTER: Tracing exporter type (otlp, stdout, none, default: none)
  • OTEL_EXPORTER_OTLP_ENDPOINT: OTLP endpoint for traces/metrics
  • OTEL_TRACES_SAMPLER_ARG: Sampling rate (0.0 to 1.0, default: 0.1)
  • OTEL_SERVICE_NAME: Service name (default: inboxfewer)

Example Usage

// Initialize instrumentation
provider, err := instrumentation.NewProvider(ctx, instrumentation.Config{
	ServiceName:    "inboxfewer",
	ServiceVersion: "0.1.0",
	Enabled:        true,
})
if err != nil {
	return err
}
defer provider.Shutdown(ctx)

// Get metrics recorder
recorder := provider.Metrics()

// Record an HTTP request
recorder.RecordHTTPRequest(ctx, "POST", "/mcp", 200, time.Since(start))

// Record a Google API operation
recorder.RecordGoogleAPIOperation(ctx, "gmail", "list", "success", time.Since(start))

// Record an MCP tool invocation
recorder.RecordToolInvocation(ctx, "gmail_list_emails", "success", time.Since(start))

Index

Constants

View Source
const (
	OperationList   = "list"
	OperationGet    = "get"
	OperationCreate = "create"
	OperationUpdate = "update"
	OperationDelete = "delete"
	OperationSend   = "send"
	OperationSearch = "search"
)

Common operation types for Google API metrics. Status, OAuth, and Service constants are defined in config.go.

View Source
const (
	// Status values
	StatusSuccess = "success"
	StatusError   = "error"
	StatusUnknown = "unknown"

	// OAuth result values
	OAuthResultSuccess = "success"
	OAuthResultFailure = "failure"
	OAuthResultExpired = "expired"

	// Google service names
	ServiceGmail    = "gmail"
	ServiceCalendar = "calendar"
	ServiceDrive    = "drive"
	ServiceDocs     = "docs"
	ServiceMeet     = "meet"
	ServiceTasks    = "tasks"

	// Exporter types
	ExporterPrometheus = "prometheus"
	ExporterOTLP       = "otlp"
	ExporterStdout     = "stdout"
	ExporterNone       = "none"

	// Metric recording intervals
	DefaultMetricInterval = 10 * time.Second
)

Constants for metric label values.

View Source
const (
	// SSOInjectionResultSuccess indicates SSO token was successfully injected
	SSOInjectionResultSuccess = "sso_success"
	// SSOInjectionResultStored indicates token was stored in token store (non-SSO path)
	SSOInjectionResultStored = "stored"
	// SSOInjectionResultNoUser indicates no authenticated user in context
	SSOInjectionResultNoUser = "no_user"
	// SSOInjectionResultNoToken indicates no access token header present
	SSOInjectionResultNoToken = "no_token"
	// SSOInjectionResultStoreFailed indicates token store operation failed
	SSOInjectionResultStoreFailed = "store_failed"
)

SSO token injection result constants

View Source
const (
	// SpanAttrTool is the MCP tool name attribute.
	SpanAttrTool = "mcp.tool"

	// SpanAttrService is the Google service name attribute.
	SpanAttrService = "google.service"

	// SpanAttrOperation is the operation type attribute.
	SpanAttrOperation = "google.operation"

	// SpanAttrAccount is the user account/email attribute.
	SpanAttrAccount = "mcp.account"

	// SpanAttrStatus is the operation status attribute.
	SpanAttrStatus = "mcp.status"

	// SpanAttrResourceID is the resource identifier (email ID, event ID, etc.).
	SpanAttrResourceID = "mcp.resource_id"

	// SpanAttrResourceType is the resource type (email, event, file, etc.).
	SpanAttrResourceType = "mcp.resource_type"

	// SpanAttrReadOnly indicates if the operation is read-only.
	SpanAttrReadOnly = "mcp.read_only"
)

Span attribute keys for operations.

View Source
const TracerName = "github.com/teemow/inboxfewer"

TracerName is the default tracer name for the inboxfewer package.

Variables

This section is empty.

Functions

func AddSpanEvent

func AddSpanEvent(span trace.Span, name string, attrs ...attribute.KeyValue)

AddSpanEvent adds an event to the span with optional attributes.

func ExtractUserDomain

func ExtractUserDomain(email string) string

ExtractUserDomain extracts the domain part from an email address. This reduces cardinality by using the domain instead of the full email. Returns "unknown" for invalid or empty emails to ensure metric labels always have a value.

Example:

ExtractUserDomain("jane@example.com")  // "example.com"
ExtractUserDomain("user@gmail.com")    // "gmail.com"
ExtractUserDomain("invalid")           // "unknown"
ExtractUserDomain("")                  // "unknown"

func GetSpanID

func GetSpanID(ctx context.Context) string

GetSpanID returns the span ID from the current span in context. Returns empty string if no valid span is present.

func GetTraceID

func GetTraceID(ctx context.Context) string

GetTraceID returns the trace ID from the current span in context. Returns empty string if no valid span is present.

func SetSpanError

func SetSpanError(span trace.Span, err error)

SetSpanError records an error on the span and sets the status to error.

func SetSpanSuccess

func SetSpanSuccess(span trace.Span)

SetSpanSuccess sets the span status to OK.

func SpanContextString

func SpanContextString(ctx context.Context) string

SpanContextString returns a human-readable trace context string. Format: "trace_id=X span_id=Y" or empty string if no valid context.

func StartGoogleAPISpan

func StartGoogleAPISpan(ctx context.Context, service, operation string, attrs ...attribute.KeyValue) (context.Context, trace.Span)

StartGoogleAPISpan starts a span for Google API operations. Includes service and operation attributes.

func StartSpan

func StartSpan(ctx context.Context, name string, attrs ...attribute.KeyValue) (context.Context, trace.Span)

StartSpan starts a new span with the given name and attributes. Returns the context with the span and the span itself. The caller is responsible for ending the span with defer span.End().

func StartToolSpan

func StartToolSpan(ctx context.Context, toolName string, attrs ...attribute.KeyValue) (context.Context, trace.Span)

StartToolSpan starts a span for an MCP tool invocation. Automatically adds tool name and sets appropriate span kind.

func TraceIDFromContext deprecated

func TraceIDFromContext(ctx context.Context) string

TraceIDFromContext extracts the trace ID from the current span in context. Returns empty string if no valid span is present.

Deprecated: Use GetTraceID instead. This function is kept for backwards compatibility and will be removed in v2.0.

Types

type AuditLogger

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

AuditLogger provides structured audit logging for tool invocations. It wraps slog.Logger with convenience methods for logging tool operations.

func NewAuditLogger

func NewAuditLogger(logger *slog.Logger) *AuditLogger

NewAuditLogger creates a new AuditLogger with the given slog.Logger. By default, PII is not included in logs (anonymized identifiers are used instead).

func NewAuditLoggerWithConfig

func NewAuditLoggerWithConfig(logger *slog.Logger, config AuditLoggingConfig) *AuditLogger

NewAuditLoggerWithConfig creates a new AuditLogger with the given configuration.

func (*AuditLogger) LogToolAudit

func (al *AuditLogger) LogToolAudit(ti *ToolInvocation)

LogToolAudit logs a tool invocation with full audit details. This includes PII (full email addresses) for compliance/audit purposes. SECURITY: Ensure audit logs are routed to secure storage with appropriate access controls.

Note: This method respects the enabled flag but always includes PII when called, regardless of the IncludePII configuration. Use LogToolInvocation for configuration-aware logging.

func (*AuditLogger) LogToolInvocation

func (al *AuditLogger) LogToolInvocation(ti *ToolInvocation)

LogToolInvocation logs a tool invocation using the standard log attributes. This is suitable for general operational logging with cardinality controls. If the logger is configured with IncludePII, full user emails are logged; otherwise, only domain-based anonymized identifiers are used.

func (*AuditLogger) SetEnabled

func (al *AuditLogger) SetEnabled(enabled bool)

SetEnabled sets whether audit logging is enabled.

func (*AuditLogger) SetIncludePII

func (al *AuditLogger) SetIncludePII(include bool)

SetIncludePII sets whether to include full email addresses in audit logs.

type AuditLoggingConfig

type AuditLoggingConfig struct {
	// Enabled determines if audit logging is active (default: true)
	// Audit logs contain full PII (user emails) and should be routed to secure storage.
	Enabled bool

	// IncludePII controls whether to include full email addresses in audit logs.
	// When false (default), only anonymized user identifiers are logged.
	// When true, full email addresses are included for compliance/audit purposes.
	// SECURITY: Ensure audit logs are stored securely with appropriate access controls.
	IncludePII bool

	// LogLevel sets the slog level for audit log messages (default: INFO).
	// Options: "debug", "info", "warn", "error"
	// Note: Audit events are always logged regardless of this level.
	LogLevel string
}

AuditLoggingConfig holds configuration for audit logging.

type Config

type Config struct {
	// ServiceName is the name of the service (default: inboxfewer)
	ServiceName string

	// ServiceVersion is the version of the service
	ServiceVersion string

	// ServiceInstanceID is the unique instance identifier (default: hostname)
	// In Kubernetes, this is typically the pod name
	ServiceInstanceID string

	// K8sNamespace is the Kubernetes namespace where the service is running
	K8sNamespace string

	// K8sPodName is the Kubernetes pod name
	K8sPodName string

	// Enabled determines if instrumentation is active (default: true)
	// Set to false via INSTRUMENTATION_ENABLED=false to disable metrics and tracing
	Enabled bool

	// MetricsExporter specifies the metrics exporter type
	// Options: "prometheus", "otlp", "stdout" (default: "prometheus")
	MetricsExporter string

	// TracingExporter specifies the tracing exporter type
	// Options: "otlp", "stdout", "none" (default: "none")
	TracingExporter string

	// OTLPEndpoint is the OTLP collector endpoint
	// Example: "localhost:4318" (without protocol prefix)
	OTLPEndpoint string

	// OTLPInsecure controls whether to use insecure HTTP for OTLP export
	// When false (default), uses TLS for secure transport
	// Set to true only for local development or testing with unencrypted endpoints
	// WARNING: Never use insecure transport in production - traces may contain
	// sensitive metadata and should be encrypted in transit
	OTLPInsecure bool

	// TraceSamplingRate is the sampling rate for traces (0.0 to 1.0, default: 0.1)
	TraceSamplingRate float64

	// PrometheusEndpoint is the path for the Prometheus metrics endpoint (default: "/metrics")
	PrometheusEndpoint string

	// DetailedLabels controls whether high-cardinality labels are included.
	// When false (default), only essential labels are included.
	// When true, additional labels like specific email addresses may be added.
	// For production, keep detailedLabels disabled to avoid cardinality explosion.
	DetailedLabels bool

	// AuditLogging configures audit logging behavior.
	AuditLogging AuditLoggingConfig
}

Config holds the configuration for OpenTelemetry instrumentation.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config with sensible defaults based on environment variables.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks if the configuration is valid.

type Metrics

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

Metrics provides methods for recording observability metrics.

func NewMetrics

func NewMetrics(meter metric.Meter, detailedLabels bool) (*Metrics, error)

NewMetrics creates a new Metrics instance with all metrics initialized. The detailedLabels parameter controls whether high-cardinality labels are included.

func (*Metrics) DecrementActiveSessions

func (m *Metrics) DecrementActiveSessions(ctx context.Context)

DecrementActiveSessions decrements the active sessions counter.

func (*Metrics) IncrementActiveSessions

func (m *Metrics) IncrementActiveSessions(ctx context.Context)

IncrementActiveSessions increments the active sessions counter.

func (*Metrics) RecordGoogleAPIOperation

func (m *Metrics) RecordGoogleAPIOperation(ctx context.Context, service, operation, status string, duration time.Duration)

RecordGoogleAPIOperation records a Google API operation with service, operation, status, and duration.

Parameters:

  • service: Google service name (gmail, calendar, drive, docs, meet, tasks)
  • operation: Operation type (list, get, create, update, delete, send, etc.)
  • status: Result status ("success" or "error")
  • duration: Time taken for the operation

func (*Metrics) RecordHTTPRequest

func (m *Metrics) RecordHTTPRequest(ctx context.Context, method, path string, statusCode int, duration time.Duration)

RecordHTTPRequest records an HTTP request with method, path, status code, and duration.

func (*Metrics) RecordOAuthAuth

func (m *Metrics) RecordOAuthAuth(ctx context.Context, result string)

RecordOAuthAuth records an OAuth authentication attempt with result. Result should be one of: "success", "failure"

func (*Metrics) RecordOAuthCrossClientToken added in v0.0.46

func (m *Metrics) RecordOAuthCrossClientToken(ctx context.Context, result, audience string)

RecordOAuthCrossClientToken records a cross-client OAuth token event for SSO monitoring. This tracks when tokens from trusted upstream clients (aggregators) are processed.

Parameters:

  • result: "accepted" when token from TrustedAudiences is validated, "rejected" when audience not trusted
  • audience: The token's audience (client ID) for debugging (optional, only included if detailedLabels enabled)

func (*Metrics) RecordOAuthTokenRefresh

func (m *Metrics) RecordOAuthTokenRefresh(ctx context.Context, result string)

RecordOAuthTokenRefresh records an OAuth token refresh attempt with result. Result should be one of: "success", "failure", "expired"

func (*Metrics) RecordSSOTokenInjection added in v0.0.51

func (m *Metrics) RecordSSOTokenInjection(ctx context.Context, result string)

RecordSSOTokenInjection records an SSO access token injection attempt. This tracks the different outcomes of the SSOAccessTokenMiddleware.

Parameters:

  • result: One of the SSOInjectionResult* constants indicating the outcome

func (*Metrics) RecordToolInvocation

func (m *Metrics) RecordToolInvocation(ctx context.Context, toolName, status string, duration time.Duration)

RecordToolInvocation records an MCP tool invocation with tool name, status, and duration.

Parameters:

  • toolName: Name of the MCP tool (e.g., "gmail_list_emails", "calendar_create_event")
  • status: Result status ("success" or "error")
  • duration: Time taken for the tool execution

func (*Metrics) RecordToolInvocationWithAccount

func (m *Metrics) RecordToolInvocationWithAccount(ctx context.Context, toolName, status, account string, duration time.Duration)

RecordToolInvocationWithAccount records an MCP tool invocation with account info. This is the detailed version that includes account information when detailedLabels is enabled.

Parameters:

  • toolName: Name of the MCP tool
  • status: Result status ("success" or "error")
  • account: User account/email (only included if detailedLabels is true)
  • duration: Time taken for the tool execution

type Provider

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

Provider encapsulates OpenTelemetry meter and tracer providers.

func NewProvider

func NewProvider(ctx context.Context, config Config) (*Provider, error)

NewProvider creates a new OpenTelemetry provider with the given configuration.

func (*Provider) Enabled

func (p *Provider) Enabled() bool

Enabled returns true if instrumentation is enabled.

func (*Provider) Metrics

func (p *Provider) Metrics() *Metrics

Metrics returns the metrics recorder for recording observability metrics.

func (*Provider) PrometheusHandler

func (p *Provider) PrometheusHandler() interface{}

PrometheusHandler returns an HTTP handler for the Prometheus metrics endpoint. Returns nil if Prometheus exporter is not configured.

func (*Provider) Shutdown

func (p *Provider) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the provider, flushing any pending telemetry.

func (*Provider) Tracer

func (p *Provider) Tracer(name string) trace.Tracer

Tracer returns a tracer for creating spans.

type SpanAttributeBuilder

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

SpanAttributeBuilder helps construct OpenTelemetry span attributes with consistent naming.

func NewSpanAttributeBuilder

func NewSpanAttributeBuilder() *SpanAttributeBuilder

NewSpanAttributeBuilder creates a new SpanAttributeBuilder.

func (*SpanAttributeBuilder) Build

Build returns the constructed attributes.

func (*SpanAttributeBuilder) WithAccount

func (b *SpanAttributeBuilder) WithAccount(account string) *SpanAttributeBuilder

WithAccount adds the user account attribute.

func (*SpanAttributeBuilder) WithOperation

func (b *SpanAttributeBuilder) WithOperation(operation string) *SpanAttributeBuilder

WithOperation adds the operation type attribute.

func (*SpanAttributeBuilder) WithReadOnly

func (b *SpanAttributeBuilder) WithReadOnly(readOnly bool) *SpanAttributeBuilder

WithReadOnly adds the read-only indicator attribute.

func (*SpanAttributeBuilder) WithResource

func (b *SpanAttributeBuilder) WithResource(resourceType, resourceID string) *SpanAttributeBuilder

WithResource adds resource attributes.

func (*SpanAttributeBuilder) WithService

func (b *SpanAttributeBuilder) WithService(service string) *SpanAttributeBuilder

WithService adds the Google service name attribute.

func (*SpanAttributeBuilder) WithTool

WithTool adds the MCP tool name attribute.

type ToolInvocation

type ToolInvocation struct {
	// Tool name
	Tool string

	// User identity (from OAuth)
	UserEmail string

	// Target information for Google services
	Account     string // Account name (default, work, personal)
	ServiceName string // Google service (gmail, calendar, drive, docs, meet, tasks)
	Operation   string // Operation type (list, get, create, update, delete, send)

	// Execution details
	StartTime time.Time
	Duration  time.Duration
	Success   bool
	Error     string

	// Tracing context
	TraceID string
	SpanID  string
}

ToolInvocation captures all information about a tool invocation for audit logging. This provides a comprehensive audit trail for all MCP tool calls.

Privacy Considerations

The UserEmail field contains PII. When logging, consider:

  • Using UserDomain() to get only the domain for metrics/general logs
  • Only logging full email in audit-specific log streams
  • Ensuring audit logs have appropriate access controls

func NewToolInvocation

func NewToolInvocation(tool string) *ToolInvocation

NewToolInvocation creates a new ToolInvocation with timing started. Call Complete() when the tool operation finishes.

func (*ToolInvocation) Complete

func (ti *ToolInvocation) Complete(success bool, err error) *ToolInvocation

Complete marks the invocation as completed and calculates duration. Returns the same ToolInvocation for method chaining.

func (*ToolInvocation) CompleteSuccess

func (ti *ToolInvocation) CompleteSuccess() *ToolInvocation

CompleteSuccess marks the invocation as successful.

func (*ToolInvocation) CompleteWithError

func (ti *ToolInvocation) CompleteWithError(err error) *ToolInvocation

CompleteWithError marks the invocation as failed with the given error.

func (*ToolInvocation) LogAttrs

func (ti *ToolInvocation) LogAttrs() []slog.Attr

LogAttrs returns slog attributes for structured logging. This provides a consistent set of fields for all tool invocation logs.

Cardinality

This function uses cardinality-controlled values (user_domain) for metrics-compatible logging. For full audit logging, use LogAuditAttrs.

func (*ToolInvocation) LogAuditAttrs

func (ti *ToolInvocation) LogAuditAttrs() []slog.Attr

LogAuditAttrs returns slog attributes for full audit logging. This includes the full user email for compliance/audit purposes.

Security Warning

This method includes PII (full email). Ensure audit logs are:

  • Stored securely with appropriate access controls
  • Not exposed to general monitoring dashboards
  • Retained according to compliance requirements

func (*ToolInvocation) Status

func (ti *ToolInvocation) Status() string

Status returns "success" or "error" based on the Success field.

func (*ToolInvocation) UserDomain

func (ti *ToolInvocation) UserDomain() string

UserDomain returns the domain portion of the user's email for lower-cardinality logging.

func (*ToolInvocation) WithAccount

func (ti *ToolInvocation) WithAccount(account string) *ToolInvocation

WithAccount sets the Google account name.

func (*ToolInvocation) WithService

func (ti *ToolInvocation) WithService(serviceName, operation string) *ToolInvocation

WithService sets the Google service and operation.

func (*ToolInvocation) WithSpanContext

func (ti *ToolInvocation) WithSpanContext(ctx context.Context) *ToolInvocation

WithSpanContext extracts trace context from the current span.

func (*ToolInvocation) WithUser

func (ti *ToolInvocation) WithUser(email string) *ToolInvocation

WithUser sets the user identity information.

Jump to

Keyboard shortcuts

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