Skip to content

Observability

This guide demonstrates the built-in observability capabilities provided by Teleportal, including Prometheus metrics endpoints, structured logging with LogTape, and server event hooks for custom monitoring.

  • Accessing the /health endpoint for server health checks
  • Accessing the /metrics endpoint for Prometheus-compatible metrics
  • Accessing the /status endpoint for server status information
  • Configuring structured logging with LogTape
  • Listening to server events for custom monitoring workflows
import { Server } from "teleportal/server";
import { getMetricsHandler, getHealthHandler, getStatusHandler } from "teleportal/http";
const server = new Server({
storage: async (ctx) => {
// Your storage implementation
return documentStorage;
},
});
// Expose observability endpoints
app.get("/metrics", getMetricsHandler(server));
app.get("/health", getHealthHandler(server));
app.get("/status", getStatusHandler(server));

The /metrics endpoint returns Prometheus-formatted metrics:

Terminal window
curl http://localhost:3000/metrics

Metrics include:

  • teleportal_sessions_active: Current number of active document sessions (gauge)
  • teleportal_clients_active: Current number of active clients (gauge)
  • teleportal_documents_opened_total: Total documents opened (counter)
  • teleportal_messages_total: Total messages processed, labeled by type (counter)
  • teleportal_messages_total_all: Total messages processed across all types, single series (counter)
  • teleportal_message_duration_seconds: Message processing duration, labeled by type (histogram)

The /health endpoint returns server health status:

Terminal window
curl http://localhost:3000/health

Response:

{
"status": "healthy",
"timestamp": "2024-01-01T00:00:00.000Z",
"checks": {},
"uptime": 3600
}

The endpoint always returns status: "healthy" with an empty checks object and the process uptime in seconds. The monitoring module defines only the HealthStatus and StatusData types – it does not implement any dependency health probes. checks is a placeholder you can populate by extending the server if you need liveness signals for downstream dependencies (storage, PubSub, etc.).

The /status endpoint returns detailed operational status:

Terminal window
curl http://localhost:3000/status

Response:

{
"nodeId": "node-123",
"activeClients": 10,
"activeSessions": 5,
"pendingSessions": 0,
"totalMessagesProcessed": 1000,
"totalDocumentsOpened": 50,
"messageTypeBreakdown": {
"doc": 500,
"awareness": 300,
"rpc": 200
},
"rateLimitExceededTotal": 0,
"rateLimitBreakdown": {},
"rateLimitTopOffenders": [],
"rateLimitRecentEvents": [],
"totalDocumentSizeBytes": 1048576,
"documentsOverWarningThreshold": 0,
"documentsOverLimit": 0,
"uptime": 3600,
"timestamp": "2024-01-01T00:00:00.000Z"
}

The messageTypeBreakdown is keyed by wire message type (doc, awareness, presence, rpc, ack) – milestone and file operations are carried over rpc, not distinct message types.

Teleportal uses LogTape for structured logging. Following LogTape’s library guidelines, Teleportal does not configure any sinks itself – your application must set up LogTape before starting the server.

import { configure, getConsoleSink, getFileSink } from "@logtape/logtape";
await configure({
sinks: {
console: getConsoleSink(),
file: getFileSink("teleportal.log"),
},
filters: {},
loggers: [
{
category: ["teleportal"],
sinks: ["console", "file"],
lowestLevel: "info",
},
],
});

Teleportal emits all of its logs under a single category:

  • ["teleportal", "server"] – every wide event (one structured log line per logical operation): connections, errors, message processing, and session lifecycle. Each wide event carries the environment context { service: "teleportal" } merged in.

Because LogTape uses hierarchical categories, configuring the ["teleportal"] category (as shown above) captures these logs. Per LogTape’s library guidelines, Teleportal configures no sinks of its own – your application owns sink and level configuration.

The server emits wide events – single structured log lines that capture the full context of an operation. Each wide event includes environment context (service name, node ID) merged with operation-specific fields such as document IDs, client IDs, message types, error details, and timing information.

The Server extends Observable and emits typed events you can listen to for custom monitoring, alerting, or integration with external systems.

import { Server } from "teleportal/server";
const server = new Server({
/* ... */
});
server.on("document-load", (data) => {
console.log("Document loaded:", data.documentId);
console.log("Encrypted:", data.encrypted);
});
server.on("client-connect", (data) => {
console.log("Client connected:", data.clientId);
});
server.on("client-disconnect", (data) => {
console.log("Client disconnected:", data.clientId);
console.log("Reason:", data.reason);
});
server.on("document-size-warning", (data) => {
// Trigger an alert when a document grows too large
alerting.warn(`Document ${data.documentId} is ${data.sizeBytes} bytes`);
});

Document events: document-load, document-unload, milestone-created, milestone-deleted, milestone-restored, document-delete, document-size-warning, document-size-limit-exceeded

Client events: client-connect, client-disconnect, client-message

Lifecycle events: before-server-shutdown, after-server-shutdown

See the Server documentation for the full event list and payload types.

  • Server - Server configuration, events, and monitoring details