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 ( {
@@ -122,7 +127,16 @@ describe('EndToEnd', () => {
122127 } ) ;
123128
124129 beforeEach ( async ( ) => {
125- const setupResult = await setup ( ) ;
130+ traceExporter = new InMemorySpanExporter ( ) ;
131+ const sampler = new AlwaysOnSampler ( ) ;
132+ const provider = new NodeTracerProvider ( {
133+ sampler : sampler ,
134+ exporter : traceExporter ,
135+ } ) ;
136+ const setupResult = await setup ( {
137+ tracerProvider : provider ,
138+ enableExtendedTracing : false ,
139+ } ) ;
126140 spanner = setupResult . spanner ;
127141 server = setupResult . server ;
128142 spannerMock = setupResult . spannerMock ;
@@ -138,21 +152,10 @@ describe('EndToEnd', () => {
138152 mock . StatementResult . updateCount ( 1 )
139153 ) ;
140154
141- traceExporter = new InMemorySpanExporter ( ) ;
142- const sampler = new AlwaysOnSampler ( ) ;
143-
144- const provider = new NodeTracerProvider ( {
145- sampler : sampler ,
146- exporter : traceExporter ,
147- } ) ;
148155 provider . addSpanProcessor ( new SimpleSpanProcessor ( traceExporter ) ) ;
149156
150157 const instance = spanner . instance ( 'instance' ) ;
151158 database = instance . database ( 'database' ) ;
152- database . observabilityConfig = {
153- tracerProvider : provider ,
154- enableExtendedTracing : false ,
155- } ;
156159 } ) ;
157160
158161 afterEach ( ( ) => {
@@ -440,3 +443,143 @@ describe('EndToEnd', () => {
440443 } ) ;
441444 } ) ;
442445} ) ;
446+
447+ describe ( 'ObservabilityOptions injection and propagation' , async ( ) => {
448+ const globalTraceExporter = new InMemorySpanExporter ( ) ;
449+ const globalTracerProvider = new NodeTracerProvider ( {
450+ sampler : new AlwaysOnSampler ( ) ,
451+ exporter : globalTraceExporter ,
452+ } ) ;
453+ globalTracerProvider . addSpanProcessor (
454+ new SimpleSpanProcessor ( globalTraceExporter )
455+ ) ;
456+ globalTracerProvider . register ( ) ;
457+
458+ const injectedTraceExporter = new InMemorySpanExporter ( ) ;
459+ const injectedTracerProvider = new NodeTracerProvider ( {
460+ sampler : new AlwaysOnSampler ( ) ,
461+ exporter : injectedTraceExporter ,
462+ } ) ;
463+ injectedTracerProvider . addSpanProcessor (
464+ new SimpleSpanProcessor ( injectedTraceExporter )
465+ ) ;
466+
467+ const observabilityOptions : typeof ObservabilityOptions = {
468+ tracerProvider : injectedTracerProvider ,
469+ enableExtendedTracing : true ,
470+ } ;
471+
472+ const setupResult = await setup ( observabilityOptions ) ;
473+ const spanner = setupResult . spanner ;
474+ const server = setupResult . server ;
475+ const spannerMock = setupResult . spannerMock ;
476+
477+ after ( async ( ) => {
478+ globalTraceExporter . reset ( ) ;
479+ injectedTraceExporter . reset ( ) ;
480+ await globalTracerProvider . shutdown ( ) ;
481+ await injectedTracerProvider . shutdown ( ) ;
482+ spannerMock . resetRequests ( ) ;
483+ spanner . close ( ) ;
484+ server . tryShutdown ( ( ) => { } ) ;
485+ } ) ;
486+
487+ it ( 'Passed into Spanner, Instance and Database' , done => {
488+ // Ensure that the same observability configuration is set on the Spanner client.
489+ assert . deepStrictEqual ( spanner . _observabilityOptions , observabilityOptions ) ;
490+
491+ // Acquire a handle to the Instance through spanner.instance.
492+ const instanceByHandle = spanner . instance ( 'instance' ) ;
493+ assert . deepStrictEqual (
494+ instanceByHandle . _observabilityOptions ,
495+ observabilityOptions
496+ ) ;
497+
498+ // Create the Instance by means of a constructor directly.
499+ const instanceByConstructor = new Instance ( spanner , 'myInstance' ) ;
500+ assert . deepStrictEqual (
501+ instanceByConstructor . _observabilityOptions ,
502+ observabilityOptions
503+ ) ;
504+
505+ // Acquire a handle to the Database through instance.database.
506+ const databaseByHandle = instanceByHandle . database ( 'database' ) ;
507+ assert . deepStrictEqual (
508+ databaseByHandle . _observabilityOptions ,
509+ observabilityOptions
510+ ) ;
511+
512+ // Create the Database by means of a constructor directly.
513+ const databaseByConstructor = new Database (
514+ instanceByConstructor ,
515+ 'myDatabase'
516+ ) ;
517+ assert . deepStrictEqual (
518+ databaseByConstructor . _observabilityOptions ,
519+ observabilityOptions
520+ ) ;
521+
522+ done ( ) ;
523+ } ) ;
524+
525+ it ( 'Propagates spans to the injected not global TracerProvider' , done => {
526+ const instance = spanner . instance ( 'instance' ) ;
527+ const database = instance . database ( 'database' ) ;
528+
529+ database . run ( 'SELECT 1' , ( err , rows ) => {
530+ assert . ifError ( err ) ;
531+
532+ injectedTraceExporter . forceFlush ( ) ;
533+ globalTraceExporter . forceFlush ( ) ;
534+ const spansFromInjected = injectedTraceExporter . getFinishedSpans ( ) ;
535+ const spansFromGlobal = globalTraceExporter . getFinishedSpans ( ) ;
536+
537+ assert . strictEqual (
538+ spansFromGlobal . length ,
539+ 0 ,
540+ 'Expecting no spans from the global exporter'
541+ ) ;
542+ assert . strictEqual (
543+ spansFromInjected . length > 0 ,
544+ true ,
545+ 'Expecting spans from the injected exporter'
546+ ) ;
547+
548+ spansFromInjected . sort ( ( spanA , spanB ) => {
549+ spanA . startTime < spanB . startTime ;
550+ } ) ;
551+ const actualSpanNames : string [ ] = [ ] ;
552+ const actualEventNames : string [ ] = [ ] ;
553+ spansFromInjected . forEach ( span => {
554+ actualSpanNames . push ( span . name ) ;
555+ span . events . forEach ( event => {
556+ actualEventNames . push ( event . name ) ;
557+ } ) ;
558+ } ) ;
559+
560+ const expectedSpanNames = [
561+ 'CloudSpanner.Database.runStream' ,
562+ 'CloudSpanner.Database.run' ,
563+ ] ;
564+ assert . deepStrictEqual (
565+ actualSpanNames ,
566+ expectedSpanNames ,
567+ `span names mismatch:\n\tGot: ${ actualSpanNames } \n\tWant: ${ expectedSpanNames } `
568+ ) ;
569+
570+ const expectedEventNames = [
571+ 'Acquiring session' ,
572+ 'Waiting for a session to become available' ,
573+ 'Acquired session' ,
574+ 'Using Session' ,
575+ ] ;
576+ assert . deepStrictEqual (
577+ actualEventNames ,
578+ expectedEventNames ,
579+ `Unexpected events:\n\tGot: ${ actualEventNames } \n\tWant: ${ expectedEventNames } `
580+ ) ;
581+
582+ done ( ) ;
583+ } ) ;
584+ } ) ;
585+ } ) ;
0 commit comments