Skip to content

OpenTelemetry — vyuh_server_plugin_telemetry_otel

Production TelemetryPlugin backed by the OpenTelemetry SDK. Adapts the framework's TelemetrySink / TelemetrySpan interfaces to OTel tracers, span processors, and exporters. Configures itself from standard OTEL_* environment variables — zero plugin-specific knobs for the common case.

Install

yaml
dependencies:
  vyuh_server_plugin_telemetry_otel:
    hosted: https://pub.vyuh.tech
    version: ^0.1.0

Minimal Wiring

dart
import 'package:vyuh_server/vyuh_server.dart';
import 'package:vyuh_server_plugin_telemetry_otel/vyuh_server_plugin_telemetry_otel.dart';

final runtime = await VyuhServer.bootstrap(
  plugins: [
    OtelTelemetryPlugin(defaultServiceName: 'saas-api'),
  ],
);

The plugin reads OTEL_* env vars during init. When tracing is off (none of the env vars are set), every call is a no-op — handlers don't need to guard their vyuh.telemetry.startSpan(...) calls.

Env Vars

The plugin honors the standard OpenTelemetry environment variables:

VariablePurposeExample
OTEL_ENABLED=trueForce-enabletrue
OTEL_SDK_DISABLED=trueForce-disabletrue
OTEL_SERVICE_NAMEOverride defaultServiceNamesaas-api-prod
OTEL_SERVICE_VERSIONService version attribute2.4.1
OTEL_RESOURCE_ATTRIBUTESExtra resource attrsenv=prod,region=us-east-1
OTEL_TRACES_EXPORTERExporter selectionotlp, jaeger, cloud_trace, console, none
OTEL_EXPORTER_OTLP_TRACES_ENDPOINTTraces endpointhttps://otlp.example.com:4317/v1/traces
OTEL_EXPORTER_OTLP_ENDPOINTAll-signals endpointhttps://otlp.example.com:4317
OTEL_ENDPOINTShortcut for the abovehttps://otlp.example.com:4317
OTEL_TRACES_SAMPLERSamplingalways_on, always_off, parentbased_always_on, parentbased_always_off

Wire whatever your APM vendor documents (Honeycomb, Lightstep, Datadog, Tempo, Jaeger, OTel Collector) — the plugin doesn't care.

Tracing Middleware

The plugin ships a tracingMiddleware() that starts a span per HTTP request:

dart
await runtime.start(
  port: 8080,
  middleware: [
    tracingMiddleware(),      // one span per request
    GlobalMiddleware.cors(),
    GlobalMiddleware.errorHandler(),
    GlobalMiddleware.requestLogger(),
  ],
);

Each request span carries:

  • http.method, http.route, http.status_code
  • client.address (from req.clientContext.ipAddress)
  • user_agent.original
  • vyuh.request_id (correlates with the response's X-Request-Id)

Child spans started via vyuh.telemetry.startSpan(...) automatically nest under the request span — no parent threading required.

In Handlers

dart
Future<Response> placeOrder(Request req) async {
  final span = vyuh.telemetry.startSpan(
    'order.place',
    attrs: {'tenant_id': req.actor.tenantId},
  );
  try {
    final order = await _persist(req);
    span.setAttribute('order_id', order.id);
    span.setAttribute('total_cents', order.totalCents);
    vyuh.telemetry.counter('orders.created', delta: 1);
    return jsonResponse(body: {'success': true, 'data': order.toJson()});
  } catch (e, st) {
    span.recordException(e, st);
    span.end(success: false);
    rethrow;
  } finally {
    span.end();
  }
}

span.end(success: false) records Status.error so SLO dashboards can split failures cleanly.

Custom Config Object

For non-standard deployments, bypass env vars with an explicit OtelConfig:

dart
final cfg = OtelConfig(
  serviceName: 'saas-api',
  serviceVersion: '2.4.1',
  exporter: OtelExporter.otlp,
  endpoint: 'https://otlp.example.com:4317',
  resourceAttributes: {
    'env': 'prod',
    'region': 'us-east-1',
  },
  sampler: OtelSampler.parentBasedAlwaysOn,
);

OtelTelemetryPlugin(
  defaultServiceName: 'saas-api',
  config: cfg,
);

Shutdown

The plugin's dispose flushes pending spans before the SDK provider shuts down. The framework's graceful-shutdown wiring calls dispose automatically on SIGINT/SIGTERM — no in-flight spans are lost under a kubectl rollout.

Where to Go Next