@edakzin/mapi
v1.1.3
Published
Real-time performance and network monitoring module for Next.js / Node.js on Linux.
Readme
@edakzin/mapi
Real-time system performance and network monitoring module for Node.js and Next.js. The native collector is written in Rust with NAPI-RS, and the package exposes a typed TypeScript wrapper for structured snapshots or continuous logging.
What It Measures
- CPU usage: global usage and per-core usage.
- Memory: used RAM, total RAM, used swap, total swap, and percentages.
- System state: uptime, load average, process count, and timestamp.
- TCP connections: established TCP/TCP6 connections owned by the current process, grouped by local port.
- Listening ports: listening ports detected for the current process on Linux.
Requirements
- Node.js 20 or newer is recommended.
- Linux is required for real TCP connection sampling because the native collector reads
/proc/self/fd,/proc/net/tcp, and/proc/net/tcp6. - macOS and Windows can load the package, but connection sampling remains degraded and returns an empty breakdown with a non-fatal error message in the snapshot.
Installation
npm install @edakzin/mapiAutomatic Startup
Node.js does not execute dependencies just because they are installed. A production app must load mapi once at startup. Professional monitoring packages usually document this as one of these setup paths:
- Framework hook: use the startup hook provided by the framework.
- Preload hook: use
NODE_OPTIONS=--require ...so Node loads the package before the app entrypoint. - Manual API: import the package and call the starter yourself.
Next.js
Create instrumentation.ts in the root of your Next.js project:
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('@edakzin/mapi/auto');
}
}This starts mapi with defaults when the Next.js server starts:
enabled:trueintervalMs:180000port:'auto'logPath:./logs/mapi-performance.loglogRetentionDays:14
Node.js Preload
Use this when you want mapi to start without adding imports to your app code:
NODE_OPTIONS="--require @edakzin/mapi/auto" node server.jsFor npm scripts:
{
"scripts": {
"start": "NODE_OPTIONS=\"--require @edakzin/mapi/auto\" node server.js"
}
}On Windows PowerShell:
$env:NODE_OPTIONS="--require @edakzin/mapi/auto"; npm startAutomatic Configuration
@edakzin/mapi/auto reads these environment variables:
| Variable | Default | Description |
| --- | --- | --- |
| MAPI_ENABLED | true | Use false, 0, no, or off to disable automatic startup. |
| MAPI_INTERVAL_MS | 180000 | Interval between reports in milliseconds. |
| MAPI_PORT | auto | Use auto, one port, or a comma-separated list such as 3000,8080. |
| MAPI_LOG_PATH | ./logs/mapi-performance.log | Base path used to derive daily files such as ./logs/mapi-performance-2026-06-10.log. |
| MAPI_LOG_RETENTION_DAYS | 14 | Days a rotated file can stay before it becomes eligible for cleanup. Cleanup runs once per day and only removes files older than this value. Use false, 0, no, or off to disable cleanup. |
If a previous version already wrote a monolithic file at MAPI_LOG_PATH, mapi archives that legacy file on the next start and redistributes its reports into the daily files using each report timestamp.
How Automatic Port Detection Works
When port: 'auto' is enabled, Rust discovers ports from the live process state instead of reading framework config:
- It scans
/proc/self/fdand extracts the socket inodes owned by the current Node.js process. - A native background thread reads
/proc/net/tcpand/proc/net/tcp6every200ms. - It keeps only rows whose inode belongs to the current process.
- Sockets in
LISTENstate becomedetectedListeningPorts. - Sockets in
ESTABLISHEDstate incrementtotalEstablishedand are grouped by local port inbyLocalPort.
This is why auto can discover ports such as 3000 and 3001 without the user declaring them first. Declared ports remain a presentation hint for the plain-text report.
Direct Report Usage
Use this mode when you want to decide when metrics are collected.
import { getTelemetrySnapshot, getMetricsReport } from '@edakzin/mapi';
const snapshot = getTelemetrySnapshot();
console.log(snapshot.connections.totalEstablished);
const report = getMetricsReport('auto');
console.log(report);initTracker() is still available if you want to start the native sampler eagerly, but the TypeScript wrapper now initializes it automatically on first use.
Continuous Logging Usage
Use this mode in a server process when you want mapi to append reports to a daily rotated log file on an interval.
import { startTracker, stopTracker } from '@edakzin/mapi';
startTracker({
enabled: process.env.NODE_ENV === 'production',
intervalMs: 180_000,
port: 'auto',
logPath: './logs/mapi-performance.log',
logRetentionDays: 14,
});
process.on('SIGTERM', () => {
stopTracker();
});By default, startTracker() only runs in production. Pass enabled: true if you need to force it in another environment.
logPath acts as a base path, and mapi writes files like mapi-performance-2026-06-10.log.
If a legacy monolithic log already exists at that exact base path, mapi archives it as mapi-performance-legacy-archive.log and migrates its parsed reports to the daily files without discarding the original data.
API
@edakzin/mapi/auto
Starts continuous logging as soon as the module is loaded. It is intended for Next.js instrumentation or Node.js preload setups.
initTracker(): void
Initializes the native system tracker and starts the background connection sampler.
getTelemetrySnapshot(): TelemetrySnapshot
Returns a structured snapshot with CPU, memory, swap, uptime, load average, process count, timestamp, and connection data.
TelemetrySnapshot.connections contains:
totalEstablished: totalESTABLISHEDsockets owned by the current process.byLocalPort: breakdown by local port, for example[{ port: 3000, established: 4 }].detectedListeningPorts: listening ports detected for the current process.lastScanAt: timestamp of the last successful native scan.stale: whether the connection sampler is degraded or stale.error: a non-fatal error message when connection sampling is not healthy.
getMetricsReport(ports?: number | number[] | 'auto'): string
Returns a formatted text report built from the structured snapshot.
Parameters:
ports: optional preferred ports to highlight in the text report. They do not limit native collection.
Important details:
- On Linux, mapi samples all
ESTABLISHEDTCP/TCP6 sockets owned by the current process and groups them by local port. - In
automode, listening ports come from live sockets discovered through/proc/self/fd, not from Next.js or Node.js configuration introspection. - Declared ports are only a presentation preference for the text report.
- On Windows and macOS, the connection breakdown stays empty and the snapshot marks the sampler as degraded instead of throwing a fatal error.
startTracker(config?: MapiConfig): void
Starts continuous metric collection and appends each report to a daily rotated log file.
Configuration:
enabled?: boolean: enables or disables the tracker. Default:process.env.NODE_ENV === 'production'.intervalMs?: number: interval between reports in milliseconds. Default:180000.port?: number | number[] | 'auto': preferred ports for report presentation. Default:3000in the manual API andautoin@edakzin/mapi/auto.logPath?: string: base path for rotated log files. Relative paths are resolved from the current working directory. Default:./logs/mapi-performance.log, which produces files such as./logs/mapi-performance-2026-06-10.log.logRetentionDays?: number | false: number of days a rotated file can stay before it becomes eligible for cleanup. Cleanup runs once per day and only removes files older than this value. Usefalseto disable cleanup. Default:14.
Legacy migration details:
- If a monolithic legacy file still exists at
logPath, mapi archives it before writing new daily files. - Parsed legacy reports are redistributed by the
TIMESTAMPdate found inside each report block. - The archived legacy copy is preserved so no existing data is discarded during migration.
stopTracker(): void
Stops the active interval created by startTracker() and shuts down the native connection sampler.
Output Example
======================================================================
TIMESTAMP: 2026-05-31 18:42:15.331
UPTIME: 1d 04h 12m 30s
LOAD AVERAGE: 0.15, 0.22, 0.08
TASKS: 142 total
ESTABLISHED CONNECTIONS (Proceso actual): 5
CONFIGURED PORTS: auto
DETECTED LISTENING PORTS: 3000, 3001
LAST CONNECTION SCAN: 2026-05-31 18:42:15.200 (ok)
----------------------------------------------------------------------
CONNECTION BREAKDOWN (Local Port):
Puerto 3000 : 4
Puerto 49152: 1
----------------------------------------------------------------------
CPU USAGE:
Global: 12.45%
MEMORY:
RAM: 4096 MB / 16384 MB (25.00%)
SWAP: 1024 MB / 8192 MB (12.50%)
======================================================================