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

@youneed/server-plugin-otel

v0.2.0

Published

@youneed/server plugin + middleware: real OpenTelemetry SDK — SERVER spans per request with W3C propagation, http.server metrics, OTLP/HTTP export.

Readme

@youneed/server-plugin-otel

Real OpenTelemetry SDK instrumentation for @youneed/server, built on the shared @youneed/otel core (the only package that imports @opentelemetry/*). One SERVER span per request with W3C traceparent extraction — an incoming trace id continues here, so your service joins an existing distributed trace — plus http.server.* metrics and OTLP/HTTP export.

import { Application } from "@youneed/server";
import { otel } from "@youneed/server-plugin-otel";

const app = Application(MyController).plugin(
  otel({
    serviceName: "my-api",
    endpoint: "http://localhost:4318", // OTLP/HTTP receiver (collector :4318, Tempo, …)
  }),
);
app.listen(3000);

Every request becomes an exported SERVER span (GET /users, kind SERVER, attributes http.request.method / url.path / http.response.status_code), and the span stays active for the whole pipeline — any withSpanAsync / instrumentedFetch from @youneed/otel nests under it automatically. The SDK flushes and shuts down with the server.

Metrics: histogram http.server.request.duration (unit s, attributes http.request.method, http.response.status_code) and up-down counter http.server.active_requests.

Middleware-only usage

If the SDK is started elsewhere (custom bootstrap, tests), install just the middleware — no plugin, no lifecycle:

import { otelMiddleware } from "@youneed/server-plugin-otel";

app.use(otelMiddleware()); // global OTel providers
app.use(otelMiddleware({ handle })); // a specific startNodeOtel() handle

Handlers can reach the live span (and correlation ids) at ctx.state.span:

.get("/users", (ctx) => {
  const span = (ctx.state.span as OtelSpanFacade).otel;
  span.setAttribute("user.count", 3);
  span.addEvent("queried-db");
  return Response.json([/* … */]);
});

otelProvider() — typed this.otel in controllers

The provider form of the integration, for decorator controllers (mirrors @youneed/server-middleware-logger's loggerProvider):

class Users extends Controller("/users", { providers: [otelProvider()] }) {
  @Controller.get("/:id")
  one() {
    // child of the request's SERVER span — no tracer threading
    return this.otel.spanAsync("load-user", async (span) => {
      span.setAttribute("user.id", this.ctx!.params.id);
      this.log.info("loading user", { traceId: this.otel.traceId });
      // …
    });
  }
}

this.otel is a ServerOtelApi — the shared @youneed/otel OtelApi plus the current request's ids:

  • span(name, fn) / spanAsync(name, fn) — child spans, nested under the request's SERVER span automatically (it is active for the whole pipeline);
  • counter(name) / histogram(name) — the process-wide global metrics (useGlobalCounter / useGlobalHistogram), shared with every other level;
  • traceId / spanId — the current request's SERVER-span ids, resolved per access from the ambient request (context() → the ctx.state.span facade the middleware stores).

Like loggerProvider, the instance getter reads the ambient request via context(), so one memoized api object serves every request — and it degrades gracefully where there is no HTTP request at all (e.g. a WebSocket JSON-RPC frame): traceId/spanId are undefined there while spans and metrics keep working. Options: { handle } (a specific startNodeOtel() handle's tracer; default the global one) and { key } (instance member name, default "otel"). The middleware must still be installed for the SERVER span (and thus the ids) to exist.

API

  • otel(opts?) — the ServerPlugin (name: "otel"). opts extends the @youneed/otel NodeOtelConfig (serviceName, endpoint, headers, resourceAttributes, traces, metrics, sampleRatio, metricExportIntervalMs, enabled, plus the test hooks traceExporter / metricExporter / metricReader / batch) with:
    • handle — inject an existing startNodeOtel() handle; the plugin then never flushes/shuts it down (ownership stays with the caller);
    • installMiddleware (default true) — set false to wire otelMiddleware yourself. Exposes .handle and inspect(){ kind: "otel", endpoint }.
  • otelMiddleware(opts?)Middleware; { handle?, tracer? }, falling back to the global getTracer() / getMeter() providers.
  • otelProvider(opts?)ControllerProvider<{ otel: ServerOtelApi }>; { handle?, key? } (default member name "otel"). See the section above.
  • ServerOtelApiOtelApi (child spans + global metrics) plus traceId? / spanId? of the current request's SERVER span.
  • OtelSpanFacade — the ctx.state.span shape: { traceId, spanId, otel }.

Errors: a thrown error records an exception event + ERROR status and is rethrown (the framework turns it into a response); a non-thrown 5xx result sets ERROR without an exception event.

Compatibility

  • The middleware stores { traceId, spanId, otel } at ctx.state.span — the same bag @youneed/server-middleware-logger and -request-logger read structurally, so log correlation (traceId on every line) keeps working. All-zero (unsampled) ids are skipped by those packages, as before.
  • The zero-dependency alternative — @youneed/server-middleware-trace + @youneed/server-plugin-otlp — remains valid when you don't want the OTel SDK in the process; it produces OTel-shaped spans and ships them as OTLP/HTTP JSON without any @opentelemetry/* dependency. Don't run both tracing middlewares on the same app (they share the ctx.state.span slot).

Env vars honored by the underlying SDK setup: OTEL_SDK_DISABLED, OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS.