UNPKG

@google-cloud/spanner

Version:
218 lines 8.03 kB
"use strict"; /*! * Copyright 2024 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SPAN_NAMESPACE_PREFIX = exports.TRACER_VERSION = exports.TRACER_NAME = void 0; exports.getTracer = getTracer; exports.ensureInitialContextManagerSet = ensureInitialContextManagerSet; exports.startTrace = startTrace; exports.setSpanError = setSpanError; exports.setSpanErrorAndException = setSpanErrorAndException; exports.getActiveOrNoopSpan = getActiveOrNoopSpan; const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const api_1 = require("@opentelemetry/api"); const optedInPII = process.env.SPANNER_ENABLE_EXTENDED_TRACING === 'true'; const TRACER_NAME = 'cloud.google.com/nodejs/spanner'; exports.TRACER_NAME = TRACER_NAME; const TRACER_VERSION = require('../../package.json').version; exports.TRACER_VERSION = TRACER_VERSION; /** * getTracer fetches the tracer from the provided tracerProvider. * @param {TracerProvider} [tracerProvider] optional custom tracer provider * to use for fetching the tracer. If not provided, the global provider will be used. * * @returns {Tracer} The tracer instance associated with the provided or global provider. */ function getTracer(tracerProvider) { if (tracerProvider) { return tracerProvider.getTracer(TRACER_NAME, TRACER_VERSION); } // Otherwise use the global tracer. return api_1.trace.getTracer(TRACER_NAME, TRACER_VERSION); } const SPAN_NAMESPACE_PREFIX = 'CloudSpanner'; // TODO: discuss & standardize this prefix. exports.SPAN_NAMESPACE_PREFIX = SPAN_NAMESPACE_PREFIX; const { AsyncHooksContextManager, } = require('@opentelemetry/context-async-hooks'); /* * This function ensures that async/await works correctly by * checking if context.active() returns an invalid/unset context * and if so, sets a global AsyncHooksContextManager otherwise * spans resulting from async/await invocations won't be correctly * associated in their respective hierarchies. */ function ensureInitialContextManagerSet() { if (api_1.context.active() === api_1.ROOT_CONTEXT) { // If no active context was set previously, trace context propagation cannot // function correctly with async/await for OpenTelemetry // See {@link https://opentelemetry.io/docs/languages/js/context/#active-context} api_1.context.disable(); // Disable any prior contextManager. const contextManager = new AsyncHooksContextManager(); contextManager.enable(); api_1.context.setGlobalContextManager(contextManager); } } /** * startTrace begins an active span in the current active context * and passes it back to the set callback function. Each span will * be prefixed with "cloud.google.com/nodejs/spanner". It is the * responsibility of the caller to invoke [span.end] when finished tracing. * * @returns {Span} The created span. */ function startTrace(spanNameSuffix, config, cb) { if (!config) { config = {}; } return getTracer(config.opts?.tracerProvider).startActiveSpan(SPAN_NAMESPACE_PREFIX + '.' + spanNameSuffix, { kind: api_1.SpanKind.CLIENT }, span => { span.setAttribute(semantic_conventions_1.ATTR_OTEL_SCOPE_NAME, TRACER_NAME); span.setAttribute(semantic_conventions_1.ATTR_OTEL_SCOPE_VERSION, TRACER_VERSION); span.setAttribute('gcp.client.service', 'spanner'); span.setAttribute('gcp.client.version', TRACER_VERSION); span.setAttribute('gcp.client.repo', 'googleapis/nodejs-spanner'); if (config.tableName) { span.setAttribute('db.sql.table', config.tableName); } if (config.dbName) { span.setAttribute('db.name', config.dbName); } if (config.requestTag) { span.setAttribute('request.tag', config.requestTag); } if (config.transactionTag) { span.setAttribute('transaction.tag', config.transactionTag); } const allowExtendedTracing = optedInPII || config.opts?.enableExtendedTracing; if (config.sql && allowExtendedTracing) { const sql = config.sql; if (typeof sql === 'string') { span.setAttribute('db.statement', sql); } else { const stmt = sql; span.setAttribute('db.statement', stmt.sql); } } // If at all the invoked function throws an exception, // record the exception and then end this span. try { return cb(span); } catch (e) { setSpanErrorAndException(span, e); span.end(); // Finally re-throw the exception. throw e; } }); } /** * Sets the span status with err, if non-null onto the span with * status.code=ERROR and the message of err.toString() * * @returns {boolean} to signify if the status was set. */ function setSpanError(span, err) { if (!err || !span) { return false; } let message = ''; if (typeof err === 'object' && 'message' in err) { message = err.message; } else { message = err.toString(); } span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: message, }); return true; } /** * Sets err, if non-null onto the span with * status.code=ERROR and the message of err.toString() * as well as recording an exception on the span. * @param {Span} [span] the subject span * @param {Error} [err] the error whose message to use to record * the span error and the span exception. * * @returns {boolean} to signify if the status and exception were set. */ function setSpanErrorAndException(span, err) { if (setSpanError(span, err)) { span.recordException(err); return true; } return false; } /** * getActiveOrNoopSpan queries the global tracer for the currently active * span and returns it, otherwise if there is no active span available, it'll * simply create a NoopSpan. This is important in the cases where we don't * want to create a new span, such as in sensitive and frequently called code * for which the new spans would be too many and thus pollute the trace, * but yet we'd like to record an important annotation. * * @returns {Span} the non-null span. */ function getActiveOrNoopSpan() { const span = api_1.trace.getActiveSpan(); if (span) { return span; } return new noopSpan(); } /** * noopSpan is a pass-through Span that does nothing and shall not * be exported, nor added into any context. It serves as a placeholder * to allow calls in sensitive areas like sessionPools to transparently * add attributes to spans without lots of ugly null checks. * * It exists because OpenTelemetry-JS does not seem to export the NoopSpan. */ class noopSpan { constructor() { } spanContext() { return api_1.INVALID_SPAN_CONTEXT; } setAttribute(key, value) { return this; } setAttributes(attributes) { return this; } addEvent(name, attributes) { return this; } addLink(link) { return this; } addLinks(links) { return this; } setStatus(status) { return this; } end(endTime) { } isRecording() { return false; } recordException(exc, timeAt) { } updateName(name) { return this; } } //# sourceMappingURL=instrument.js.map