npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@primafuture/telemetry-stack

v0.3.0

Published

Reusable PrimaFuture telemetry stack launcher powered by Docker Compose.

Readme

@primafuture/telemetry-stack

Reusable telemetry stack.

This package does not contain Grafana, Loki, Tempo, Mimir, MinIO, Redpanda, Alloy, or Prometheus binaries. It contains a Docker Compose recipe and a small CLI that starts those Docker images for you.

Requirements

  • Node.js 18+
  • Docker
  • Docker Compose plugin

Quick Start

Run a development telemetry stack and store its data in ./data/telemetry:

npx @primafuture/telemetry-stack up ./data/telemetry

Open Grafana:

npx @primafuture/telemetry-stack open ./data/telemetry grafana

Stop the stack:

npx @primafuture/telemetry-stack down ./data/telemetry

Local Development

From this package directory:

node bin/telemetry-stack.mjs doctor
node bin/telemetry-stack.mjs config /tmp/telemetry-stack-test
node bin/telemetry-stack.mjs up /tmp/telemetry-stack-test

Commands

telemetry-stack init <data-dir> [options]
telemetry-stack up <data-dir> [options]
telemetry-stack down <data-dir> [options]
telemetry-stack restart <data-dir> [options]
telemetry-stack ps <data-dir> [options]
telemetry-stack logs <data-dir> [service] [options]
telemetry-stack open <data-dir> [target] [options]
telemetry-stack config <data-dir> [options]
telemetry-stack doctor

<data-dir> is where the stack stores runtime data and the instance config. Use a different directory for a different instance.

Common options:

--name <name>        Override Docker Compose project name.
--mode <dev|prod>    Instance mode for newly created config. Default: dev.

Port options:

--grafana-port <port>           Default: 3000
--otlp-grpc-port <port>         Default: 4317
--otlp-http-port <port>         Default: 4318
--tempo-port <port>             Default: 3200
--loki-port <port>              Default: 3100
--prometheus-port <port>        Default: 9090
--mimir-port <port>             Default: 9009
--minio-console-port <port>     Default: 9001
--redpanda-port <port>          Default: 9092
--redpanda-console-port <port>  Default: 8080
--alloy-port <port>             Default: 12345

Logs options:

--follow, -f       Follow log output. Only for logs.
--tail <lines>     Number of log lines to show. Only for logs.

Modes

The stack has two modes:

  • dev: default mode. Uses simple non-secure credentials, enables anonymous Grafana Admin, and binds public ports to 0.0.0.0.
  • prod: single-node production-oriented mode. Generates random credentials, disables anonymous Grafana, enables the login form, and binds public ports to 127.0.0.1.

Create an instance config without starting containers:

telemetry-stack init ./data/telemetry --mode prod

The instance config is stored here:

<data-dir>/telemetry-stack.env

Generated service configs are stored here:

<data-dir>/generated-configs/

For prod, find the Grafana admin user and password in telemetry-stack.env. The generated configs can also contain credentials, so do not commit the data directory. The prod mode is still single-node and not high availability. Put a reverse proxy, SSH tunnel, or private network in front of it if it needs remote access.

Multiple Instances

The simplest command uses default ports:

telemetry-stack up ./data/telemetry

If another stack already uses those ports, override them:

telemetry-stack up ./data/telemetry-project-b \
  --grafana-port 3300 \
  --otlp-grpc-port 14317 \
  --otlp-http-port 14318 \
  --tempo-port 13200 \
  --loki-port 13100 \
  --prometheus-port 19090 \
  --mimir-port 19009 \
  --minio-console-port 19001 \
  --redpanda-port 19092 \
  --redpanda-console-port 18080 \
  --alloy-port 22345

Project name is derived automatically from the absolute data directory. If you need a specific Docker Compose project name:

telemetry-stack up ./data/telemetry --name my-telemetry

Logs

Follow all stack logs:

telemetry-stack logs ./data/telemetry --follow --tail 100

Follow one service:

telemetry-stack logs ./data/telemetry alloy --follow --tail 100

Useful service names:

alloy
tempo
loki
prometheus
mimir
grafana
minio
redpanda
redpanda-console

Smoke Test

The stack no longer generates fake telemetry by default. To test the log path, send one OTLP HTTP log record to Alloy:

TS="$(date +%s%N)"

