Skip to main content

Observability matrix

Operating a Titan app means knowing what to grep for and what to alert on. This page lists — per module — the events emitted, notable log keys, and metric/health signals you can rely on.

All claims here are verified against module source. If a row is empty, the module is deliberately quiet at that layer.

At-a-glance

Official@omnitron-dev/titan-*

Maintained by the Omnitron team. Independent npm package.

The Metrics surface column distinguishes metrics actively pushed into titan-metrics from stats you read on demand (pull — a getStats() / getMetrics() / stats() accessor).

ModuleEmits eventsNotable logsMetrics surfaceHealth indicator
titan-authJWT verify failures
titan-cacheL2 fallback / errorshit/miss/evict (pull: getStats())
titan-databaseslow query, retriesyes (via titan-health)
titan-discoverydiscovery:eventregister, heartbeat, dereg
titan-eventsproxydispatch errors
titan-healthindicator failuresn/a (itself)
titan-lockfailure-tracker windows
titan-metricsquiet by designprocess/system collectors
titan-notificationsnotifications.* (via titan-events)send + DLQyes
titan-pmmany (below)spawn / crash / restartprocess statsyes (via titan-health)
titan-ratelimitreject reasonsallow/deny (pull: getStats())
titan-redisreconnect / errorsyes (via titan-health)
titan-schedulerjob start / finishruns/failures (pull: getMetrics())
titan-telemetry-relayWAL eventspull: stats()
Built-in@omnitron-dev/titan

Ships inside @omnitron-dev/titan. No additional install required.

ModuleChange notificationsNotable logsPushes metrics
configonChange() callbackreload success/fail
logger(the logger itself)

Per-module reference

titan-pm — the most chatty module

Official@omnitron-dev/titan-pm

Maintained by the Omnitron team. Independent npm package.

Process manager is the loudest module by design — every supervised process / pool / worker raises events you can subscribe to.

EventArgsMeaning
process:spawnprocessInfoChild process started
process:readyprocessInfoChild responded to ready handshake
process:stopprocessInfoChild exited cleanly
process:crashprocessInfo, errorChild died unexpectedly
child:startednameSupervised child entry started
child:stoppednameSupervised child stopped
child:start-failedname, errorInitial start raised before ready
child:crashname, errorSupervised child crashed mid-run
child:restartname, countChild restarted; count = total restarts
pool:initialized{ size, class }Worker pool warmed up
pool:scaled{ from, to, class }Pool grew / shrank
pool:drained{ class }Pool finished draining queued work
pool:destroyed{ class }Pool released
pool:memory{ workerId, rssMB }Memory limit exceeded
worker:spawned{ workerId, class }Pool worker forked
worker:shutdown{ workerId, class }Pool worker reaped
worker:unhealthy{ workerId }Worker missed a ping
worker:unresponsive{ workerId }Worker still silent after retries
worker:replaced{ workerId }Unresponsive worker replaced
request:queued{ poolClass, queueSize }Pool saturated; work queued
circuitbreaker:openBreaker tripped
circuitbreaker:halfopenProbe window started
circuitbreaker:closeBreaker reset
escalatename, errorRestart limit exceeded; failure escalated upstream
shutdownSupervisor shutdown
exitinfoProcess exited
health:changeprocessId, IHealthStatusPer-process health flipped
health:criticalprocessId, IHealthStatusPer-process health became critical

Subscribe with the standard event emitter API:

pmService.on('process:crash', (info, error) => {
logger.error({ pid: info.id, error }, 'child crashed');
});

titan-notifications

Official@omnitron-dev/titan-notifications

Maintained by the Omnitron team. Independent npm package.

Lifecycle events are emitted through titan-events under the NOTIFICATIONS_EVENTS topic names (only when an EventsService is available). Selected topics:

