@oas-tools/oas-telemetry
v0.8.0
Published
Automatically collects and stores telemetry data in memory using OpenTelemetry. Just import the Express middleware to access it through the built-in REST API or web UI—no extra setup required.
Readme
OAS TELEMETRY
OAS Telemetry is a library that automatically configures the OpenTelemetry SDK for your Express application based on OpenAPI, with no extra code required. It sets up auto-instrumentations along with some custom ones out of the box, and stores the collected traces, metrics, and logs in memory, providing a UI to inspect them locally. This allows you to analyze your API's behavior and debug issues easily, without manual setup or complex integration.
By default, telemetry data is kept only in memory and is discarded after a configurable retention time (60 minutes by default). If you want to persist it, you can set the appropriate environment variable to export data directly to an OpenTelemetry Collector, which can then forward it to your backend of choice.
The middleware is highly configurable and supports both ES Module (ESM-ES2020) and CommonJS (CJS) formats. Its functionality can be extended via plugins; see Telemetry Plugins for details.
⚠️ Warning: Early Development Notice
OAS Telemetry is a functional and working package currently progressing toward version 1.0.
While the core architecture is solid, features and APIs may still receive refinements or minor changes before the first stable release.
Below is the current module status and roadmap:
Logs: Fully functional and stable for general use. Only minor changes are expected before version 1.0.
Supports fast search by message content and Mongo-like query syntax (similar to traces).AI Chat: Fully functional and stable. Integrated with OpenAI, requiring an API key set via environment variable or in the
.envfile.
Only small improvements or new provider integrations are planned.Plugin System: Operational and stable. Only minor adjustments may occur before 1.0.
The plugin management page in the UI is already functional.Configuration: The configuration system is considered stable in design.
Using environment variables and initialization parameters has proven to be convenient and flexible.
We do not expect breaking changes — new configuration options will be added only when new functionality is introduced, and all updates will always be documented in the.env.examplefile and official documentation.UI: Under active development. Logs, Plugins, ChatAI, Traces, and Metrics pages are available, although the UI may still receive improvements before version 1.0.
Traces: Currently functional with auto instrumentations for the exporters. In memory exporter only saves HTTP spans in order to reduce memory usage.
Metrics: Functional but expected to change.
We are exploring optimizations to reduce memory usage and improve data handling before 1.0.Please, if you want to use this package or collaborate, contact us via [email protected].
Usage
This section provides an overview of how to install and integrate OAS Telemetry into your EXISTING Express.js application. If you want to create a new example express application with OAS Telemetry integrated, refer to the Full Examples section at the end of this document.
First, install the package using npm:
npm install @oas-tools/oas-telemetryThen add the .env file to your project root directory. This file contains the environment variables used by the telemetry middleware. You can find an example of the .env file in the .env.example file in the root of the repository. The .env file is optional, but it is needed for the AI chat.
You can integrate the middleware into your Express application. The spec option is the OpenAPI Specification (OAS) content in JSON or YAML format. While this configuration is optional, it is recommended for the UI to function correctly.
Using ES Modules (ESM)
Add the following lines to your index.js file:
// This import MUST be at the top of the file
import { oasTelemetry } from '@oas-tools/oas-telemetry';
// ...rest of your code here creating an express app and importing the OpenAPI spec
// NOTE: Do not add express.json() before oasTelemetry, or set its limit to at least "10mb" to avoid issues with large files.
app.use(oasTelemetry({ general: { spec: JSON.stringify(spec) } }));
Using CommonJS
Add the following lines to your index.js file:
// This require MUST be at the top of the file
const { oasTelemetry } = require('@oas-tools/oas-telemetry');
// ...rest of your code here creating an express app and importing the OpenAPI spec
// NOTE: Do not add express.json() before oasTelemetry, or set its limit to at least "10mb" to avoid issues with large files.
app.use(oasTelemetry({ general: { spec: JSON.stringify(spec) } }));
For complete examples of a working API with OAS Telemetry enabled, refer to the Full Examples section at the end of this document.
Custom Configuration
You can also customize the telemetry configuration by passing options to the middleware function. For example:
Note: Although you can set configuration options programmatically, it is recommended to use the provided
.env.examplefile as a template for your environment variables. Values set in the.envfile will override any options passed directly to the middleware.
export const customTelemetryConfig = {
general: {
specFileName: "oas.json",
spec: null,
},
auth: {
enabled: true,
accessTokenMaxAge: 1000 * 60 * 30, // 30 minutes
// For development purposes only. In production, use a secure password and consider using environment variables to store it.
password: "my-custom-password",
jwtSecret: "my-super-secret",
},
ai: {
openAIModel: "gpt-4o",
extraContextPrompts: [
"Provide clear, concise answers",
"Use professional tone",
],
},
traces: {
extraExporters: [],
extraProcessors: [],
memoryExporter: {
enabled: true,
retentionTimeSeconds: 60 * 60 * 2, // 2 hours
},
captureBody: {
// Attaches request/response body to the active span for debugging.
// "off" | "onError" | "onMismatch" | "onMismatchOrError" | "always"
mode: "off",
maxSizeBytes: 8 * 1024,
},
},
metrics: {
mainMetricReaderOptions: {
exportIntervalMillis: 1000 * 60,
metricProducers: [],
},
extraReaders: [],
memoryExporter: {
enabled: true,
retentionTimeSeconds: 60 * 60 * 2, // 2 hours
},
recordSchemaCompliance: true, // records oas.schema.compliance, whether each request matched the OpenAPI spec
extraViews: [],
},
logs: {
extraExporters: [],
extraProcessors: [],
memoryExporter: {
enabled: true,
retentionTimeSeconds: 60 * 60 * 2, // 2 hours
},
},
plugins: {
enabled: true,
extraPlugins: [],
},
instrumentations: [],
};
// Configure the OpenAI key with OASTLM_CONFIG_AI_OPENAI_KEY in the environment.
app.use(oasTelemetry(customTelemetryConfig));Note: To disable the module, set the environment variable OASTLM_BOOT_MODULE_DISABLED to 'true'.
Telemetry UI
You can access the telemetry UI at the endpoint /oas-telemetry (or at a custom path if you set the OASTLM_BOOT_BASE_URL environment variable). This UI provides a user-friendly interface to interact with the telemetry data collected by the middleware.
Rest API Endpoints Overview
All routes below are relative to OASTLM_BOOT_BASE_URL, which defaults to /oas-telemetry.
Authentication Endpoints
POST /auth/login: Log in to the system.POST /auth/logout: Log out of the system.POST /auth/refresh: Refresh the access token.GET /auth/enabled: Check whether authentication is enabled.
Metrics Endpoints
GET /metrics/exporters/in-memory-exporter/data: List all metrics.POST /metrics/exporters/in-memory-exporter/data: Insert metrics into the database.DELETE /metrics/exporters/in-memory-exporter/data: Reset metrics data.POST /metrics/exporters/in-memory-exporter/data/find: Search metrics.POST /metrics/exporters/in-memory-exporter/start: Start metrics data collection.POST /metrics/exporters/in-memory-exporter/stop: Stop metrics data collection.GET /metrics/exporters/in-memory-exporter/status: Get metrics status.POST /metrics/exporters/in-memory-exporter/reset: Reset metrics data.GET /metrics/exporters/in-memory-exporter/retention-time: Get metrics retention time.POST /metrics/exporters/in-memory-exporter/retention-time: Set metrics retention time.GET /metrics/exporters/in-memory-exporter/export: Export metrics data.POST /metrics/exporters/in-memory-exporter/import: Import metrics data.GET /metrics/stats: Get metrics statistics.
Logs Endpoints
GET /logs/exporters/in-memory-exporter/data: List all logs.POST /logs/exporters/in-memory-exporter/data: Insert logs into the database.DELETE /logs/exporters/in-memory-exporter/data: Reset logs data.POST /logs/exporters/in-memory-exporter/data/find: Search logs.POST /logs/exporters/in-memory-exporter/start: Start logs data collection.POST /logs/exporters/in-memory-exporter/stop: Stop logs data collection.GET /logs/exporters/in-memory-exporter/status: Get logs status.POST /logs/exporters/in-memory-exporter/reset: Reset logs data.GET /logs/exporters/in-memory-exporter/retention-time: Get logs retention time.POST /logs/exporters/in-memory-exporter/retention-time: Set logs retention time.GET /logs/exporters/in-memory-exporter/export: Export logs data.POST /logs/exporters/in-memory-exporter/import: Import logs data.
Traces/Spans Endpoints
GET /spans/exporters/in-memory-exporter/data: List all spans.POST /spans/exporters/in-memory-exporter/data: Insert spans into the database.DELETE /spans/exporters/in-memory-exporter/data: Reset spans data.POST /spans/exporters/in-memory-exporter/data/find: Search spans.POST /spans/exporters/in-memory-exporter/start: Start spans data collection.POST /spans/exporters/in-memory-exporter/stop: Stop spans data collection.GET /spans/exporters/in-memory-exporter/status: Get spans status.POST /spans/exporters/in-memory-exporter/reset: Reset spans data.GET /spans/exporters/in-memory-exporter/retention-time: Get spans retention time.POST /spans/exporters/in-memory-exporter/retention-time: Set spans retention time.GET /spans/exporters/in-memory-exporter/export: Export spans data.POST /spans/exporters/in-memory-exporter/import: Import spans data.GET /traces/:traceId: Get spans filtered by trace ID.
AI Endpoints
GET /ai/chat: List conversations.POST /ai/chat: Create a conversation.GET /ai/chat/tools: List available AI tools.GET /ai/chat/:conversationId: Get conversation history.POST /ai/chat/:conversationId/message: Send a message.DELETE /ai/chat/:conversationId: Delete a conversation.GET /ai/chat/health: Check AI service health.
Plugins Endpoints
GET /plugins: List all registered plugins.POST /plugins: Register a plugin.POST /plugins/:id/activate: Activate a plugin.POST /plugins/:id/deactivate: Deactivate a plugin.DELETE /plugins/:id: Delete a plugin.
Utility Endpoints
GET /utils/spec: Load the OpenAPI specification.GET /utils/heapStats: Show v8 heap statistics.GET /utils/generate-log: Generate a log message.GET /utils/generate-wait/:seconds?: Wait for a specified number of seconds.GET /health: Perform a health check.
Telemetry Plugins
Note: Plugins run in separate child processes and can be registered as ESM or CommonJS modules. Their concrete telemetry integrations depend on the plugin implementation.
OAS Telemetry supports a range of plugins to extend its functionality, allowing developers to tailor telemetry data collection, alerting, and reporting to meet specific requirements. Plugins enable additional features, such as integration with alerting systems, custom data exporters, and data visualization tools.
One example plugin is the Outlier Alert Over Messaging plugin, which can be configured to send anomaly alerts to messaging platforms like Telegram. This plugin is especially useful for monitoring abnormal response times in your API, notifying selected channels to allow rapid responses to potential issues. For setup details, refer to its README documentation.
OAS Telemetry plugins are flexible and support both ES Modules (ESM) and CommonJS (CJS) formats, regardless of whether your application is using ESM or CJS. This compatibility ensures that plugins work seamlessly in all configurations:
- ESM applications can use plugins in either ESM or CJS format.
- CJS applications can use plugins in either CJS or ESM format.
This flexibility makes it easy to incorporate a wide variety of plugins in your preferred module system.
Accessing Telemetry Data
Using OAS Telemetry, you can access telemetry data through the UI, the /oas-telemetry/spans/exporters/in-memory-exporter/data endpoint, or the /oas-telemetry/spans/exporters/in-memory-exporter/data/find endpoint with a POST request using a MongoDB search syntax. Replace /oas-telemetry with the value configured in OASTLM_BOOT_BASE_URL.
Note: if authentication is enabled, you must provide the correct credentials to access the telemetry data.
Search Example
To perform a simple search, send a POST request to the /oas-telemetry/spans/exporters/in-memory-exporter/data/find endpoint with the following JSON payload:
{
"query": {
"attributes.http.target": "/api/v1/pets",
"attributes.http.method": "GET",
"$or": [
{"attributes.http.status_code": 200},
{"attributes.http.status_code": 304}
]
}
}You can also use regular expressions and comparison operators for more complex searches. For example, to find all GET requests to paths starting with /api/v1/pets that returned a status code less than or equal to 400, you can use the following query:
{
"query": {
"attributes.http.target": {
"$regex": "^\/api\/v1\/pets.*$"
},
"attributes.http.method": "GET",
"$or": [
{
"attributes.http.status_code": {
"$lte": 400
}
}
]
}
}Full Examples
To run these examples, follow these steps:
You will need Node.js >= v18 and npm. You can install them using nvm
Create a new folder for your project.
Navigate to the folder and initialize a new Node.js project:
npm init -yInstall the OAS Telemetry package and other dependencies:
npm install @oas-tools/oas-telemetry@alpha express dotenvSave the example cjs code as
index.jsin the project folder. You can add in the .env necessary variables like your openAI api key. (see .env.example)Run the application:
node index.jsGo to
/oas-telemetry/(or the path configured withOASTLM_BOOT_BASE_URL) to open the UI. You can also use the API, for exampleGET /oas-telemetry/logs/exporters/in-memory-exporter/data,GET /oas-telemetry/spans/exporters/in-memory-exporter/data, orGET /oas-telemetry/traces/:traceId.
Your project folder should now contain the necessary files to run the example with OAS Telemetry integrated.
Simple Example ES Module (*.mjs)
import { oasTelemetry } from '@oas-tools/oas-telemetry';
import express from 'express';
import dotenv from 'dotenv';
if (process.env.NODE_ENV !== 'test') {
dotenv.config();
}
const app = express();
const port = process.env.PORT || 3000;
const spec = {
"paths": {
"/api/v1/pets": {
"get": {
"summary": "Get pets",
"responses": {
"200": {
"description": "Success"
}
}
},
"post": {
"summary": "Insert a pet",
"responses": {
"201": {
"description": "Pet Created"
},
"400": {
"description": "Bad Request"
}
}
}
},
"/api/v1/pets/{petName}": {
"get": {
"summary": "Get a pet",
"parameters": [
{
"name": "petName",
"in": "path",
"required": true,
"description": "The name of the pet to retrieve",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success"
},
"404": {
"description": "Not Found"
}
}
}
},
"/api/v1/clinics": {
"get": {
"summary": "Get pets",
"responses": {
"200": {
"description": "Success"
}
}
}
}
}
}
app.use(oasTelemetry({ general: { spec: JSON.stringify(spec) } }));
app.use(express.json());
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
let pets = [{ name: "rocky" }, { name: "pikachu" }];
let clinics = [{ name: "Pet Heaven" }, { name: "Pet Care" }];
app.get("/api/v1/pets", (req, res) => {
res.send(pets);
});
app.post("/api/v1/pets", (req, res) => {
if (req.body && req.body.name) {
pets.push(req.body);
res.sendStatus(201);
} else {
res.sendStatus(400);
}
});
app.get("/api/v1/pets/:name", (req, res) => {
let name = req.params.name;
let filterdPets = pets.filter((p) => (p.name == name));
if (filterdPets.length > 0)
return res.send(filterdPets[0]);
else
return res.sendStatus(404);
});
app.get("/api/v1/clinics", (req, res) => {
res.send(clinics);
});Simple Example Common.js Module (*.cjs)
let { oasTelemetry } = require('@oas-tools/oas-telemetry');
let express = require('express');
let dotenv = require('dotenv');
if (process.env.NODE_ENV !== 'test') {
dotenv.config();
}
const app = express();
const port = process.env.PORT || 3000;
const spec = {
"paths": {
"/api/v1/pets": {
"get": {
"summary": "Get pets",
"responses": {
"200": {
"description": "Success"
}
}
},
"post": {
"summary": "Insert a pet",
"responses": {
"201": {
"description": "Pet Created"
},
"400": {
"description": "Bad Request"
}
}
}
},
"/api/v1/pets/{petName}": {
"get": {
"summary": "Get a pet",
"parameters": [
{
"name": "petName",
"in": "path",
"required": true,
"description": "The name of the pet to retrieve",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success"
},
"404": {
"description": "Not Found"
}
}
}
},
"/api/v1/clinics": {
"get": {
"summary": "Get pets",
"responses": {
"200": {
"description": "Success"
}
}
}
}
}
}
app.use(oasTelemetry({ general: { spec: JSON.stringify(spec) } }));
app.use(express.json());
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
let pets = [{ name: "rocky" }, { name: "pikachu" }];
let clinics = [{ name: "Pet Heaven" }, { name: "Pet Care" }];
app.get("/api/v1/pets", (req, res) => {
res.send(pets);
});
app.post("/api/v1/pets", (req, res) => {
if (req.body && req.body.name) {
pets.push(req.body);
res.sendStatus(201);
} else {
res.sendStatus(400);
}
});
app.get("/api/v1/pets/:name", (req, res) => {
let name = req.params.name;
let filterdPets = pets.filter((p) => (p.name == name));
if (filterdPets.length > 0)
return res.send(filterdPets[0]);
else
return res.sendStatus(404);
});
app.get("/api/v1/clinics", (req, res) => {
res.send(clinics);
});