1- import { trace , context , propagation , Span } from '@opentelemetry/api' ;
1+ import {
2+ trace ,
3+ context ,
4+ propagation ,
5+ Span ,
6+ SpanStatusCode ,
7+ } from '@opentelemetry/api' ;
28import { describe , test , expect , vi , assert , beforeEach } from 'vitest' ;
39import { dummySession , readNextResult } from '../testUtil' ;
410
@@ -9,10 +15,7 @@ import {
915} from '@opentelemetry/sdk-trace-base' ;
1016import { W3CTraceContextPropagator } from '@opentelemetry/core' ;
1117import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks' ;
12- import tracer , {
13- createSessionTelemetryInfo ,
14- getPropagationContext ,
15- } from './index' ;
18+ import { createSessionTelemetryInfo , getPropagationContext } from './index' ;
1619import { testMatrix } from '../testUtil/fixtures/matrix' ;
1720import {
1821 cleanupTransports ,
@@ -27,26 +30,27 @@ import { createClient } from '../router/client';
2730import { UNCAUGHT_ERROR_CODE } from '../router' ;
2831import { LogFn } from '../logging' ;
2932
30- describe ( 'Basic tracing tests' , ( ) => {
31- const provider = new BasicTracerProvider ( ) ;
32- provider . addSpanProcessor (
33- new SimpleSpanProcessor ( new InMemorySpanExporter ( ) ) ,
34- ) ;
35- const contextManager = new AsyncHooksContextManager ( ) ;
36- contextManager . enable ( ) ;
37- trace . setGlobalTracerProvider ( provider ) ;
38- context . setGlobalContextManager ( contextManager ) ;
39- propagation . setGlobalPropagator ( new W3CTraceContextPropagator ( ) ) ;
33+ const provider = new BasicTracerProvider ( ) ;
34+ const spanExporter = new InMemorySpanExporter ( ) ;
35+ provider . addSpanProcessor ( new SimpleSpanProcessor ( spanExporter ) ) ;
36+ const contextManager = new AsyncHooksContextManager ( ) ;
37+ contextManager . enable ( ) ;
38+ trace . setGlobalTracerProvider ( provider ) ;
39+ context . setGlobalContextManager ( contextManager ) ;
40+ propagation . setGlobalPropagator ( new W3CTraceContextPropagator ( ) ) ;
4041
42+ describe ( 'Basic tracing tests' , ( ) => {
4143 test ( 'createSessionTelemetryInfo' , ( ) => {
4244 const parentCtx = context . active ( ) ;
45+ const tracer = trace . getTracer ( 'test' ) ;
4346 const span = tracer . startSpan ( 'empty span' , { } , parentCtx ) ;
4447 const ctx = trace . setSpan ( parentCtx , span ) ;
4548
4649 const propCtx = getPropagationContext ( ctx ) ;
4750 expect ( propCtx ?. traceparent ) . toBeTruthy ( ) ;
4851 const session = dummySession ( ) ;
4952 const teleInfo = createSessionTelemetryInfo (
53+ tracer ,
5054 session . id ,
5155 session . to ,
5256 session . from ,
@@ -75,6 +79,7 @@ describe.each(testMatrix())(
7579 const setup = await transport . setup ( { client : opts , server : opts } ) ;
7680 getClientTransport = setup . getClientTransport ;
7781 getServerTransport = setup . getServerTransport ;
82+ spanExporter . reset ( ) ;
7883
7984 return async ( ) => {
8085 await postTestCleanup ( ) ;
@@ -189,5 +194,74 @@ describe.each(testMatrix())(
189194 server,
190195 } ) ;
191196 } ) ;
197+
198+ test ( 'river errors are recorded on handler spans' , async ( ) => {
199+ // setup
200+ const clientTransport = getClientTransport ( 'client' ) ;
201+ const clientMockLogger = vi . fn < LogFn > ( ) ;
202+ clientTransport . bindLogger ( clientMockLogger ) ;
203+ const serverTransport = getServerTransport ( ) ;
204+ const serverMockLogger = vi . fn < LogFn > ( ) ;
205+ serverTransport . bindLogger ( serverMockLogger ) ;
206+ const services = {
207+ fallible : FallibleServiceSchema ,
208+ } ;
209+ const server = createServer ( serverTransport , services ) ;
210+ const client = createClient < typeof services > (
211+ clientTransport ,
212+ serverTransport . clientId ,
213+ ) ;
214+ addPostTestCleanup ( async ( ) => {
215+ await cleanupTransports ( [ clientTransport , serverTransport ] ) ;
216+ } ) ;
217+
218+ const { reqWritable, resReadable } = client . fallible . echo . stream ( { } ) ;
219+
220+ reqWritable . write ( {
221+ msg : 'abc' ,
222+ throwResult : false ,
223+ throwError : false ,
224+ } ) ;
225+ let result = await readNextResult ( resReadable ) ;
226+ expect ( result ) . toStrictEqual ( {
227+ ok : true ,
228+ payload : {
229+ response : 'abc' ,
230+ } ,
231+ } ) ;
232+
233+ // this isn't the first message so doesn't have telemetry info on the message itself
234+ reqWritable . write ( {
235+ msg : 'def' ,
236+ throwResult : false ,
237+ throwError : true ,
238+ } ) ;
239+
240+ result = await readNextResult ( resReadable ) ;
241+ expect ( result ) . toStrictEqual ( {
242+ ok : false ,
243+ payload : {
244+ code : UNCAUGHT_ERROR_CODE ,
245+ message : 'some message' ,
246+ } ,
247+ } ) ;
248+
249+ const spans = spanExporter . getFinishedSpans ( ) ;
250+
251+ const errSpan = spans . find (
252+ ( span ) =>
253+ span . name === 'river.server.fallible.echo' &&
254+ span . status . code === SpanStatusCode . ERROR ,
255+ ) ;
256+ expect ( errSpan ) . toBeTruthy ( ) ;
257+ expect ( errSpan ?. attributes [ 'river.error_code' ] ) . toBe ( UNCAUGHT_ERROR_CODE ) ;
258+ expect ( errSpan ?. attributes [ 'river.error_message' ] ) . toBe ( 'some message' ) ;
259+
260+ await testFinishesCleanly ( {
261+ clientTransports : [ clientTransport ] ,
262+ serverTransport,
263+ server,
264+ } ) ;
265+ } ) ;
192266 } ,
193267) ;
0 commit comments