Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/interceptors-opentelemetry/src/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @module
*/
import * as otel from '@opentelemetry/api';
import { Headers, defaultPayloadConverter } from '@temporalio/common';
import { ApplicationFailure, ApplicationFailureCategory, Headers, defaultPayloadConverter } from '@temporalio/common';

/** Default trace header for opentelemetry interceptors */
export const TRACE_HEADER = '_tracer-data';
Expand Down Expand Up @@ -43,8 +43,10 @@ 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)) {
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: err instanceof Error ? err.message : String(err) });
const statusCode = isBenignErr ? otel.SpanStatusCode.UNSET : otel.SpanStatusCode.ERROR;
span.setStatus({ code: statusCode, message: err instanceof Error ? err.message : String(err) });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Being an Error is neither necessary nor sufficient to deduce that message will be present. Prefer this instead:

Suggested change
span.setStatus({ code: statusCode, message: err instanceof Error ? err.message : String(err) });
span.setStatus({ code: statusCode, message: (err as Error).message ?? String(err) });

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

span.recordException(err);
} else {
span.setStatus({ code: otel.SpanStatusCode.OK });
Expand Down
3 changes: 2 additions & 1 deletion packages/test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"build:ts": "tsc --build",
"build:protos": "node ./scripts/compile-proto.js",
"test": "ava ./lib/test-*.js",
"test.watch": "ava --watch ./lib/test-*.js"
"test.watch": "ava --watch ./lib/test-*.js",
"test-otel": "ava ./lib/test-otel.js"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does otel seem important enough to have this separately, or was this a during development thing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does otel seem important enough to have this separately,

No. I'd prefer we don't add such things.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

development thing - i'll remove

},
"ava": {
"timeout": "60s",
Expand Down
13 changes: 11 additions & 2 deletions packages/test/src/activities/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Context } from '@temporalio/activity';
import { ApplicationFailure } from '@temporalio/common';
import { activityInfo, Context } from '@temporalio/activity';
import { ApplicationFailure, ApplicationFailureCategory } from '@temporalio/common';
import { ProtoActivityInput, ProtoActivityResult } from '../../protos/root';
import { cancellableFetch as cancellableFetchInner } from './cancellable-fetch';
import { fakeProgress as fakeProgressInner } from './fake-progress';
Expand Down Expand Up @@ -93,3 +93,12 @@ export async function progressiveSleep(): Promise<void> {
export async function protoActivity(args: ProtoActivityInput): Promise<ProtoActivityResult> {
return ProtoActivityResult.create({ sentence: `${args.name} is ${args.age} years old.` });
}

export async function throwMaybeBenign(): Promise<void> {
if (activityInfo().attempt === 1) {
throw ApplicationFailure.create({ message: 'not benign' });
}
if (activityInfo().attempt === 2) {
throw ApplicationFailure.create({ message: 'benign', category: ApplicationFailureCategory.BENIGN });
}
}
39 changes: 39 additions & 0 deletions packages/test/src/test-otel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,4 +470,43 @@ if (RUN_INTEGRATION_TESTS) {
const exceptionEvents = span.events.filter((event) => event.name === 'exception');
t.is(exceptionEvents.length, 1);
});

test('Otel workflow omits ApplicationError with BENIGN category', async (t) => {
const memoryExporter = new InMemorySpanExporter();
const provider = new BasicTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter));
provider.register();
const tracer = provider.getTracer('test-error-tracer');

const worker = await Worker.create({
workflowsPath: require.resolve('./workflows'),
activities,
taskQueue: 'test-otel-benign-err',
interceptors: {
activity: [
(ctx) => {
return { inbound: new OpenTelemetryActivityInboundInterceptor(ctx, { tracer }) };
},
],
},
});

const client = new WorkflowClient();

await worker.runUntil(
client.execute(workflows.throwMaybeBenignErr, {
taskQueue: 'test-otel-benign-err',
workflowId: uuid4(),
retry: { maximumAttempts: 3 },
})
);

const spans = memoryExporter.getFinishedSpans();
t.is(spans.length, 3);
t.is(spans[0].status.code, SpanStatusCode.ERROR);
t.is(spans[0].status.message, 'not benign');
t.is(spans[1].status.code, SpanStatusCode.UNSET);
t.is(spans[1].status.message, 'benign');
t.is(spans[2].status.code, SpanStatusCode.OK);
});
}
1 change: 1 addition & 0 deletions packages/test/src/workflows/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export * from './swc';
export * from './tasks-and-microtasks';
export * from './text-encoder-decoder';
export * from './throw-async';
export * from './throw-maybe-benign';
export * from './trailing-timer';
export * from './try-to-continue-after-completion';
export * from './two-strings';
Expand Down
11 changes: 11 additions & 0 deletions packages/test/src/workflows/throw-maybe-benign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as workflow from '@temporalio/workflow';
import * as activities from '../activities';

const { throwMaybeBenign } = workflow.proxyActivities<typeof activities>({
startToCloseTimeout: '5s',
retry: { maximumAttempts: 3, backoffCoefficient: 1, initialInterval: 500 },
});

export async function throwMaybeBenignErr(): Promise<void> {
await throwMaybeBenign();
}
Loading