curl -sS -X POST http://localhost:4318/v1/logs \
  -H 'Content-Type: application/json' \
  -d "{
    \"resourceLogs\": [{
      \"resource\": {
        \"attributes\": [{
          \"key\": \"service.name\",
          \"value\": {\"stringValue\": \"telemetry-smoke-test\"}
        }]
      },
      \"scopeLogs\": [{
        \"scope\": {\"name\": \"manual-curl\"},
        \"logRecords\": [{
          \"timeUnixNano\": \"${TS}\",
          \"severityText\": \"INFO\",
          \"body\": {\"stringValue\": \"hello from telemetry-stack smoke test\"},
          \"attributes\": [
            {
              \"key\": \"app.value.log_type\",
              \"value\": {\"stringValue\": \"smoke_test\"}
            },
            {
              \"key\": \"app.value.route\",
              \"value\": {\"stringValue\": \"/smoke\"}
            },
            {
              \"key\": \"app.value.count\",
              \"value\": {\"intValue\": \"3\"}
            },
            {
              \"key\": \"app.value.parallel\",
              \"value\": {\"boolValue\": true}
            },
            {
              \"key\": \"app.type.deletedAt\",
              \"value\": {\"stringValue\": \"null\"}
            },
            {
              \"key\": \"app.meta.json\",
              \"value\": {\"stringValue\": \"{\\\"log_type\\\":\\\"smoke_test\\\",\\\"route\\\":\\\"/smoke\\\",\\\"count\\\":3,\\\"parallel\\\":true,\\\"deletedAt\\\":null}\"}
            }
          ]
        }]
      }]
    }]
  }"

Query it from Loki:

curl -G 'http://localhost:3100/loki/api/v1/query_range' \
  --data-urlencode 'query={loki_service_name="telemetry-smoke-test"}' \
  --data-urlencode 'limit=5'

For Grafana trace-to-logs compatibility, the same log is also indexed by the legacy service_name label:

curl -G 'http://localhost:3100/loki/api/v1/query_range' \
  --data-urlencode 'query={service_name="telemetry-smoke-test"}' \
  --data-urlencode 'limit=5'

The returned log should expose application metadata under structured names such as app_value_log_type, app_value_route, app_value_count, app_value_parallel, app_type_deletedAt, and app_meta_json. OpenTelemetry metadata should be under otel_*, for example otel_resource_service_name and otel_scope_name.

Log Metadata in Loki

PrimaFuture telemetry libraries encode application metadata before export. The query-friendly view uses app.value.* for values, app.type.* for type markers such as null or undefined, and app.meta.json for the JSON representation. Loki displays those dotted names as underscore names such as app_value_route.

Legacy applications can still log plain metadata such as count, parallel, or route. Alloy wraps only those unprefixed legacy fields into app.*; it leaves already namespaced app.* metadata untouched.

The stack uses these names:

app_value_*           query-friendly application metadata values
app_type_*            application metadata type markers
app_meta_json         JSON application metadata representation
app_*                 legacy application log attributes wrapped by Alloy
otel_resource_*       OpenTelemetry resource attributes
otel_scope_*          OpenTelemetry instrumentation scope attributes
otel_log_*            OpenTelemetry log record fields
loki_*                explicit Loki index labels
service_name          legacy compatibility label for Grafana trace-to-logs

Examples:

count                 -> app_value_count
route                 -> app_value_route
deletedAt: null       -> app_type_deletedAt = "null"
metadata JSON         -> app_meta_json
service.name          -> otel_resource_service_name
instrumentation scope -> otel_scope_name
trace id              -> otel_log_trace_id

Loki indexes only the low-cardinality resource labels configured in loki.yaml, such as loki_service_name and the compatibility service_name. Application and OpenTelemetry detail fields are stored as structured metadata, not as index labels. Query them after selecting a stream:

{loki_service_name="telemetry-dice-roller-example"}
| trace_id="0242ac120002"
| app_value_route="/roll"

Current Loki versions return structured metadata merged into the stream labels map in query_range responses. This is a Loki API behavior: the fields are still not index labels. Use /loki/api/v1/labels or /loki/api/v1/series to see the real indexed labels.

If a query response is too noisy, reduce returned fields at query time without changing stored data:

{service_name="telemetry-dice-roller-example"}
| trace_id="0242ac120002"
| keep service_name, loki_service_name, trace_id, span_id

Useful URLs

Default ports:

  • Grafana: http://localhost:3000
  • Tempo: http://localhost:3200
  • Loki: http://localhost:3100
  • Prometheus: http://localhost:9090
  • Mimir: http://localhost:9009
  • MinIO Console: http://localhost:9001
  • Redpanda Console: http://localhost:8080
  • OTLP gRPC: localhost:4317
  • OTLP HTTP: http://localhost:4318

Notes

  • dev is for local development and demos.
  • prod is a safer single-node mode, not a full HA observability platform.
  • Grafana runs with anonymous Admin access only in dev mode by default.
  • Grafana runtime data is stored under <data-dir>/grafana.
  • Docker images are pinned by digest in stack/docker-compose.yaml.