Skip to content

Tokenbooks/zenstack-otel

Repository files navigation

@tokenbooks/zenstack-otel

OpenTelemetry runtime plugin for ZenStack ORM.

This package wraps ZenStack's runtime plugin hooks and emits spans for:

  • ORM query operations via onQuery
  • Kysely query execution via onKyselyQuery

It is a runtime plugin for ZenStack ORM, not a zen generate CLI plugin.

Install

pnpm add @tokenbooks/zenstack-otel @opentelemetry/api @zenstackhq/orm

Usage

Install the plugin either through the client constructor's plugins option or by calling $use.

import { Pool } from 'pg';
import { ZenStackClient } from '@zenstackhq/orm';
import { PostgresDialect } from '@zenstackhq/orm/dialects/postgres';
import { createZenStackOtelPlugin } from '@tokenbooks/zenstack-otel';
import { schema } from './schema';

const db = new ZenStackClient(schema, {
  dialect: new PostgresDialect({
    pool: new Pool({
      connectionString: process.env.DATABASE_URL,
    }),
  }),
  plugins: [
    createZenStackOtelPlugin({
      tracerName: 'api-db',
    }),
  ],
});

Or:

const tracedDb = db.$use(createZenStackOtelPlugin());

Emitted spans

createZenStackOtelPlugin() creates spans with these names:

  • zenstack.<Model>.<operation> for ORM query hooks
  • zenstack.kysely.query for Kysely query hooks

It sets these attributes when available:

  • db.orm=zenstack
  • db.zenstack.model
  • db.zenstack.operation
  • db.zenstack.kysely.kind
  • db.statement when captureStatement is enabled
  • db.zenstack.kysely.parameter_count when captureStatement is enabled
  • db.zenstack.kysely.parameters when both captureStatement and captureParameters are enabled

API

createZenStackOtelPlugin(options?)

Creates a ZenStack runtime plugin instance.

Options:

  • pluginId?: string overrides the default plugin id (zenstack-otel)
  • tracerName?: string overrides the tracer name passed to trace.getTracer
  • captureStatement?: boolean attaches the compiled Kysely SQL text with placeholders to Kysely spans
  • captureParameters?: boolean also attaches compiled bind parameters as JSON; requires captureStatement: true
  • spanLifecycle?: SpanLifecycle injects a custom span runner, useful for tests or custom wrappers

new ZenStackOtelPlugin(options).create()

Class-based API for callers who prefer explicit construction over the factory function.

getDefaultZenStackOtelPlugin()

Returns a lazily cached default plugin instance.

SQL capture

By default, the plugin records logical ZenStack attributes only. If you enable captureStatement, the Kysely hook recompiles the operation node to recover dialect-specific SQL text with placeholders. If you also enable captureParameters, it serializes the compiled bind parameters into a separate span attribute.

Example:

createZenStackOtelPlugin({
  tracerName: 'api-db',
  captureStatement: true,
  captureParameters: true,
});

That produces Kysely span attributes like:

  • db.statement = select * from "User" where "id" = $1
  • db.zenstack.kysely.parameter_count = 1
  • db.zenstack.kysely.parameters = ["user-1"]

captureParameters requires captureStatement: true.

This extra compilation work can add overhead on hot paths. ZenStack's current onKyselyQuery hook exposes the Kysely operation node, not ZenStack's already-compiled query object, so there is no stable public hook payload to reuse instead of recompiling.

Development notes

ZenStack's plugin API is currently documented as a preview feature, so this package tracks the ZenStack v3 runtime plugin surface described here:

Release

pnpm release:patch
pnpm release:minor
pnpm release:major

The release script runs lint, tests, and build, bumps package.json, tags vX.Y.Z, and pushes to main. GitHub Actions publishes to npm from the tag.

About

OpenTelemetry runtime plugin for ZenStack ORM.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors