Closed lucaskatayama closed 2 weeks ago
This is not a bug. It just can't work that way.
In the TS SDK, Workflows execute in a sandboxed environment. Among other things, that implies that Workflow Code can't do any I/O, and therefore, can't interact with a regular Node.js logger.
Instead, log messages emitted using the Workflow context logger are funneled through a Workflow Sink to the Runtime logger. Now, Sinks are an advanced feature of the TS SDK, but essentially, that means that messages are queued internally in the Workflow context for the duration of a Workflow Activation; at the end of an Activation, queued Sink Calls are passed from the Workflow Worker Thread, through a V8 MessagePort, back to the main SDK thread, where they get processed (in this case, passing them to the Runtime logger, which in your case would be an instance of Winston's Logger class).
Note that, at the time that the call is actually passed to the Winston's Logger, execution is no longer contained by the OpenTelemetry call scope, and therefore, the OpenTelemetry Auto Instrumentation can't attach these calls to the parent context.
Note also that the @opentelemetry/auto-instrumentations-node
plugin can't reach into the Workflow sandbox, or get loaded there, as that plugin would bring in dependencies on various non-deterministic-safe modules.
The solution
What you can do instead is to register a WorkflowOutboundInterceptor
on your Workflow Worker that intercepts the getLogAttributes
function and injects the trace_id
and span_id
attributes from there. I think the following should work (untested):
export class OpenTelemetryTraceInLogsWorkflowOutboundInterceptor implements WorkflowOutboundCallsInterceptor {
public getLogAttributes(
input: GetLogAttributesInput,
next: Next<WorkflowOutboundCallsInterceptor, 'getLogAttributes'>
): Record<string, unknown> {
const span = otel.trace.getSpan(otel.context.active());
const spanContext = span?.spanContext();
if (spanContext && otel.isSpanContextValid(spanContext)) {
return next({
trace_id: spanContext.traceId,
span_id: spanContext.spanId,
trace_flags: `0${spanContext.traceFlags.toString(16)}`,
...input,
});
} else {
return next(input);
}
}
}
I certainly agree that it would make sense to be done out of the box by our @temporalio/interceptors-opentelemetry
Workflow Interceptor. I'll therefore keep this ticket open as a feature request.
This is not a bug. It just can't work that way.
In the TS SDK, Workflows execute in a sandboxed environment. Among other things, that implies that Workflow Code can't do any I/O, and therefore, can't interact with a regular Node.js logger.
Instead, log messages emitted using the Workflow context logger are funneled through a Workflow Sink to the Runtime logger. Now, Sinks are an advanced feature of the TS SDK, but essentially, that means that messages are queued internally in the Workflow context for the duration of a Workflow Activation; at the end of an Activation, queued Sink Calls are passed from the Workflow Worker Thread, through a V8 MessagePort, back to the main SDK thread, where they get processed (in this case, passing them to the Runtime logger, which in your case would be an instance of Winston's Logger class).
Note that, at the time that the call is actually passed to the Winston's Logger, execution is no longer contained by the OpenTelemetry call scope, and therefore, the OpenTelemetry Auto Instrumentation can't attach these calls to the parent context.
Note also that the
@opentelemetry/auto-instrumentations-node
plugin can't reach into the Workflow sandbox, or get loaded there, as that plugin would bring in dependencies on various non-deterministic-safe modules.The solution
What you can do instead is to register a
WorkflowOutboundInterceptor
on your Workflow Worker that intercepts thegetLogAttributes
function and injects thetrace_id
andspan_id
attributes from there. I think the following should work (untested):export class OpenTelemetryTraceInLogsWorkflowOutboundInterceptor implements WorkflowOutboundCallsInterceptor { public getLogAttributes( input: GetLogAttributesInput, next: Next<WorkflowOutboundCallsInterceptor, 'getLogAttributes'> ): Record<string, unknown> { const span = otel.trace.getSpan(otel.context.active()); const spanContext = span?.spanContext(); if (spanContext && otel.isSpanContextValid(spanContext)) { return next({ input, trace_id: spanContext.traceId, span_id: spanContext.spanId, trace_flags: `0${spanContext.traceFlags.toString(16)}`, }); } else { return next(input); } } }
I certainly agree that it would make sense to be done out of the box by our
@temporalio/interceptors-opentelemetry
Workflow Interceptor. I'll therefore keep this ticket open as a feature request.
Yep... That WORKED @mjameswh !
I think that piece of code could be in @temporalio/interceptors-opentelemetry
... Or at least documented ...
Thanks!
What are you really trying to do?
Get OpenTelemetry
trace_id
andspan_id
fields on Workflow logs.Describe the bug
The tracing fields from OpenTelemetry (
trace_id
andspan_id
) is not appearing on Workflow logs specifically.It works on Client and Activities.
Calling
worflow.log.error
:Calling
activity.log.error
Which gives
"span_id":"10036d093b04a315"
and"trace_id":"f2731bb716c062624923e55b29f8fcec"
.Minimal Reproduction
interceptors-opentelemetry
sample: https://github.com/temporalio/samples-typescript/tree/main/interceptors-opentelemetrywinston
(https://opentelemetry.io/docs/languages/js/automatic/)custom-logger
using https://github.com/temporalio/samples-typescript/tree/main/custom-loggerexport async function greet(name: string): Promise {
activity.log.error("alskjdbnkajsdbnkjandsjk")
return
Hello, ${name}!
; }❯ temporal --version temporal version 0.12.0 (server 1.23.0) (ui 2.26.2)
{ resource: { attributes: { 'service.name': 'interceptors-sample-worker' } }, traceId: 'd498bbf00762a58fc78c022804644789', <<<<<<<<<< parentId: '38b7d260146c73e6', <<<<<<<< traceState: undefined, name: 'RunWorkflow:example', id: 'aaa81665fd7d1198', kind: 0, timestamp: 1716328235467000, duration: 46000, attributes: { ... { resource: { attributes: { 'service.name': 'interceptors-sample-worker' } }, traceId: 'd498bbf00762a58fc78c022804644789', parentId: 'aaa81665fd7d1198', traceState: undefined, name: 'StartActivity:greet', id: 'aec76af986522d29', kind: 0,