diff --git a/lib/index.js b/lib/index.js index 4675553..c33117b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -47,12 +47,6 @@ module.exports = function () { const resource = getResource() - // REVISIT: better way to make available? - cds._telemetry = { - name: resource.attributes[SEMRESATTRS_SERVICE_NAME], - version: resource.attributes[SEMRESATTRS_SERVICE_VERSION] - } - /* * setup tracing */ diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 0b0367c..10654b1 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -139,12 +139,10 @@ module.exports = resource => { tracerProvider.addSpanProcessor(spanProcessor) } - // REVISIT: better way to set/ pass tracer? - cds._telemetry.tracer = trace.getTracer('@cap-js/telemetry', require('../../package.json').version) - - // REVISIT: only start tracing once served + // create tracer but only start tracing once served + const tracer = trace.getTracer('@cap-js/telemetry', require('../../package.json').version) cds.on('served', () => { - cds._telemetry.tracer._active = true + cds._tracer = tracer }) // clear sap passport for new tx diff --git a/lib/tracing/trace.js b/lib/tracing/trace.js index c76cbff..3b62bdf 100644 --- a/lib/tracing/trace.js +++ b/lib/tracing/trace.js @@ -47,22 +47,20 @@ const ADJUST_ROOT_NAME = adjust_root_name && adjust_root_name !== 'false' if (HRTIME) cds.on('listening', ({ server }) => server.on('request', req => (req.__hrnow = _hrnow()))) function _getParentSpan() { + const activeSpan = otel.trace.getActiveSpan() + let parent + if (!cds.context) return if (!cds.context._otelctx) { cds.context._otelKey = otel.createContextKey(cds.context.id) cds.context._otelctx = otel.context.active() - const parent = otel.trace.getSpan(cds.context._otelctx) - if (parent && !parent.__adjusted) { - parent.__adjusted = true - // root span gets request attributes - _setAttributes(parent, _getRequestAttributes()) - if (HRTIME) parent.startTime = cds.context.http?.req?.__hrnow || _hrnow() - if (ADJUST_ROOT_NAME && parent.attributes[SEMATTRS_HTTP_TARGET]) - parent.name += ' ' + parent.attributes[SEMATTRS_HTTP_TARGET] - } + /* const */ parent = otel.trace.getSpan(cds.context._otelctx) if (!parent?._is_async) cds.context._otelctx.setValue(cds.context._otelKey, parent) } - return otel.context.active().getValue(cds.context._otelKey) || cds.context._otelctx.getValue(cds.context._otelKey) + + const r1 = otel.context.active().getValue(cds.context._otelKey) + const r2 = cds.context._otelctx.getValue(cds.context._otelKey) + return r1 || r2 } function _getSpanName(arg, fn, targetObj) { @@ -123,7 +121,7 @@ function _getStaticAttributes(fn) { } function _getRequestAttributes() { - if (!cds.context?.http?.req) return + if (!cds.context.http?.req) return if (!cds.context.http.req.__attributes) { const req = cds.context.http.req @@ -195,8 +193,21 @@ function _setAttributes(span, attributes) { } function trace(name, fn, targetObj, args, options = {}) { - // REVISIT: only start tracing once served - if (!cds._telemetry.tracer._active) return fn.apply(targetObj, args) + // only start tracing once served and there is a context + if (!cds._tracer || !cds.context) return fn.apply(targetObj, args) + + const activeSpan = otel.trace.getActiveSpan() + + // if the current request matches instrumentation-http's ignoreIncomingPaths -> abort + if (cds.context.http?.req && !activeSpan) return fn.apply(targetObj, args) + + // root span gets request attributes + if (cds.context.http?.req && !activeSpan.__adjusted) { + activeSpan.__adjusted = true + _setAttributes(activeSpan, _getRequestAttributes()) + if (HRTIME) activeSpan.startTime = cds.context.http?.req?.__hrnow || _hrnow() + if (ADJUST_ROOT_NAME && activeSpan.attributes[SEMATTRS_HTTP_TARGET]) activeSpan.name += ' ' + activeSpan.attributes[SEMATTRS_HTTP_TARGET] + } /* * create span @@ -220,13 +231,16 @@ function trace(name, fn, targetObj, args, options = {}) { } let spanName = typeof name === 'string' ? name : _getSpanName(name, fn, targetObj) if (spanName.length > 80 && _truncate_span_name !== false) spanName = spanName.substring(0, 79) + '…' - // REVISIT: better way to get tracer? - const span = cds._telemetry.tracer.startSpan(spanName, spanOptions, ctx) + + const span = cds._tracer.startSpan(spanName, spanOptions, ctx) + if (name.event?.match(/^cds\.spawn/) || name?.phase === 'emit') span._is_async = true - // REVISIT: can we determine this earlier? - // if the current request matches instrumentation-http's ignoreIncomingPaths, we get a NonRecordingSpan -> abort - if (span.constructor.name === 'NonRecordingSpan') return fn.apply(targetObj, args) + // // if the current request matches instrumentation-http's ignoreIncomingPaths, we get a NonRecordingSpan -> abort + // if (span.constructor.name === 'NonRecordingSpan') { + // debugger + // return fn.apply(targetObj, args) + // } /* * set attributes on span @@ -238,7 +252,7 @@ function trace(name, fn, targetObj, args, options = {}) { if (cds.requires.multitenancy) { const creds = targetObj?.dbc?._connection?._settings || targetObj?.dbc?._creds //> hdb vs. @sap/hana-client - _setAttributes(span, _getStaticDBAttributes(cds.context?.tenant, creds)) + _setAttributes(span, _getStaticDBAttributes(cds.context.tenant, creds)) } else { _setAttributes(span, _getStaticDBAttributes()) } @@ -276,24 +290,20 @@ function trace(name, fn, targetObj, args, options = {}) { } // augment db.statement at parent, if necessary - if ( - span.attributes[SEMATTRS_DB_STATEMENT] && - parentSpan?.attributes[SEMATTRS_DB_SYSTEM] && - !parentSpan.attributes[SEMATTRS_DB_STATEMENT] - ) { + if (span.attributes[SEMATTRS_DB_STATEMENT] && parentSpan?.attributes[SEMATTRS_DB_SYSTEM] && !parentSpan.attributes[SEMATTRS_DB_STATEMENT]) { parentSpan.setAttribute(SEMATTRS_DB_STATEMENT, span.attributes[SEMATTRS_DB_STATEMENT]) } } const otherAttributes = new Map() - if (cds.context?.tenant) otherAttributes.set('sap.tenancy.tenant_id', cds.context.tenant) + if (cds.context.tenant) otherAttributes.set('sap.tenancy.tenant_id', cds.context.tenant) if (options.outbound) otherAttributes.set('sap.btp.destination', options.outbound) _setAttributes(span, otherAttributes) /* * call original function and subsequently end trace in callback */ - return otel.context.with(cds.context?._otelctx.setValue(cds.context._otelKey, span), () => { + return otel.context.with(cds.context._otelctx.setValue(cds.context._otelKey, span), () => { const onSuccess = res => { span.setStatus({ code: SpanStatusCode.OK }) return res