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

@biorate/opentelemetry

v2.2.4

Published

OpenTelemetry integration

Readme

@biorate/opentelemetry

OpenTelemetry integration with auto-instrumentation, decorator-based tracing, data masking, and span attribute filtering.

How it works

The package sets up a fully configured OpenTelemetry NodeSDK on import — auto-instrumentations, resource detectors (AWS, GCP, Alibaba, container, host), OTLP trace/metric exporters, and a configurable Prometheus or console metric reader.

Two decorators provide declarative tracing:

  • @scope() — class-level decorator: assigns an OpenTelemetry Tracer to the class via Reflect metadata.
  • @span() — method-level decorator: wraps a method in an active span, recording class, method, arguments, and result as span attributes. Arguments and result are serialised via json-stringify-safe.

A custom DataMaskingProcessor (extends SimpleSpanProcessor) applies two layers of redaction before span export:

  1. String-level masking — regex-based (email, phone, card numbers via @biorate/masquerade).
  2. JSON-level masking — parsed JSON attributes are run through maskdata rules (field-name‑based).

Additionally, @span() supports an exclude option that filters data before it ever reaches the span — preventing large or sensitive payloads from being recorded at all.

Installation

pnpm add @biorate/opentelemetry

Usage

Minimal setup

Set the required environment variables and import the package. The SDK starts automatically on import.

process.env.OTEL_SERVICE_NAME = 'my-app';
process.env.OTEL_TRACES_SAMPLER = 'always_on';
process.env.OTEL_TRACES_SAMPLER_ARG = '1';
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4317';

import { scope, span } from '@biorate/opentelemetry';

@scope('1.0')
class MyService {
  @span()
  public greet(name: string) {
    return `Hello, ${name}!`;
  }
}

const svc = new MyService();
svc.greet('world');

The exported span will contain the following attributes:

| Attribute | Value | | ------------- | ------------------------------- | | class | "MyService" | | method | "greet" | | arguments | '["world"]' | | result | '"Hello, world!"' | | SpanKind | "SERVER" |

@scope — assigning a tracer

@scope()
class A {}
// tracer name = 'A', version undefined

@scope('2.0')
class B {}
// tracer name = 'B', version '2.0'

@scope('2.0', 'my-domain')
class C {}
// tracer name = 'my-domain', version '2.0'

@span — method tracing

@span()
public syncMethod(a: number, b: number) {
  return a + b;
}

@span({ name: 'custom-span-name' })
public namedSpan() { /* ... */ }

@span({ spanKind: 'CLIENT' })
public clientSpan() { /* ... */ }

The name option overrides the span name (defaults to the method name). spanKind sets the SpanKind attribute (default SERVER).

Async methods are handled transparently — the span is kept open until the promise settles:

@span()
public async query(sql: string) {
  const result = await db.execute(sql);
  return result;
}

@span({ exclude }) — filtering span data

The exclude option accepts an array of JSON path patterns that prevent matching fields from being recorded in the span. This avoids storing large or sensitive payloads (binary blobs, tokens, passwords) in tracing backends.

Patterns are relative to the span attribute name (arguments or result):

| Pattern | Effect | | ------- | ------ | | 'arguments' | Entire arguments attribute is omitted | | 'arguments.0.password' | password field of the first argument is removed | | 'arguments.*.creditCard' | creditCard field at any argument is removed (* — single segment wildcard) | | 'arguments.**.token' | token at any nesting depth is removed (** — multi‑level globstar) | | 'result' | Entire result attribute is omitted | | 'result.token' | token field in the result is removed |

The original method arguments and return value are not mutated — filtering is applied on a deep clone.

@span({ exclude: ['arguments.password', 'result.token'] })
public authenticate(password: string) {
  return { token: 'jwt...', user: { id: 1 } };
}
// arguments written to span: '[password]' → password field value IS the string,
// but since password is at index 0 and pattern is 'arguments.password',
// it matches only if an argument *object* has a 'password' key.
// To match a positional argument, use 'arguments.0' or 'arguments.*'.

Practical example — exclude a field nested in an argument object:

@span({ exclude: ['arguments.0.password'] })
public authenticate(credentials: { password: string; login: string }) {
  return { session: 'abc' };
}
// span.arguments → '[{"login":"user"}]' (password removed)

Exclude result fields:

@span({ exclude: ['result.token', 'result.nested.secret'] })
public fetchData() {
  return {
    token: 's3cr3t',
    nested: { secret: 'classified', visible: 'ok' },
    normal: 'hello',
  };
}
// span.result → '{"nested":{"visible":"ok"},"normal":"hello"}'

Wildcard patterns — single (*) and multi‑level (**):

@span({ exclude: ['arguments.*.creditCard'] })
public pay(users: Array<{ name: string; creditCard: string }>) {
  // ...
}

@span({ exclude: ['arguments.**.creditCard'] })
public processNested(data: { deep: { creditCard: string } }) {
  // ...
}

Combined with data masking:

// exclude prevents large payloads from being stored in the span,
// DataMaskingProcessor masks remaining sensitive fields on export.
@span({ exclude: ['arguments.binaryData', 'result.token'] })
public handleRequest(body: { binaryData: Buffer; token: string }) {
  // ...
}

Data masking

The built-in DataMaskingProcessor is automatically added to the span processor chain. Configure @biorate/masquerade anywhere in your application startup:

import { CardMask, EmailMask, Masquerade, PhoneMask } from '@biorate/masquerade';

Masquerade.configure({
  maskJSON2: {
    emailFields: ['result.email', 'arguments.*'],
    passwordFields: ['password'],
  },
});

Masquerade.use(EmailMask).use(PhoneMask).use(CardMask);

At export time every string attribute value is scanned for emails, phones, and card numbers via regex, and JSON-parsed attributes are masked by field name rules from maskJSON2.

Configuration

OpenTelemetry environment variables

| Variable | Default | Description | | -------- | ------- | ----------- | | OTEL_SERVICE_NAME | — | Logical service name (resource attribute) | | OTEL_TRACES_SAMPLER | — | Sampler: always_on, always_off, traceidratio, parentbased_traceidratio | | OTEL_TRACES_SAMPLER_ARG | — | Sampler argument (e.g. ratio for traceidratio) | | OTEL_PROPAGATORS | 'tracecontext,baggage' | Propagators list | | OTEL_EXPORTER_OTLP_ENDPOINT | — | OTLP gRPC endpoint for traces and metrics | | OTEL_LOG_LEVEL | — | OpenTelemetry log level | | OTEL_METRICS_EXPORTER | 'none' | Metrics exporter: otlp, prometheus, console, none | | OTEL_BSP_SCHEDULE_DELAY | 5000 | Batch span processor schedule delay (ms) | | OTEL_BSP_EXPORT_TIMEOUT | 30000 | Batch span processor export timeout (ms) | | OTEL_EXCLUDED_DETECTORS | — | Comma-separated resource detector names to skip |

Resource detectors

Detectors are loaded from environment, container, and cloud metadata. Exclude any detector by adding its name to OTEL_EXCLUDED_DETECTORS:

OTEL_EXCLUDED_DETECTORS=gcpDetector,alibabaCloudEcsDetector

Available detectors:

| Name | Source | | ---- | ------ | | containerDetector | Container metadata | | envDetector | OTEL_RESOURCE_ATTRIBUTES | | hostDetector | Host info | | osDetector | OS info | | processDetector | Process info | | alibabaCloudEcsDetector | Alibaba Cloud ECS | | awsEksDetector | AWS EKS | | awsEc2Detector | AWS EC2 | | gcpDetector | GCP |

Resource attributes (Kubernetes example)

OTEL_RESOURCE_ATTRIBUTES='
  k8s.cluster.name=prod,
  k8s.container.name=app,
  k8s.deployment.name=app,
  k8s.namespace.name=default,
  k8s.node.name=node-1,
  k8s.pod.name=app-7d9f8c6b4-abcde,
  k8s.replicaset.name=app-7d9f8c6b4,
  service.instance.id=app.prod.1,
  service.namespace=production,
  service.version=1.0.0'

Span attributes

Every method decorated with @span() produces a span with the following attributes (unless excluded):

| Attribute | Type | Description | | ------------ | -------- | ----------- | | class | string | Decorated class name (target.constructor.name) | | method | string | Method name (propertyKey) | | arguments | string | JSON.stringify-safe serialisation of all arguments | | result | string | JSON.stringify-safe serialisation of the return value | | SpanKind | string | Kind hint: SERVER, CLIENT, PRODUCER, CONSUMER, INTERNAL |

Exclude pattern reference

Pattern matching uses micromatch with / as path separator.

| Wildcard | Meaning | | -------- | ------- | | * | Matches any single path segment | | ** | Matches zero or more path segments | | ?(...) | Matches zero or one of the given patterns | | +(...) | Matches one or more of the given patterns | | @(...) | Matches exactly one of the given patterns | | !(...) | Matches anything except one of the given patterns |

For full glob syntax see the micromatch documentation.

Architecture

Import @biorate/opentelemetry
  └─ NodeSDK.start()
       ├─ auto-instrumentations (HTTP, gRPC, DB, ...)
       ├─ resource detectors (env, host, container, cloud)
       ├─ OTLPTraceExporter
       ├─ DataMaskingProcessor (masks span attributes on export)
       └─ metricReader (OTLP / Prometheus / Console)

@scope('v') class C { @span() m() {} }
  ├─ @scope:  trace.getTracer(C.name, 'v') → Reflect.defineMetadata(tracer, C)
  └─ @span:   descriptor.value = wrapper
                ├─ tracer.startActiveSpan(name, span => { ... })
                ├─ span.setAttribute('arguments', stringify(filterByPaths(args, ...)))
                ├─ const result = method.apply(this, args)
                ├─ span.setAttribute('result', stringify(filterByPaths(result, ...)))
                └─ span.end()

DataMaskingProcessor.onEnd(span)
  ├─ forEach attribute: Masquerade.processString (regex masking)
  ├─ if maskJSON2 enabled: Masquerade.processJSON (field-name masking)
  └─ super.onEnd(span) → export

Learn

  • Documentation can be found here - docs.

Release History

See the CHANGELOG

License

MIT

Copyright (c) 2021-present Leonid Levkin (llevkin)