Topic constantEvent nameMeaning
NOTIFICATION_SENTnotifications.notification.sentSend completed
NOTIFICATION_FAILEDnotifications.notification.failedSend failed
CHANNEL_DELIVERY_SUCCESSnotifications.channel.delivery.successPer-channel delivery ok
CHANNEL_DELIVERY_FAILEDnotifications.channel.delivery.failedPer-channel delivery failed
DLQ_MESSAGE_ADDEDnotifications.dlq.message.addedMessage routed to DLQ

Dead-lettered messages are also inspectable on the service via getDLQStats() / getDLQMessages(options?) and requeued with requeueFromDLQ(count?).

Health: the module registers NotificationsHealthIndicator (NOTIFICATIONS_HEALTH) — automatically picked up by titan-health if both modules are loaded.

titan-discovery

Official@omnitron-dev/titan-discovery

Maintained by the Omnitron team. Independent npm package.

EventPayloadMeaning
discovery:event{ type, node, service?, ts }Node added / removed / updated

type is one of node.added, node.removed, node.updated.

Notable logs (all under the service's own logger namespace):

LevelPattern
infoNode 'X' registered, DiscoveryService started
infoInitiating graceful shutdown for node 'X'
warnHeartbeat attempt failed
errorAll N heartbeat attempts failed
errorFailed to publish discovery event
debugReceived discovery event, Cleaned up inactive nodes

titan-events

Official@omnitron-dev/titan-events

Maintained by the Omnitron team. Independent npm package.

titan-events is the event bus; it doesn't emit framework-level events of its own. Per-emitter introspection is available via EventHistoryService if enableHistory: true.

titan-scheduler

Official@omnitron-dev/titan-scheduler

Maintained by the Omnitron team. Independent npm package.

Doesn't expose a hot event stream — IJobListener is the extension point. Implement one to observe job execution:

class MyJobListener implements IJobListener {
onJobStart?(job, context) { /* ... */ }
onJobComplete?(job, result) { /* ... */ }
onJobError?(job, error, context) { /* ... */ }
onJobRetry?(job, attempt, error) { /* ... */ }
onJobCancelled?(job, reason?) { /* ... */ }
}

All hooks are optional. Register listeners via the listeners module option or the SCHEDULER_LISTENERS_TOKEN.

titan-cache

Official@omnitron-dev/titan-cache

Maintained by the Omnitron team. Independent npm package.

Cache hit/miss/eviction metrics are exposed through the @Cached decorator metadata and the CacheService.getStats() API.

const stats = cache.getStats();
// { hits, misses, evictions, size, hitRate, ... }

No native event stream — wrap calls if you need per-key observability.

titan-lock

Official@omnitron-dev/titan-lock

Maintained by the Omnitron team. Independent npm package.

Uses the framework's FailureTracker primitive: instead of logging every failure, it collapses repeated failures of the same operation into windowed "X failing" warnings. This keeps logs quiet when Redis briefly hiccups.

LevelPattern
warn[DistributedLock] X started failing
debug[DistributedLock] X still failing
info[DistributedLock] X recovered

titan-database

Official@omnitron-dev/titan-database

Maintained by the Omnitron team. Independent npm package.

LevelPattern
warnSlow query (over the configured threshold)
warnTransient error retried (via withRetry)
errorPool exhaustion / connection lost
infoMigration applied

The module exposes DatabaseHealthIndicator for k8s probes.

titan-redis

Official@omnitron-dev/titan-redis

Maintained by the Omnitron team. Independent npm package.

Inherits ioredis's event model (connect, ready, error, close, reconnecting, end). The manager logs reconnect attempts and surfaces a RedisHealthIndicator via titan-health.

titan-auth

Official@omnitron-dev/titan-auth

Maintained by the Omnitron team. Independent npm package.

Quiet by design — token-verification details should not be logged at info level (PII/tokens). Failures log at warn with error.code populated; successful verifications are silent.

titan-ratelimit

Official@omnitron-dev/titan-ratelimit

Maintained by the Omnitron team. Independent npm package.

Statistics are pull-based:

const stats = rate.getStats();
// { totalChecks, totalAllowed, totalDenied, activeKeys, byTier? }

Denied requests are not logged automatically (volume would be high) — count them via metrics.recordTyped('counter', ...) from the caller if you need to.

titan-metrics

Official@omnitron-dev/titan-metrics

Maintained by the Omnitron team. Independent npm package.

The MetricsService itself is quiet — it neither logs flush activity at info level nor records meta-metrics about its own flushing. (The MetricsCollector keeps an internal totalDropped counter for buffer- pressure diagnostics, surfaced by the omnitron metrics-bridge rather than as a self-instrumented metric.)

The process collector (collection: { process: true }) records, for the daemon's own process: heap_used_bytes, heap_total_bytes, rss_bytes, external_bytes, cpu_percent, uptime_seconds. The child/orchestrator collector (system: true) additionally records memory_bytes, rpc_requests_total, rpc_errors_total, and app_status per supervised app. Event-loop lag is not collected by the built-in collector.

titan-telemetry-relay

Official@omnitron-dev/titan-telemetry-relay

Maintained by the Omnitron team. Independent npm package.

Exposes a pull-based stats() snapshot — { totalEmitted, totalSent, totalFailed, totalReceived, transportConnected, buffer, wal } — where buffer ({ size, totalPushed, totalDropped, totalFlushed }) and wal ({ segments, totalSize, totalWritten, currentSegment }) carry the queue-depth and throughput signals. Surface these through your metrics module at the leader node; the relay does not push them itself.

titan-health

Official@omnitron-dev/titan-health

Maintained by the Omnitron team. Independent npm package.

LevelPattern
errorIndicator threw during check
warnIndicator returned degraded
debugProbe cache hit / refresh

Health results carry per-check timing in their own result objects; the module does not push check latency into titan-metrics.

Built-in modules

config

Built-in@omnitron-dev/titan/module/config

Ships inside @omnitron-dev/titan. No additional install required.

Change notifications are delivered through a callback subscription, not a named event-bus topic:

const unsubscribe = config.onChange((event: IConfigChangeEvent) => {
// event: { path, oldValue, newValue, source, timestamp }
});

Notable logs:

  • infoConfiguration reloaded due to file change (after a watched source changes)
  • errorFailed to reload configuration

logger

Built-in@omnitron-dev/titan/module/logger

Ships inside @omnitron-dev/titan. No additional install required.

The logger doesn't log about itself except at fatal failures (e.g., transport open failed at boot). Use createNullLogger() in tests when you want absolute silence.

Cross-cutting recommendations

Subscribe instead of poll

The pm/discovery event streams are hot — subscribing is cheaper and more responsive than polling state. For per-process autoscaling or alerting, hook the events:

pm.on('pool:memory', handleMemorySpike);
pm.on('worker:unresponsive', handleWorkerHang);
pm.on('circuitbreaker:open', notifyOncall);

Tie metrics to events

For dashboards, route key events through titan-metrics:

pm.on('process:crash', (info, error) => {
metrics.recordTyped('counter', 'pm.process.crash.total',
{ class: info.processName }, 1);
});

Alert thresholds — common starting points

SignalSuggested alert
rss_bytes growth rate> +50 MB/min sustained
cpu_percentsustained near 100 for 2 min
pool:scaled to/from limitrepeated within 5 min
worker:replaced count> 3 in 5 min for same pool
circuitbreaker:openany open lasting > 30 s
app_status flips to 0any supervised app offline
Health degraded/unhealthysustained > 1 probe window

Don't log per-call by default

Several modules (titan-ratelimit, titan-cache, titan-auth) intentionally stay quiet at info level. Per-call logging at request rate is a recipe for disk/SIEM saturation. Use counters and sampled debug logging instead.

See also