diff --git a/.changeset/fuzzy-stingrays-join.md b/.changeset/fuzzy-stingrays-join.md new file mode 100644 index 0000000..2079907 --- /dev/null +++ b/.changeset/fuzzy-stingrays-join.md @@ -0,0 +1,5 @@ +--- +"@kubiks/otel-resend": minor +--- + +Resend OpenTelemetry initial release diff --git a/images/otel-resend-trace.png b/images/otel-resend-trace.png new file mode 100644 index 0000000..5baa1e7 Binary files /dev/null and b/images/otel-resend-trace.png differ diff --git a/packages/otel-resend/README.md b/packages/otel-resend/README.md index 9d2a120..96398ad 100644 --- a/packages/otel-resend/README.md +++ b/packages/otel-resend/README.md @@ -4,6 +4,10 @@ OpenTelemetry instrumentation for the [Resend](https://resend.com) Node.js SDK. Capture spans for every Resend API call, enrich them with operation metadata, and keep an eye on message delivery from your traces. +![Resend Trace Visualization](https://github.com/kubiks-inc/otel/blob/main/images/otel-resend-trace.png) + +_Visualize your email operations with detailed span information including recipients, subject lines, and delivery status._ + ## Installation ```bash @@ -30,7 +34,7 @@ await resend.emails.send({ }); ``` -`instrumentResend` wraps the instance you already use—no configuration changes +`instrumentResend` wraps the instance you already use — no configuration changes needed. Every SDK call creates a client span with useful attributes. ## What Gets Traced @@ -41,36 +45,24 @@ This instrumentation specifically wraps the `resend.emails.send` method (and its Each span includes: -| Attribute | Description | Example | -| --- | --- | --- | -| `messaging.system` | Constant value `resend` | `resend` | -| `messaging.operation` | Operation type | `send` | -| `resend.resource` | Resource name | `emails` | -| `resend.target` | Full operation target | `emails.send` | -| `resend.to_addresses` | Comma-separated TO addresses | `user@example.com, another@example.com` | -| `resend.cc_addresses` | Comma-separated CC addresses (if present) | `cc@example.com` | -| `resend.bcc_addresses` | Comma-separated BCC addresses (if present) | `bcc@example.com` | -| `resend.recipient_count` | Total number of recipients | `3` | -| `resend.from` | Sender email address | `noreply@example.com` | -| `resend.subject` | Email subject | `Welcome to our service` | -| `resend.template_id` | Template ID (if using templates) | `tmpl_123` | -| `resend.message_id` | Message ID returned by Resend | `email_123` | -| `resend.message_count` | Number of messages sent (always 1 for single sends) | `1` | +| Attribute | Description | Example | +| ------------------------ | --------------------------------------------------- | --------------------------------------- | +| `messaging.system` | Constant value `resend` | `resend` | +| `messaging.operation` | Operation type | `send` | +| `resend.resource` | Resource name | `emails` | +| `resend.target` | Full operation target | `emails.send` | +| `resend.to_addresses` | Comma-separated TO addresses | `user@example.com, another@example.com` | +| `resend.cc_addresses` | Comma-separated CC addresses (if present) | `cc@example.com` | +| `resend.bcc_addresses` | Comma-separated BCC addresses (if present) | `bcc@example.com` | +| `resend.recipient_count` | Total number of recipients | `3` | +| `resend.from` | Sender email address | `noreply@example.com` | +| `resend.subject` | Email subject | `Welcome to our service` | +| `resend.template_id` | Template ID (if using templates) | `tmpl_123` | +| `resend.message_id` | Message ID returned by Resend | `email_123` | +| `resend.message_count` | Number of messages sent (always 1 for single sends) | `1` | The instrumentation captures email addresses and metadata to help with debugging and monitoring, while avoiding sensitive email content. -## Configuration - -```ts -instrumentResend(resend, { - tracerName: "my-service", - tracer: myCustomTracer, // optional: bring your own tracer -}); -``` - -- `tracerName`: Custom name for the tracer (defaults to `@kubiks/otel-resend`) -- `tracer`: Use an existing tracer instance instead of creating a new one - ## License MIT diff --git a/packages/otel-resend/src/index.ts b/packages/otel-resend/src/index.ts index 872af79..550ae75 100644 --- a/packages/otel-resend/src/index.ts +++ b/packages/otel-resend/src/index.ts @@ -4,7 +4,6 @@ import { SpanStatusCode, trace, type Span, - type Tracer, } from "@opentelemetry/api"; import type { Resend, CreateEmailOptions, CreateEmailResponse } from "resend"; @@ -30,11 +29,6 @@ export const SEMATTRS_RESEND_BCC_ADDRESSES = "resend.bcc_addresses" as const; export const SEMATTRS_RESEND_FROM = "resend.from" as const; export const SEMATTRS_RESEND_SUBJECT = "resend.subject" as const; -export interface InstrumentResendConfig { - tracerName?: string; - tracer?: Tracer; -} - interface InstrumentedResend extends Resend { [INSTRUMENTED_FLAG]?: true; } @@ -130,17 +124,13 @@ function finalizeSpan(span: Span, error?: unknown): void { span.end(); } -export function instrumentResend( - client: Resend, - config?: InstrumentResendConfig, -): Resend { +export function instrumentResend(client: Resend): Resend { // Check if already instrumented if ((client as InstrumentedResend)[INSTRUMENTED_FLAG]) { return client; } - const tracerName = config?.tracerName ?? DEFAULT_TRACER_NAME; - const tracer = config?.tracer ?? trace.getTracer(tracerName); + const tracer = trace.getTracer(DEFAULT_TRACER_NAME); const originalSend = client.emails.send.bind(client.emails); const originalCreate = client.emails.create