17
17
import * as assert from 'assert' ;
18
18
import { grpc } from 'google-gax' ;
19
19
import { google } from '../protos/protos' ;
20
- import { Database , Spanner } from '../src' ;
20
+ import { Database , Instance , Spanner } from '../src' ;
21
21
import { MutationSet } from '../src/transaction' ;
22
22
import protobuf = google . spanner . v1 ;
23
23
import * as mock from '../test/mockserver/mockspanner' ;
@@ -35,6 +35,8 @@ const {
35
35
AsyncHooksContextManager,
36
36
} = require ( '@opentelemetry/context-async-hooks' ) ;
37
37
38
+ const { ObservabilityOptions} = require ( '../src/instrument' ) ;
39
+
38
40
/** A simple result set for SELECT 1. */
39
41
function createSelect1ResultSet ( ) : protobuf . ResultSet {
40
42
const fields = [
@@ -60,7 +62,9 @@ interface setupResults {
60
62
spannerMock : mock . MockSpanner ;
61
63
}
62
64
63
- async function setup ( ) : Promise < setupResults > {
65
+ async function setup (
66
+ observabilityOptions ?: typeof ObservabilityOptions
67
+ ) : Promise < setupResults > {
64
68
const server = new grpc . Server ( ) ;
65
69
66
70
const spannerMock = mock . createMockSpanner ( server ) ;
@@ -97,6 +101,7 @@ async function setup(): Promise<setupResults> {
97
101
servicePath : 'localhost' ,
98
102
port,
99
103
sslCreds : grpc . credentials . createInsecure ( ) ,
104
+ observabilityOptions : observabilityOptions ,
100
105
} ) ;
101
106
102
107
return Promise . resolve ( {
@@ -122,7 +127,16 @@ describe('EndToEnd', () => {
122
127
} ) ;
123
128
124
129
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
+ } ) ;
126
140
spanner = setupResult . spanner ;
127
141
server = setupResult . server ;
128
142
spannerMock = setupResult . spannerMock ;
@@ -138,21 +152,10 @@ describe('EndToEnd', () => {
138
152
mock . StatementResult . updateCount ( 1 )
139
153
) ;
140
154
141
- traceExporter = new InMemorySpanExporter ( ) ;
142
- const sampler = new AlwaysOnSampler ( ) ;
143
-
144
- const provider = new NodeTracerProvider ( {
145
- sampler : sampler ,
146
- exporter : traceExporter ,
147
- } ) ;
148
155
provider . addSpanProcessor ( new SimpleSpanProcessor ( traceExporter ) ) ;
149
156
150
157
const instance = spanner . instance ( 'instance' ) ;
151
158
database = instance . database ( 'database' ) ;
152
- database . observabilityConfig = {
153
- tracerProvider : provider ,
154
- enableExtendedTracing : false ,
155
- } ;
156
159
} ) ;
157
160
158
161
afterEach ( ( ) => {
@@ -440,3 +443,143 @@ describe('EndToEnd', () => {
440
443
} ) ;
441
444
} ) ;
442
445
} ) ;
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