otel_grpc 0.2.0 copy "otel_grpc: ^0.2.0" to clipboard
otel_grpc: ^0.2.0 copied to clipboard

OpenTelemetry instrumentation for `package:grpc`. Client and server interceptors emitting rpc.* spans, propagating W3C trace context via gRPC metadata, with recursion suppression.

example/example.md

otel_grpc example #

// example/lib/main.dart

import 'package:dartastic_opentelemetry/dartastic_opentelemetry.dart';
// `grpc` also exports a `Server` type; hide it if you import the OTel
// semconv `Server` attribute group in the same file.
import 'package:grpc/grpc.dart';
import 'package:otel_grpc/otel_grpc.dart';

// Generated by protoc from your .proto files:
//   protoc --dart_out=grpc:lib/src/generated -Iprotos protos/greeter.proto
import 'src/generated/greeter.pbgrpc.dart';

Future<void> main() async {
  // 1. Bring up OTel first so trace context is flowing before the
  //    first RPC.
  await OTel.initialize(
    serviceName: 'greeter-client',
  );

  // 2. Attach the client interceptor to the channel. Every unary and
  //    streaming call gets a CLIENT-kind span with the registry-current
  //    rpc.* attributes (rpc.system.name=grpc, fully-qualified
  //    rpc.method, rpc.response.status_code), and W3C
  //    traceparent/tracestate/baggage injected into the call metadata.
  final channel = ClientChannel(
    'api.example.com',
    options: const ChannelOptions(),
    interceptors: [
      OTelGrpcClientInterceptor(
        // Optional: interceptors can't see the channel, so pass the
        // same host/port you gave the channel to get
        // server.address / server.port on the spans. Omit them and
        // the attributes are simply not set - never fabricated.
        serverAddress: 'api.example.com',
        serverPort: 443,
      ),
    ],
  );

  final greeter = GreeterClient(channel);

  // 3. Calls made inside an active span parent into it; the receiving
  //    service joins the same trace via the injected traceparent.
  final tracer = OTel.tracerProvider().getTracer('example');
  await tracer.startActiveSpan(
    name: 'say-hello',
    fn: (span) async {
      final reply = await greeter.sayHello(HelloRequest(name: 'world'));
      print(reply.message);
    },
  );

  await channel.shutdown();
}

Server side #

// example/lib/server.dart

import 'package:dartastic_opentelemetry/dartastic_opentelemetry.dart';
import 'package:grpc/grpc.dart';
import 'package:otel_grpc/otel_grpc.dart';

import 'src/generated/greeter.pbgrpc.dart';

class GreeterService extends GreeterServiceBase {
  @override
  Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async {
    return HelloReply(message: 'Hello, ${request.name}!');
  }
}

Future<void> main() async {
  await OTel.initialize(
    serviceName: 'greeter-server',
  );

  // The class-based ServerInterceptor wraps the whole handler
  // invocation: SERVER-kind spans with real durations and real
  // completion status, parented into the caller's trace via the
  // W3C traceparent extracted from the client metadata.
  final server = Server.create(
    services: [GreeterService()],
    serverInterceptors: [OTelGrpcServerInterceptor()],
  );
  await server.serve(port: 50051);
}

Suppressing instrumentation #

gRPC export over OTLP is itself gRPC. If a ClientChannel you control both carries OTLP export traffic and has OTelGrpcClientInterceptor attached, wrap each export call so the interceptor skips it - otherwise every exported span creates another span:

await runWithoutGrpcInstrumentationAsync(() async {
  await exporter.export(spans);
});
0
likes
160
points
71
downloads

Documentation

API reference

Publisher

verified publisherdartastic.io

Weekly Downloads

OpenTelemetry instrumentation for `package:grpc`. Client and server interceptors emitting rpc.* spans, propagating W3C trace context via gRPC metadata, with recursion suppression.

Homepage
Repository (GitHub)
View/report issues

License

Apache-2.0 (license)

Dependencies

dartastic_opentelemetry, dartastic_opentelemetry_api, grpc

More

Packages that depend on otel_grpc