0.4.0 • Published 5 months ago

snapmetrics v0.4.0

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

SnapMetrics

Author: ToolPlayer

License: MIT

Overview

SnapMetrics is a lightweight, in-memory metrics tracking library that calculates rolling statistics over configurable time windows. It provides:

  • Rolling Statistics: Calculate comprehensive statistics including averages, medians, percentiles, minimums, maximums, and standard deviations over user-defined time windows
  • Flexible Time Windows: Configure multiple concurrent time windows (e.g., "15s", "1m", "2h") to track metrics over different durations
  • Performance Measurement: Built-in utilities to measure and record function execution times, with native support for both synchronous and asynchronous functions
  • Efficient Processing: Uses a circular buffer implementation with smart caching and configurable throttling for optimal memory usage and performance

The library is designed to be lightweight and easy to integrate into any JavaScript/TypeScript application needing real-time performance monitoring and statistical analysis.

Important Note

This package is native ESM and does not provide CommonJS support. Ensure your project is configured to work with ESM modules.

Features

  • Rolling Statistics: Calculate comprehensive metrics over configurable time windows:
    • Averages (mean)
    • Medians
    • Percentiles (configurable, defaults to 90th and 95th)
    • Minimums and maximums
    • Standard deviations
    • Counts and sums
  • Event Tracking: Track frequency of named events over time windows
    • Increment counters by custom values
    • Track multiple event types independently
    • Automatic expiration of old events
  • Flexible Time Windows: Support for multiple concurrent time windows using <number><unit> format (e.g., "15s", "1m", "2h")
  • Performance Measurement: Built-in utilities to measure function execution times:
    • Support for both synchronous and asynchronous functions
    • Automatic duration recording

Installation

Install SnapMetrics via npm:

npm install snapmetrics

Usage

Basic Example

Track request durations in an Express application and expose rolling averages:

import express from "express";
import { SnapMetrics } from "snapmetrics";

const app = express();
const sm = new SnapMetrics();

app.use((req, res, next) => {
  const startTime = performance.now();
  res.on("finish", () => {
    const duration = performance.now() - startTime;
    sm.record(duration);
  });
  next();
});

app.get("/", (req, res) => {
  res.send('Hello! <a href="/metrics">See metrics</a>');
});

app.get("/metrics", (req, res) => {
  res.json(sm.getAverages());
});

app.listen(3000, () => {
  console.log("Express app listening on port 3000");
});
// http://localhost:3000/metrics
{
  "1m": 2.33,
  "5m": 2.15,
  "15m": 2.53
}

Advanced Example with PM2 Integration

Monitor rolling averages using PM2 metrics:

import io from "@pm2/io";
import { SnapMetrics } from "snapmetrics";

function setupPm2Metrics() {
  const sm = new SnapMetrics();

  const pm2Metrics = {};
  const initialAverages = sm.getAverages();

  for (const key in initialAverages) {
    pm2Metrics[key] = io.metric({ name: `Response Time (${key})`, unit: "ms" });
  }

  setInterval(() => {
    const updatedAverages = sm.getAverages();

    for (const key in updatedAverages) {
      if (pm2Metrics[key]) {
        pm2Metrics[key].set(updatedAverages[key]);
      }
    }
  }, 1000);
}

// Note: The code to record response times into SnapMetrics is not included here.
// You would need to call `sm.record(value)` with the appropriate response time elsewhere in your application.

setupPm2Metrics();

Track API calls and errors:

import { SnapMetrics } from "snapmetrics";

const sm = new SnapMetrics();

// Track API calls
app.use((req, res, next) => {
  sm.increment("api_calls");
  next();
});

// Track errors
app.use((err, req, res, next) => {
  sm.increment("errors");
  next(err);
});

// Track bytes sent
app.use((req, res, next) => {
  const originalSend = res.send;
  res.send = function (body) {
    sm.increment("bytes_sent", Buffer.byteLength(body));
    return originalSend.call(this, body);
  };
  next();
});

// Expose metrics
app.get("/metrics", (req, res) => {
  res.json({
    counters: sm.getCounters(),
    stats: sm.getMetrics(),
  });
});
// http://localhost:3000/metrics
{
  "counters": {
    "1m": {
      "api_calls": 150,
      "errors": 2,
      "bytes_sent": 52428
    },
    "5m": {
      "api_calls": 720,
      "errors": 8,
      "bytes_sent": 248832
    },
    "15m": {
      "api_calls": 2160,
      "errors": 15,
      "bytes_sent": 746496
    }
  },
  "stats": {
    // ... other metrics ...
  }
}

API Reference

Constructor

new SnapMetrics(timeWindowsOrOptions)

The constructor accepts either an array of time windows or a configuration options object:

Option 1: Pass an Array of Time Windows

  • timeWindows (optional):

    Array of time windows, formatted as <integer><unit> where unit is s, m, or h.

    Defaults to ["1m", "5m", "15m"]

Option 2: Pass a Configuration Options Object

  • options (optional):

    An object containing the following properties:

    • timeWindows (optional):

      Array of time windows, formatted as <integer><unit> where unit is s, m, or h. Defaults to ["1m", "5m", "15m"].

    • removeExpiredRecordsThrottlingMS (optional):

      Time in milliseconds to throttle the removal of expired records. Must be a non-negative number (>= 0) or false to disable throttling entirely. Defaults to 100 ms.

    • debug (optional):

      Enables logging for debugging. Defaults to false.

Methods

  • record(value: number): void

    Records a value into all active time windows. The value is stored with a timestamp and used for calculating various metrics.

  • recordDuration<T>(fn: () => T | Promise<T>): T | Promise<T>

    Measures the execution time of a synchronous or asynchronous function and records the duration in all time windows. Returns the result of the executed function. For async functions, returns a Promise that resolves to the function result.

  • getCounts(): Record<TimeWindow, number>

    Returns the count of values for all time windows. Returns a record mapping each time window to its count of recorded values.

  • getSums(): Record<TimeWindow, number | null>

    Returns the sum of values for all time windows. Returns a record mapping each time window to the sum of its recorded values. Returns null for empty windows.

  • getAverages(): Record<TimeWindow, number | null>

    Returns the rolling averages for all time windows. Returns a record mapping each time window to the average (mean) of its recorded values. Returns null for empty windows.

  • getMedians(): Record<TimeWindow, number | null>

    Returns the middle value for each time window using linear interpolation. For an even number of values, uses linear interpolation between the two middle values. Returns null for empty windows.

  • getPercentiles(percentile: number): Record<TimeWindow, number | null>

    Returns the value below which the given percentage of observations fall, using Hyndman and Fan type 7 linear interpolation method. Takes a percentile value between 0 and 100. Returns null for empty windows.

  • getMinimums(): Record<TimeWindow, number | null>

    Returns the smallest value recorded within each time window. Returns null for empty windows.

  • getMaximums(): Record<TimeWindow, number | null>

    Returns the largest value recorded within each time window. Returns null for empty windows.

  • getStandardDeviations(): Record<TimeWindow, number | null>

    Returns the standard deviation (square root of variance) for each time window, indicating how spread out values are from their mean. Returns null for empty windows.

  • getMetrics(percentiles: number[] = [90, 95]): Record<TimeWindow, Record<string, number | null>>

    Returns all metrics for each time window. Returns a record mapping each time window to a record containing all metrics.

  • increment(name: string, value: number = 1): void

    Increments a named counter for tracking frequency across time windows. The counter is automatically maintained within the configured time windows, with old events expiring based on the window duration.

    Parameters:

    • name: The name of the counter to increment
    • value (optional): Amount to increment by (defaults to 1)
  • getCounters(): Record<TimeWindow, Record<string, number>>

    Returns the current value of all counters for each time window. Returns a record mapping each time window to a map of counter names and their current values.

  • getCounter(name: string): Record<TimeWindow, number | null>

    Returns the current value of a specific counter for each time window. Returns a record mapping each time window to the counter's value. Returns null if the counter doesn't exist.

    Parameters:

    • name: The name of the counter to retrieve

    Example:

    const metrics = new SnapMetrics();
    metrics.increment("api_calls");
    metrics.getCounter("api_calls"); // { "1m": 1, "5m": 1, "15m": 1 }
    metrics.getCounter("non_existent"); // { "1m": null, "5m": null, "15m": null }

Contribution

Contributions are welcome! Submit issues or pull requests via the GitHub repository.