1717import * as assert from 'assert' ;
1818import { grpc } from 'google-gax' ;
1919import { google } from '../protos/protos' ;
20- import { Database , Spanner } from '../src' ;
20+ import { Database , Instance , Spanner } from '../src' ;
2121import { MutationSet } from '../src/transaction' ;
2222import protobuf = google . spanner . v1 ;
2323import * as mock from '../test/mockserver/mockspanner' ;
@@ -35,6 +35,8 @@ const {
3535 AsyncHooksContextManager,
3636} = require ( '@opentelemetry/context-async-hooks' ) ;
3737
38+ const { ObservabilityOptions} = require ( '../src/instrument' ) ;
39+
3840/** A simple result set for SELECT 1. */
3941function createSelect1ResultSet ( ) : protobuf . ResultSet {
4042 const fields = [
@@ -60,7 +62,9 @@ interface setupResults {
6062 spannerMock : mock . MockSpanner ;
6163}
6264
63- async function setup ( ) : Promise < setupResults > {
65+ async function setup (
66+ observabilityOptions ?: typeof ObservabilityOptions
67+ ) : Promise < setupResults > {
6468 const server = new grpc . Server ( ) ;
6569
6670 const spannerMock = mock . createMockSpanner ( server ) ;
@@ -97,6 +101,7 @@ async function setup(): Promise<setupResults> {
97101 servicePath : 'localhost' ,
98102 port,
99103 sslCreds : grpc . credentials . createInsecure ( ) ,
104+ observabilityOptions : observabilityOptions ,
100105 } ) ;
101106
102107 return Promise . resolve ( {
@@ -149,7 +154,7 @@ describe('EndToEnd', () => {
149154
150155 const instance = spanner . instance ( 'instance' ) ;
151156 database = instance . database ( 'database' ) ;
152- database . observabilityConfig = {
157+ database . observabilityOptions_ = {
153158 tracerProvider : provider ,
154159 enableExtendedTracing : false ,
155160 } ;
@@ -440,3 +445,143 @@ describe('EndToEnd', () => {
440445 } ) ;
441446 } ) ;
442447} ) ;
448+
449+ describe ( 'ObservabilityOptions injection and propagation' , async ( ) => {
450+ const globalTraceExporter = new InMemorySpanExporter ( ) ;
451+ const globalTracerProvider = new NodeTracerProvider ( {
452+ sampler : new AlwaysOnSampler ( ) ,
453+ exporter : globalTraceExporter ,
454+ } ) ;
455+ globalTracerProvider . addSpanProcessor (
456+ new SimpleSpanProcessor ( globalTraceExporter )
457+ ) ;
458+ globalTracerProvider . register ( ) ;
459+
460+ const injectedTraceExporter = new InMemorySpanExporter ( ) ;
461+ const injectedTracerProvider = new NodeTracerProvider ( {
462+ sampler : new AlwaysOnSampler ( ) ,
463+ exporter : injectedTraceExporter ,
464+ } ) ;
465+ injectedTracerProvider . addSpanProcessor (
466+ new SimpleSpanProcessor ( injectedTraceExporter )
467+ ) ;
468+
469+ const observabilityOptions : typeof ObservabilityOptions = {
470+ tracerProvider : injectedTracerProvider ,
471+ enableExtendedTracing : true ,
472+ } ;
473+
474+ const setupResult = await setup ( observabilityOptions ) ;
475+ const spanner = setupResult . spanner ;
476+ const server = setupResult . server ;
477+ const spannerMock = setupResult . spannerMock ;
478+
479+ after ( async ( ) => {
480+ globalTraceExporter . reset ( ) ;
481+ injectedTraceExporter . reset ( ) ;
482+ await globalTracerProvider . shutdown ( ) ;
483+ await injectedTracerProvider . shutdown ( ) ;
484+ spannerMock . resetRequests ( ) ;
485+ spanner . close ( ) ;
486+ server . tryShutdown ( ( ) => { } ) ;
487+ } ) ;
488+
489+ it ( 'Passed into Spanner, Instance and Database' , done => {
490+ // Ensure that the same observability configuration is set on the Spanner client.
491+ assert . deepStrictEqual ( spanner . observabilityOptions_ , observabilityOptions ) ;
492+
493+ // Acquire a handle to the Instance through spanner.instance.
494+ const instanceByHandle = spanner . instance ( 'instance' ) ;
495+ assert . deepStrictEqual (
496+ instanceByHandle . observabilityOptions_ ,
497+ observabilityOptions
498+ ) ;
499+
500+ // Create the Instance by means of a constructor directly.
501+ const instanceByConstructor = new Instance ( spanner , 'myInstance' ) ;
502+ assert . deepStrictEqual (
503+ instanceByConstructor . observabilityOptions_ ,
504+ observabilityOptions
505+ ) ;
506+
507+ // Acquire a handle to the Database through instance.database.
508+ const databaseByHandle = instanceByHandle . database ( 'database' ) ;
509+ assert . deepStrictEqual (
510+ databaseByHandle . observabilityOptions_ ,
511+ observabilityOptions
512+ ) ;
513+
514+ // Create the Database by means of a constructor directly.
515+ const databaseByConstructor = new Database (
516+ instanceByConstructor ,
517+ 'myDatabase'
518+ ) ;
519+ assert . deepStrictEqual (
520+ databaseByConstructor . observabilityOptions_ ,
521+ observabilityOptions
522+ ) ;
523+
524+ done ( ) ;
525+ } ) ;
526+
527+ it ( 'Propagates spans to the injected not global TracerProvider' , done => {
528+ const instance = spanner . instance ( 'instance' ) ;
529+ const database = instance . database ( 'database' ) ;
530+
531+ database . run ( 'SELECT 1' , ( err , rows ) => {
532+ assert . ifError ( err ) ;
533+
534+ injectedTraceExporter . forceFlush ( ) ;
535+ globalTraceExporter . forceFlush ( ) ;
536+ const spansFromInjected = injectedTraceExporter . getFinishedSpans ( ) ;
537+ const spansFromGlobal = globalTraceExporter . getFinishedSpans ( ) ;
538+
539+ assert . strictEqual (
540+ spansFromGlobal . length ,
541+ 0 ,
542+ 'Expecting no spans from the global exporter'
543+ ) ;
544+ assert . strictEqual (
545+ spansFromInjected . length > 0 ,
546+ true ,
547+ 'Expecting spans from the injected exporter'
548+ ) ;
549+
550+ spansFromInjected . sort ( ( spanA , spanB ) => {
551+ spanA . startTime < spanB . startTime ;
552+ } ) ;
553+ const actualSpanNames : string [ ] = [ ] ;
554+ const actualEventNames : string [ ] = [ ] ;
555+ spansFromInjected . forEach ( span => {
556+ actualSpanNames . push ( span . name ) ;
557+ span . events . forEach ( event => {
558+ actualEventNames . push ( event . name ) ;
559+ } ) ;
560+ } ) ;
561+
562+ const expectedSpanNames = [
563+ 'CloudSpanner.Database.runStream' ,
564+ 'CloudSpanner.Database.run' ,
565+ ] ;
566+ assert . deepStrictEqual (
567+ actualSpanNames ,
568+ expectedSpanNames ,
569+ `span names mismatch:\n\tGot: ${ actualSpanNames } \n\tWant: ${ expectedSpanNames } `
570+ ) ;
571+
572+ const expectedEventNames = [
573+ 'Acquiring session' ,
574+ 'Waiting for a session to become available' ,
575+ 'Acquired session' ,
576+ 'Using Session' ,
577+ ] ;
578+ assert . deepStrictEqual (
579+ actualEventNames ,
580+ expectedEventNames ,
581+ `Unexpected events:\n\tGot: ${ actualEventNames } \n\tWant: ${ expectedEventNames } `
582+ ) ;
583+
584+ done ( ) ;
585+ } ) ;
586+ } ) ;
587+ } ) ;
0 commit comments