Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions lib/tracing/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const cds = require('@sap/cds')
const LOG = cds.log('telemetry')

const { trace } = require('@opentelemetry/api')
const { getEnv, getEnvWithoutDefaults } = require('@opentelemetry/core')
const { trace, SpanKind } = require('@opentelemetry/api')
const { ExportResultCode, getEnv, getEnvWithoutDefaults } = require('@opentelemetry/core')
const { Resource } = require('@opentelemetry/resources')
const { BatchSpanProcessor, SimpleSpanProcessor, SamplingDecision } = require('@opentelemetry/sdk-trace-base')
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node')
Expand Down Expand Up @@ -156,6 +156,25 @@ module.exports = resource => {
LOG._info && LOG.info('Dynatrace OneAgent detected, disabling trace exporter')
} else {
const exporter = _getExporter()

// unofficial hacks for running with xotel agent:
// - do not export @opentelemetry/instrumentation-hdb spans
// - remove parentSpanId from http spans if there is no traceparent header
// - switch SERVER spans back to INTERNAL spans
if (cds.env.requires.telemetry._with_xotel) {
LOG._info && LOG.info('Adding export mods for running with @sap/xotel-agent-ext-js')
const { export: _export } = exporter
exporter.export = function (spans, resultCallback) {
const _spans = spans.filter(s => s.instrumentationLibrary?.name !== '@opentelemetry/instrumentation-hdb')
if (!_spans.length) return resultCallback({ code: ExportResultCode.SUCCESS })
for (const _span of _spans) {
if (_should_be_orphan(_span)) _make_orphan(_span)
if (_should_be_internal(_span)) _make_internal(_span)
}
return _export.call(this, _spans, resultCallback)
}
}

const processorConfig = cds.env.requires.telemetry.tracing.processor?.config || {}
const processor =
process.env.NODE_ENV === 'production'
Expand All @@ -181,3 +200,19 @@ module.exports = resource => {

return tracerProvider
}

const _should_be_orphan = span =>
span.instrumentationLibrary?.name === '@opentelemetry/instrumentation-http' &&
span.parentSpanId &&
!span.attributes['http.request.header.traceparent']
const _make_orphan = span => {
LOG._debug && LOG.debug('Removing parentSpanId from span:', span.spanContext().spanId)
span.parentSpanId = undefined
}

const _should_be_internal = span =>
span.kind === SpanKind.SERVER && span.instrumentationLibrary?.name !== '@opentelemetry/instrumentation-http'
const _make_internal = span => {
LOG._debug && LOG.debug('Switching span kind back to INTERNAL for span:', span.spanContext().spanId)
span.kind = SpanKind.INTERNAL
}
6 changes: 6 additions & 0 deletions lib/tracing/trace.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ function trace(req, fn, that, args, opts = {}) {
if (kind == null) {
kind = SpanKind.INTERNAL //> default

// unofficial hack for running with xotel agent: default to SERVER spans
if (cds.env.requires.telemetry._with_xotel) {
LOG._debug && LOG.debug('Temporaily switching span kind to SERVER to circumvent no sampling decision by xotel')
kind = SpanKind.SERVER
}

if (that instanceof cds.RemoteService) kind = SpanKind.CLIENT
else if (that instanceof cds.MessagingService) {
const msg = cds.env.requires[that.name]
Expand Down