StreetJS ships with a complete observability stack: structured logging, Prometheus metrics, OpenTelemetry tracing, health checks, and a route performance profiler. All components are zero-dependency and self-contained.
Components
Component
Export
Purpose
Logger
Logger, correlationMiddleware
Structured JSON logging with child loggers and GCP Cloud Run support
Prometheus text exposition with request counters, latency histograms, and DB pool gauges
OpenTelemetry
OtelTracer, otelMiddleware
OTel-compatible spans for request tracing
Health Checks
HealthCheckRegistry, registerHealthRoutes
Liveness and readiness probes with timeout and delay support
Route Profiler
RouteProfiler
Per-route latency sampling and percentile reporting
Logger
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import{Logger,correlationMiddleware}from'streetjs';constlogger=newLogger({service:'my-api',level:'info'});// Basic usagelogger.info('Server started',{port:3000});logger.error('Database connection failed',{error:newError('ECONNREFUSED')});// Child logger with bound fieldsconstreqLogger=logger.child({requestId:'...',userId:'...'});reqLogger.info('Processing request');// Add correlation IDs to every request automaticallyapp.use(correlationMiddleware(logger));// ctx.state['logger'] is now a child logger bound to the correlation ID// ctx.state['correlationId'] contains the X-Correlation-ID header value
Cloud Run Logging
When the K_SERVICE environment variable is set (automatically set by Cloud Run), the logger switches to GCP structured logging format:
GCP’s log explorer interprets severity for filtering and alerting. The severity values are: DEBUG, INFO, WARNING, ERROR.
Prometheus Metrics
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import{MetricsRegistry,prometheusMiddleware,metricsHandler,}from'streetjs';import{PgPool}from'streetjs';constpool=newPgPool({host:'localhost',database:'mydb',...});constmetrics=newMetricsRegistry();// Register default metrics + DB pool gaugeapp.use(prometheusMiddleware(metrics,pool));// Expose /metrics endpointapp.use(async (ctx,next)=>{if (ctx.path==='/metrics'){returnmetricsHandler(metrics)(ctx,next);}awaitnext();});
Default metrics registered automatically:
Metric
Type
Labels
http_requests_total
Counter
method, route, status
http_request_duration_seconds
Histogram
method, route, status
process_heap_bytes
Gauge
—
db_pool_connections
Gauge
state (idle/active/waiting)
Heap metric optimization:process_heap_bytes is sampled on a 5-second background interval rather than per-request, avoiding memoryUsage() overhead on high-traffic servers.
OTel spans are emitted as OTLP-compatible records. Connect a collector like Jaeger, Tempo, or OpenTelemetry Collector via the OTEL_EXPORTER_OTLP_ENDPOINT environment variable.
import{HealthCheckRegistry,registerHealthRoutes}from'streetjs';consthealth=newHealthCheckRegistry();// Liveness check — is the process alive?health.addCheck('heartbeat',async ()=>({status:'up'}),{type:'liveness'});// Readiness check — can the app serve traffic?health.addCheck('postgres',async ()=>{awaitpool.query('SELECT 1');return{status:'up'};},{type:'readiness',timeoutMs:3000},);registerHealthRoutes(app,health);// GET /health/live → { status: 'ok', checks: { heartbeat: { status: 'up', durationMs: 0 } } }// GET /health/ready → 503 during startup delay or if postgres is down
Startup Readiness Delay
Set STREET_READINESS_DELAY_MS to delay the readiness probe during startup (e.g. waiting for cache warm-up):
1
STREET_READINESS_DELAY_MS=10000 # 10 seconds
During the delay period, /health/ready returns 503 degraded with the remaining milliseconds.
Route Profiler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import{RouteProfiler}from'streetjs';constprofiler=newRouteProfiler();app.use(async (ctx,next)=>{conststart=process.hrtime.bigint();awaitnext();constdurationMs=Number(process.hrtime.bigint()-start)/1e6;profiler.record(ctx.method+''+ctx.path,durationMs);});// Get stats for any routeconststats=profiler.stats('GET /api/users');console.log(stats);// { p50: 3.2, p95: 12.1, p99: 45.0, count: 1024, mean: 4.1 }