Skip to content

Commit

Permalink
feat: propagate trace context to hana via sap passport session context (
Browse files Browse the repository at this point in the history
  • Loading branch information
sjvans authored Oct 8, 2024
1 parent 3359088 commit 6001078
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 2 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
The format is based on [Keep a Changelog](http://keepachangelog.com/).

## Version 1.0.2 - tbd
## Version 1.1.0 - tbd

### Added

- Experimental!: Propagate W3C trace context to SAP HANA via session context `SAP_PASSPORT`
- Enable via environment variable `SAP_PASSPORT`

### Fixed

Expand All @@ -24,7 +29,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
- Support for SAP Cloud Logging credentials via user-provided service
- Support for adding `@opentelemetry/instrumentation-runtime-node`
- `npm add @opentelemetry/instrumentation-runtime-node`
- to `cds.requires.telemetry.instrumentations`, add:
- To `cds.requires.telemetry.instrumentations`, add:
```json
"instrumentation-runtime-node": {
"class": "RuntimeNodeInstrumentation",
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ Hence, the `hrtime` mode is on by default in development but not in production.

- `NO_TELEMETRY`: Disables the plugin
- `NO_LOCATE`: Disables function location in tracing
- `SAP_PASSPORT`: Enables propagating W3C trace context to SAP HANA (experimental!)
- `OTEL_LOG_LEVEL`: If not specified, the log level of cds logger `telemetry` is used
- `OTEL_SERVICE_NAME`: If not specified, the name is determined from package.json (defaulting to "CAP Application")
- `OTEL_SERVICE_VERSION`: If not specified, the version is determined from package.json (defaulting to "1.0.0")
Expand Down
9 changes: 9 additions & 0 deletions lib/tracing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ module.exports = resource => {
cds._telemetry.tracer._active = true
})

// clear sap passport for new tx
if (process.env.SAP_PASSPORT) {
cds.on('served', () => {
cds.db?.before('BEGIN', async function () {
if (this.dbc?.constructor.name in { HDBDriver: 1, HANAClientDriver: 1 }) this.dbc.set({ SAP_PASSPORT: '' })
})
})
}

/*
* add tracing
*/
Expand Down
31 changes: 31 additions & 0 deletions lib/tracing/trace.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const cds = require('@sap/cds')
const LOG = cds.log('telemetry')

const otel = require('@opentelemetry/api')
const { SpanKind, SpanStatusCode, ROOT_CONTEXT } = otel
Expand Down Expand Up @@ -244,6 +245,36 @@ function trace(name, fn, targetObj, args, options = {}) {

_setAttributes(span, _getDynamicDBAttributes(options, args, parentSpan))

// SAP Passport
if (process.env.SAP_PASSPORT && targetObj.dbc?.constructor.name in { HDBDriver: 1, HANAClientDriver: 1 }) {
const { spanId, traceId } = span.spanContext()
// prettier-ignore
let passport = [
/* EYECATCHER */ '2A54482A',
/* VERSION */ '03',
/* LENGTH */ '00E6',
/* TRACELEVEL */ '0000',
/* COMPONENTID */ '2020202020202020202020202020202020202020202020202020202020202020',
/* SERVICE */ '0000',
/* USER */ '2020202020202020202020202020202020202020202020202020202020202020',
/* ACTION */ '20202020202020202020202020202020202020202020202020202020202020202020202020202020',
/* ACTIONTYPE */ '0000',
/* PREVCOMPONENTID */ Buffer.from(span.resource.attributes['service.name'].substr(0, 32).padEnd(32, ' ')).toString('hex'),
/* TRANSACTIONID */ Buffer.from(traceId.toUpperCase()).toString('hex'),
/* CLIENT */ '202020',
/* COMPONENTTYPE */ '0000',
/* ROOTCONTEXTID */ traceId.toUpperCase(),
/* CONNECTIONID */ '0000000000000001' + spanId.toUpperCase(),
/* CONNECTIONCNT */ '00000001',
/* VARPARTCOUNT */ '0000',
/* VARPARTOFFSET */ '0000', // REVISIT: @sap/dsrpassport uses '0226'
/* EYECATCHER */ '2A54482A'
]
passport = passport.join('')
LOG._debug && LOG.debug('Setting SAP Passport:', passport)
targetObj.dbc.set({ SAP_PASSPORT: passport })
}

// augment db.statement at parent, if necessary
if (
span.attributes[SEMATTRS_DB_STATEMENT] &&
Expand Down

0 comments on commit 6001078

Please sign in to comment.