Telemetry Configuration
Complete configuration reference for logging, metrics, and tracing across AppServer and SDKs.
Overview
The telemetry system provides three pillars of observability:
- Traces - Distributed request tracing with OpenTelemetry
- Metrics - Performance and business metrics exported to Prometheus
- Logs - Structured logging with trace correlation
All components export telemetry data to an OpenTelemetry Collector, which routes it to the appropriate backends (Tempo, Prometheus, Loki).
Environment Variables Reference
Common Variables (All Components)
| Variable | Default | Description |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | http://otel-collector:4318 | OTLP HTTP endpoint |
OTEL_SERVICE_NAME | varies | Service identifier for telemetry |
OTEL_SERVICE_VERSION | - | Service version string |
LOG_LEVEL | info | Log level: debug, info, warn, error |
AppServer (Go) Variables
| Variable | Default | Description |
|---|---|---|
APPSERVER_LOG_LEVEL | info | Log level (overrides LOG_LEVEL) |
APPSERVER_METRICS_ENABLED | true | Enable Prometheus metrics export |
APPSERVER_TRACING_ENABLED | true | Enable distributed tracing |
TELEMETRY_TRACES_ENABLED | true | Alternative trace enable flag |
TELEMETRY_METRICS_ENABLED | true | Alternative metrics enable flag |
TELEMETRY_LOGS_ENABLED | true | Enable OTLP log export |
OTEL_TRACES_SAMPLER_ARG | 1.0 | Sampling rate (0.0-1.0) |
APPSERVER_ENV | development | Environment name (used by config loader) |
APP_ENV | development | Environment name (used by telemetry default config) |
OTEL_ENVIRONMENT | - | Environment name (alternative, checked by config loader) |
SDK Variables
SDKs read configuration from environment variables with these defaults:
| Variable | Node.js Default | Description |
|---|---|---|
OTEL_ENABLED | false | Master enable switch |
OTEL_TRACES_ENABLED | false | Enable trace export |
OTEL_METRICS_ENABLED | false | Enable metrics export |
OTEL_LOGS_ENABLED | false | Enable log export |
AppServer (Go) Configuration
Configuration Struct
The Go telemetry package uses the Config struct:
type Config struct {
// ServiceName is the name of the service for telemetry
ServiceName string
// Enabled controls whether telemetry is active
Enabled bool
// OTLPEndpoint is the OTLP collector endpoint
OTLPEndpoint string
// TracesEnabled enables trace export
TracesEnabled bool
// MetricsEnabled enables metrics export
MetricsEnabled bool
// LogsEnabled enables log export (via OTLP logs)
LogsEnabled bool
// MetricsExportInterval is the interval for metrics export
MetricsExportInterval time.Duration
// LogLevel sets the minimum log level
LogLevel string
// Environment is the deployment environment
Environment string
// Version is the service version
Version string
// SamplingRate is the trace sampling rate (0.0 to 1.0)
SamplingRate float64
// ExtraAttributes are additional attributes for all telemetry
ExtraAttributes map[string]string
}
Initialization
import "github.com/easy-m/appserver/pkg/v2/telemetry"
func main() {
cfg := &telemetry.Config{
ServiceName: "my-service",
Enabled: true,
OTLPEndpoint: "http://otel-collector:4318",
TracesEnabled: true,
MetricsEnabled: true,
LogsEnabled: true,
LogLevel: "info",
SamplingRate: 1.0, // Sample all traces
}
if err := telemetry.Init(cfg); err != nil {
log.Fatal(err)
}
defer telemetry.Shutdown(context.Background())
// Application code...
}
Default Configuration
The DefaultConfig() function provides sensible defaults:
cfg := telemetry.DefaultConfig()
// Returns:
// - ServiceName: "unknown-service"
// - Enabled: true
// - OTLPEndpoint: from OTEL_EXPORTER_OTLP_ENDPOINT or "http://otel-collector:4318"
// - TracesEnabled: true
// - MetricsEnabled: true
// - LogsEnabled: true
// - MetricsExportInterval: 15s
// - LogLevel: from LOG_LEVEL or "info"
// - Environment: from APP_ENV or "development"
// - Version: "" (empty string)
// - SamplingRate: 1.0
// - ExtraAttributes: nil (no additional attributes)
Node.js SDK Configuration
Configuration Schema
The Node.js SDK uses Zod for configuration validation:
interface TelemetryConfig {
serviceName: string
enabled: boolean
otlpEndpoint?: string
logLevel?: 'debug' | 'info' | 'warn' | 'error'
metricsEnabled?: boolean
tracesEnabled?: boolean
logsEnabled?: boolean
metricsExportInterval?: number // milliseconds, default 15000
}
Initialization
import { initTelemetry, shutdownTelemetry } from '@appserver/telemetry'
await initTelemetry({
serviceName: 'my-node-app',
enabled: true,
otlpEndpoint: 'http://otel-collector:4318',
tracesEnabled: true,
metricsEnabled: true,
logsEnabled: true,
logLevel: 'info',
metricsExportInterval: 15000,
})
// On shutdown
await shutdownTelemetry()
Using the Logger
import { getLogger } from '@appserver/telemetry'
const logger = getLogger()
// Basic logging
logger.info('Server started')
logger.error('Connection failed')
// Structured logging with context
logger.info({ userId: '123', action: 'login' }, 'User logged in')
// Child loggers with bound context
const childLogger = logger.child({ requestId: 'abc-123' })
childLogger.info('Processing request') // Includes requestId in all logs
Using Metrics
import { getMetrics } from '@appserver/telemetry'
const metrics = getMetrics()
// Counter
const requestCounter = metrics.counter(
'http_requests_total',
'Total HTTP requests',
['method', 'path', 'status']
)
requestCounter.inc({ method: 'GET', path: '/api/users', status: '200' })
// Histogram
const latencyHistogram = metrics.histogram(
'http_request_duration_seconds',
'HTTP request latency',
['method', 'path'],
[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5]
)
latencyHistogram.observe({ method: 'GET', path: '/api/users' }, 0.045)
// Gauge
const activeConnections = metrics.gauge(
'active_connections',
'Current active connections',
['type']
)
activeConnections.set({ type: 'websocket' }, 42)
Browser Telemetry Configuration
Configuration Interface
interface BrowserTracingConfig {
/** Service name for telemetry identification */
serviceName: string
/** Service version (default: "unknown") */
version?: string
/** OTLP collector endpoint */
otlpEndpoint: string
/** Enable or disable browser tracing (default: true) */
enabled?: boolean
/** URLs to propagate trace context to (default: all URLs via /.*/) */
propagateTraceHeaderCorsUrls?: (string | RegExp)[]
}
Note: By default, propagateTraceHeaderCorsUrls is set to /.*/ which propagates trace context headers to all URLs. You may want to restrict this in production to only your own API endpoints.
Initialization
import { initBrowserTracing, shutdownBrowserTracing } from '@appserver/telemetry/browser'
// Initialize tracing
initBrowserTracing({
serviceName: 'my-frontend-app',
version: '1.0.0',
otlpEndpoint: 'http://localhost:4318',
enabled: true,
// Propagate trace headers to these URLs
propagateTraceHeaderCorsUrls: [
/http:\/\/localhost:.*/,
/https:\/\/api\.example\.com\/.*/,
],
})
// On app unmount
await shutdownBrowserTracing()
Features
Browser telemetry automatically instruments:
- Fetch API - All fetch requests create spans
- XMLHttpRequest - XHR requests create spans
- Trace Context Propagation - W3C Trace Context headers are added to requests
Getting Trace Information
import { getBrowserTraceId, getBrowserSpanId, isBrowserTracingInitialized } from '@appserver/telemetry/browser'
if (isBrowserTracingInitialized()) {
const traceId = getBrowserTraceId()
const spanId = getBrowserSpanId()
console.log(`Current trace: ${traceId}, span: ${spanId}`)
}
Sampling Configuration
Sampling Rate
The sampling rate controls what percentage of traces are recorded:
| Rate | Description | Use Case |
|---|---|---|
1.0 | Sample all traces | Development, low-traffic |
0.1 | Sample 10% of traces | Medium traffic production |
0.01 | Sample 1% of traces | High traffic production |
Parent-Based Sampling
When a request arrives with an existing trace context, the sampling decision is inherited from the parent span. This ensures complete traces are captured rather than partial ones.
Configuration Examples
Development - Sample everything:
OTEL_TRACES_SAMPLER_ARG=1.0
Production - Sample 10%:
OTEL_TRACES_SAMPLER_ARG=0.1
Service Naming Conventions
Use consistent service names across your deployment:
| Service | Recommended Name |
|---|---|
| AppServer | appserver |
| Orchestrator | orchestrator |
| Node.js App | app-{appname} |
| Browser App | shell or app-{appname}-frontend |
Service names appear in:
- Trace spans (
service.nameattribute) - Metrics labels
- Log fields
Log Levels
| Level | Description | When to Use |
|---|---|---|
debug | Detailed debugging information | Development only |
info | General operational information | Default for production |
warn | Warning conditions | Issues that don't stop operation |
error | Error conditions | Failures requiring attention |
Log Level by Environment
Development:
LOG_LEVEL=debug
Production:
LOG_LEVEL=info
Metrics Export
Export Interval
Metrics are exported periodically to the OTLP collector:
- Default: 15 seconds
- Recommended range: 10-60 seconds
- Trade-off: Shorter intervals = more resolution but higher overhead
Configuration
Go:
cfg.MetricsExportInterval = 15 * time.Second
Node.js:
{
metricsExportInterval: 15000 // milliseconds
}
Development vs Production Configuration
Development Configuration
# Enable all telemetry for debugging
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_SERVICE_NAME=appserver-dev
LOG_LEVEL=debug
OTEL_TRACES_SAMPLER_ARG=1.0
APPSERVER_METRICS_ENABLED=true
APPSERVER_TRACING_ENABLED=true
Production Configuration
# Optimized for production workloads
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
OTEL_SERVICE_NAME=appserver
LOG_LEVEL=info
OTEL_TRACES_SAMPLER_ARG=0.1
APPSERVER_METRICS_ENABLED=true
APPSERVER_TRACING_ENABLED=true
APPSERVER_ENV=production
Docker Compose Environment
When running with Docker Compose, telemetry is pre-configured:
services:
appserver:
environment:
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4318
OTEL_SERVICE_NAME: appserver
APPSERVER_LOG_LEVEL: debug
The OTEL Collector routes telemetry to:
- Traces → Tempo
- Metrics → Prometheus
- Logs → Loki