Skip to content

Commit 3cc257e

Browse files
feat: Enable e2e tracing (#2202)
* e2eTracing * context propagation * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Add end to end tracing header and refactore resource header * Add end to end tracing header and refactore resource header --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 92248c1 commit 3cc257e

File tree

16 files changed

+260
-126
lines changed

16 files changed

+260
-126
lines changed

OBSERVABILITY.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,27 @@ const spanner = new Spanner({
8080
observabilityOptions: {
8181
tracerProvider: provider,
8282
enableExtendedTracing: true,
83-
},
83+
}
84+
}),
85+
```
86+
87+
#### End to end tracing
88+
89+
In addition to client-side tracing, you can opt in for end-to-end tracing. End-to-end tracing helps you understand and debug latency issues that are specific to Spanner. Refer [here](https://cloud.google.com/spanner/docs/tracing-overview) for more information.
90+
91+
You can opt-in by either:
92+
93+
* Setting the environment variable `SPANNER_ENABLE_END_TO_END_TRACING=true` before your application is started
94+
* In code, setting `enableEndToEndTracing: true` in your SpannerOptions before creating the Cloud Spanner client
95+
96+
```javascript
97+
const spanner = new Spanner({
98+
projectId: projectId,
99+
observabilityOptions: {
100+
tracerProvider: provider,
101+
enableEndToEndTracing: true,
102+
}
103+
}),
84104
```
85105

86106
#### OpenTelemetry gRPC instrumentation

observability-test/spanner.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import * as mockInstanceAdmin from '../test/mockserver/mockinstanceadmin';
2626
import * as mockDatabaseAdmin from '../test/mockserver/mockdatabaseadmin';
2727
import * as sinon from 'sinon';
2828
import {Row} from '../src/partial-result-stream';
29+
import {END_TO_END_TRACING_HEADER} from '../src/common';
2930
const {
3031
AlwaysOnSampler,
3132
NodeTracerProvider,
@@ -1930,3 +1931,59 @@ describe('Traces for ExecuteStream broken stream retries', () => {
19301931
);
19311932
});
19321933
});
1934+
1935+
describe('End to end tracing headers', () => {
1936+
let server: grpc.Server;
1937+
let spanner: Spanner;
1938+
let spannerMock: mock.MockSpanner;
1939+
let observabilityOptions: typeof ObservabilityOptions;
1940+
1941+
beforeEach(async () => {
1942+
observabilityOptions = {
1943+
enableEndToEndTracing: true,
1944+
};
1945+
1946+
const setupResult = await setup(observabilityOptions);
1947+
spanner = setupResult.spanner;
1948+
server = setupResult.server;
1949+
spannerMock = setupResult.spannerMock;
1950+
});
1951+
1952+
afterEach(async () => {
1953+
spannerMock.resetRequests();
1954+
spanner.close();
1955+
server.tryShutdown(() => {});
1956+
});
1957+
1958+
it('run', done => {
1959+
const instance = spanner.instance('instance');
1960+
const database = instance.database('database');
1961+
database.getTransaction((err, tx) => {
1962+
assert.ifError(err);
1963+
1964+
tx!.run('SELECT 1', async () => {
1965+
tx!.end();
1966+
let metadataCountWithE2EHeader = 0;
1967+
let metadataCountWithTraceParent = 0;
1968+
spannerMock.getMetadata().forEach(metadata => {
1969+
if (metadata.get(END_TO_END_TRACING_HEADER)[0] !== undefined) {
1970+
metadataCountWithE2EHeader++;
1971+
assert.strictEqual(
1972+
metadata.get(END_TO_END_TRACING_HEADER)[0],
1973+
'true'
1974+
);
1975+
}
1976+
if (metadata.get('traceparent')[0] !== undefined) {
1977+
metadataCountWithTraceParent++;
1978+
}
1979+
});
1980+
1981+
// Batch Create Session request and Select 1 request.
1982+
assert.strictEqual(spannerMock.getRequests().length, 2);
1983+
assert.strictEqual(metadataCountWithE2EHeader, 2);
1984+
assert.strictEqual(metadataCountWithTraceParent, 2);
1985+
done();
1986+
});
1987+
});
1988+
});
1989+
});

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"@google-cloud/promisify": "^4.0.0",
6161
"@grpc/proto-loader": "^0.7.0",
6262
"@opentelemetry/api": "^1.9.0",
63+
"@opentelemetry/core": "^1.27.0",
6364
"@opentelemetry/context-async-hooks": "^1.26.0",
6465
"@opentelemetry/semantic-conventions": "^1.25.1",
6566
"@types/big.js": "^6.0.0",

samples/observability-traces.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ async function main(
7676
observabilityOptions: {
7777
tracerProvider: provider,
7878
enableExtendedTracing: true,
79+
enableEndToEndTracing: true,
7980
},
8081
});
8182

src/common.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,37 @@ export const CLOUD_RESOURCE_HEADER = 'google-cloud-resource-prefix';
8181
*/
8282
export const LEADER_AWARE_ROUTING_HEADER = 'x-goog-spanner-route-to-leader';
8383

84+
/*
85+
* END TO END TRACING header.
86+
*/
87+
export const END_TO_END_TRACING_HEADER = 'x-goog-spanner-end-to-end-tracing';
88+
8489
/**
8590
* Add Leader aware routing header to existing header list.
8691
* @param headers Existing header list.
8792
*/
8893
export function addLeaderAwareRoutingHeader(headers: {[k: string]: string}) {
8994
headers[LEADER_AWARE_ROUTING_HEADER] = 'true';
9095
}
96+
97+
/**
98+
* Returns common headers to add.
99+
* @param headers Common header list.
100+
*/
101+
export function getCommonHeaders(
102+
resourceName: string,
103+
enableTracing?: boolean
104+
) {
105+
const headers: {[k: string]: string} = {};
106+
107+
if (
108+
process.env.SPANNER_ENABLE_END_TO_END_TRACING === 'true' ||
109+
enableTracing
110+
) {
111+
headers[END_TO_END_TRACING_HEADER] = 'true';
112+
}
113+
114+
headers[CLOUD_RESOURCE_HEADER] = resourceName;
115+
116+
return headers;
117+
}

src/database.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ import {
9191
ResourceCallback,
9292
Schema,
9393
addLeaderAwareRoutingHeader,
94+
getCommonHeaders,
9495
} from './common';
9596
import {finished, Duplex, Readable, Transform} from 'stream';
9697
import {PreciseDate} from '@google-cloud/precise-date';
@@ -340,7 +341,7 @@ class Database extends common.GrpcServiceObject {
340341
formattedName_: string;
341342
pool_: SessionPoolInterface;
342343
queryOptions_?: spannerClient.spanner.v1.ExecuteSqlRequest.IQueryOptions;
343-
resourceHeader_: {[k: string]: string};
344+
commonHeaders_: {[k: string]: string};
344345
request: DatabaseRequest;
345346
databaseRole?: string | null;
346347
labels?: {[k: string]: string} | null;
@@ -471,11 +472,13 @@ class Database extends common.GrpcServiceObject {
471472
dbName: this.formattedName_,
472473
};
473474

474-
this.resourceHeader_ = {
475-
[CLOUD_RESOURCE_HEADER]: this.formattedName_,
476-
};
477475
this.request = instance.request;
478476
this._observabilityOptions = instance._observabilityOptions;
477+
this.commonHeaders_ = getCommonHeaders(
478+
this.formattedName_,
479+
this._observabilityOptions?.enableEndToEndTracing
480+
);
481+
479482
// eslint-disable-next-line @typescript-eslint/no-explicit-any
480483
this.requestStream = instance.requestStream as any;
481484
this.pool_.on('error', this.emit.bind(this, 'error'));
@@ -582,7 +585,7 @@ class Database extends common.GrpcServiceObject {
582585
method: 'updateDatabase',
583586
reqOpts,
584587
gaxOpts,
585-
headers: this.resourceHeader_,
588+
headers: this.commonHeaders_,
586589
},
587590
callback!
588591
);
@@ -688,7 +691,7 @@ class Database extends common.GrpcServiceObject {
688691
sessionCount: count,
689692
};
690693

691-
const headers = this.resourceHeader_;
694+
const headers = this.commonHeaders_;
692695
if (this._getSpanner().routeToLeaderEnabled) {
693696
addLeaderAwareRoutingHeader(headers);
694697
}
@@ -989,7 +992,7 @@ class Database extends common.GrpcServiceObject {
989992
reqOpts.session.creatorRole =
990993
options.databaseRole || this.databaseRole || null;
991994

992-
const headers = this.resourceHeader_;
995+
const headers = this.commonHeaders_;
993996
if (this._getSpanner().routeToLeaderEnabled) {
994997
addLeaderAwareRoutingHeader(headers);
995998
}
@@ -1215,7 +1218,7 @@ class Database extends common.GrpcServiceObject {
12151218
method: 'dropDatabase',
12161219
reqOpts,
12171220
gaxOpts,
1218-
headers: this.resourceHeader_,
1221+
headers: this.commonHeaders_,
12191222
},
12201223
callback!
12211224
);
@@ -1447,7 +1450,7 @@ class Database extends common.GrpcServiceObject {
14471450
method: 'getDatabase',
14481451
reqOpts,
14491452
gaxOpts,
1450-
headers: this.resourceHeader_,
1453+
headers: this.commonHeaders_,
14511454
},
14521455
(err, resp) => {
14531456
if (resp) {
@@ -1701,7 +1704,7 @@ class Database extends common.GrpcServiceObject {
17011704
method: 'getDatabaseDdl',
17021705
reqOpts,
17031706
gaxOpts,
1704-
headers: this.resourceHeader_,
1707+
headers: this.commonHeaders_,
17051708
},
17061709
// eslint-disable-next-line @typescript-eslint/no-explicit-any
17071710
(err, statements, ...args: any[]) => {
@@ -1781,7 +1784,7 @@ class Database extends common.GrpcServiceObject {
17811784
method: 'getIamPolicy',
17821785
reqOpts,
17831786
gaxOpts: options.gaxOptions,
1784-
headers: this.resourceHeader_,
1787+
headers: this.commonHeaders_,
17851788
},
17861789
(err, resp) => {
17871790
callback!(err, resp);
@@ -1920,7 +1923,7 @@ class Database extends common.GrpcServiceObject {
19201923
method: 'listSessions',
19211924
reqOpts,
19221925
gaxOpts,
1923-
headers: this.resourceHeader_,
1926+
headers: this.commonHeaders_,
19241927
},
19251928
(err, sessions, nextPageRequest, ...args) => {
19261929
if (err) {
@@ -2013,7 +2016,7 @@ class Database extends common.GrpcServiceObject {
20132016
method: 'listSessionsStream',
20142017
reqOpts,
20152018
gaxOpts,
2016-
headers: this.resourceHeader_,
2019+
headers: this.commonHeaders_,
20172020
});
20182021
}
20192022

@@ -2405,7 +2408,7 @@ class Database extends common.GrpcServiceObject {
24052408
method: 'listDatabaseRoles',
24062409
reqOpts,
24072410
gaxOpts,
2408-
headers: this.resourceHeader_,
2411+
headers: this.commonHeaders_,
24092412
},
24102413
(err, roles, nextPageRequest, ...args) => {
24112414
const nextQuery = nextPageRequest!
@@ -2615,7 +2618,7 @@ class Database extends common.GrpcServiceObject {
26152618
method: 'restoreDatabase',
26162619
reqOpts,
26172620
gaxOpts,
2618-
headers: this.resourceHeader_,
2621+
headers: this.commonHeaders_,
26192622
},
26202623
(err, operation, resp) => {
26212624
if (err) {
@@ -3514,7 +3517,7 @@ class Database extends common.GrpcServiceObject {
35143517
method: 'batchWrite',
35153518
reqOpts,
35163519
gaxOpts,
3517-
headers: this.resourceHeader_,
3520+
headers: this.commonHeaders_,
35183521
});
35193522
dataStream
35203523
.once('data', () => (dataReceived = true))
@@ -3796,7 +3799,7 @@ class Database extends common.GrpcServiceObject {
37963799
method: 'setIamPolicy',
37973800
reqOpts,
37983801
gaxOpts: gaxOpts,
3799-
headers: this.resourceHeader_,
3802+
headers: this.commonHeaders_,
38003803
},
38013804
(err, resp) => {
38023805
callback!(err, resp);
@@ -3926,7 +3929,7 @@ class Database extends common.GrpcServiceObject {
39263929
method: 'updateDatabaseDdl',
39273930
reqOpts,
39283931
gaxOpts,
3929-
headers: this.resourceHeader_,
3932+
headers: this.commonHeaders_,
39303933
},
39313934
callback!
39323935
);

0 commit comments

Comments
 (0)