-
Notifications
You must be signed in to change notification settings - Fork 141
feat(@temporalio/interceptors-opentelemetry): implement all interceptors #1835
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
dad213a
3544bfe
a3745ba
b9e320c
6ed1f58
b51d25e
16add47
1b86c7f
dc415c2
788f637
af26de1
677f95e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,20 @@ import { | |
| export const TRACE_HEADER = '_tracer-data'; | ||
| /** As in workflow run id */ | ||
| export const RUN_ID_ATTR_KEY = 'run_id'; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ruby OTEL uses
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh? Have you compared with other SDKs, beside Ruby? We'd ideally want cross-SDKs compatibility of OTel tracing, as a customer could be operating different languages within a single Temporal application. Not saying we'll prioritize, of course, but at the very least we should settle on what names we want to normalize to across the board, so that we eventually converge to something consistent.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I kept
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair. We may however consider changing these while transitioning to OTel 2. I would mind being breaking at that point. |
||
| /** As in workflow id */ | ||
| export const WORKFLOW_ID_ATTR_KEY = 'temporalWorkflowId'; | ||
| /** As in activity id */ | ||
| export const ACTIVITY_ID_ATTR_KEY = 'temporalActivityId'; | ||
| /** As in update id */ | ||
| export const UPDATE_ID_ATTR_KEY = 'temporalUpdateId'; | ||
| /** As in termination reason */ | ||
| export const TERMINATE_REASON_ATTR_KEY = 'temporalTerminateReason'; | ||
| /** As in Nexus service */ | ||
| export const NEXUS_SERVICE_ATTR_KEY = 'temporalNexusService'; | ||
| /** As in Nexus operation */ | ||
| export const NEXUS_OPERATION_ATTR_KEY = 'temporalNexusOperation'; | ||
| /** As in Nexus endpoint */ | ||
| export const NEXUS_ENDPOINT_ATTR_KEY = 'temporalNexusEndpoint'; | ||
|
|
||
| const payloadConverter = defaultPayloadConverter; | ||
|
|
||
|
|
@@ -48,20 +62,41 @@ async function wrapWithSpan<T>( | |
| span.setStatus({ code: otel.SpanStatusCode.OK }); | ||
| return ret; | ||
| } catch (err: any) { | ||
| const isBenignErr = err instanceof ApplicationFailure && err.category === ApplicationFailureCategory.BENIGN; | ||
| if (acceptableErrors === undefined || !acceptableErrors(err)) { | ||
| const statusCode = isBenignErr ? otel.SpanStatusCode.UNSET : otel.SpanStatusCode.ERROR; | ||
| span.setStatus({ code: statusCode, message: (err as Error).message ?? String(err) }); | ||
| span.recordException(err); | ||
| } else { | ||
| span.setStatus({ code: otel.SpanStatusCode.OK }); | ||
| } | ||
| maybeAddErrorToSpan(err, span, acceptableErrors); | ||
| throw err; | ||
| } finally { | ||
| span.end(); | ||
| } | ||
| } | ||
|
|
||
| function wrapWithSpanSync<T>( | ||
| span: otel.Span, | ||
| fn: (span: otel.Span) => T, | ||
| acceptableErrors?: (err: unknown) => boolean | ||
| ): T { | ||
| try { | ||
| const ret = fn(span); | ||
| span.setStatus({ code: otel.SpanStatusCode.OK }); | ||
| return ret; | ||
| } catch (err: any) { | ||
| maybeAddErrorToSpan(err, span, acceptableErrors); | ||
| throw err; | ||
| } finally { | ||
| span.end(); | ||
| } | ||
| } | ||
|
|
||
| function maybeAddErrorToSpan(err: any, span: otel.Span, acceptableErrors?: (err: unknown) => boolean): void { | ||
| const isBenignErr = err instanceof ApplicationFailure && err.category === ApplicationFailureCategory.BENIGN; | ||
| if (acceptableErrors === undefined || !acceptableErrors(err)) { | ||
| const statusCode = isBenignErr ? otel.SpanStatusCode.UNSET : otel.SpanStatusCode.ERROR; | ||
| span.setStatus({ code: statusCode, message: (err as Error).message ?? String(err) }); | ||
| span.recordException(err); | ||
| } else { | ||
| span.setStatus({ code: otel.SpanStatusCode.OK }); | ||
| } | ||
| } | ||
|
|
||
| export interface InstrumentOptions<T> { | ||
| tracer: otel.Tracer; | ||
| spanName: string; | ||
|
|
@@ -70,6 +105,8 @@ export interface InstrumentOptions<T> { | |
| acceptableErrors?: (err: unknown) => boolean; | ||
| } | ||
|
|
||
| export type InstrumentOptionsSync<T> = Omit<InstrumentOptions<T>, 'fn'> & { fn: (span: otel.Span) => T }; | ||
|
|
||
| /** | ||
| * Wraps `fn` in a span which ends when function returns or throws | ||
| */ | ||
|
|
@@ -87,3 +124,12 @@ export async function instrument<T>({ | |
| } | ||
| return await tracer.startActiveSpan(spanName, async (span) => await wrapWithSpan(span, fn, acceptableErrors)); | ||
| } | ||
|
|
||
| export function instrumentSync<T>({ tracer, spanName, fn, context, acceptableErrors }: InstrumentOptionsSync<T>): T { | ||
| if (context) { | ||
| return otel.context.with(context, () => { | ||
| return tracer.startActiveSpan(spanName, (span) => wrapWithSpanSync(span, fn, acceptableErrors)); | ||
| }); | ||
| } | ||
| return tracer.startActiveSpan(spanName, (span) => wrapWithSpanSync(span, fn, acceptableErrors)); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.