Skip to content

Commit 1137f40

Browse files
authored
instrumentation: fix oracle db connection string (#6003)
* instrumentation: improve oracle db connection string Oracle supports three different ways to pass through conenctions. We only supported URLs so far. Others were unsupported. Instead of relying on the input, this changes the implementation to use the drivers own information to get that part in v6. This reduces the overhead of the instrumentation and we should always get the correct information, no matter how the user passes the information through. On top also parse oracle descriptors in v5 of the oracledb driver.
1 parent 785155e commit 1137f40

File tree

7 files changed

+265
-169
lines changed

7 files changed

+265
-169
lines changed

.github/workflows/apm-integrations.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -771,14 +771,12 @@ jobs:
771771
SERVICES: oracledb
772772
DD_TEST_AGENT_URL: http://testagent:9126
773773
DD_INJECT_FORCE: 'true'
774-
# Needed to fix issue with `actions/checkout@v3: https://github.com/actions/checkout/issues/1590
775-
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
776774
steps:
777775
# https://github.com/actions/runner/issues/2906#issuecomment-2109514798
778776
- name: Install Node for runner (with glibc 2.17 compatibility)
779777
run: |
780-
curl -LO https://unofficial-builds.nodejs.org/download/release/v20.9.0/node-v20.9.0-linux-x64-glibc-217.tar.xz
781-
tar -xf node-v20.9.0-linux-x64-glibc-217.tar.xz --strip-components 1 -C /node20217
778+
curl -LO https://unofficial-builds.nodejs.org/download/release/v20.19.3/node-v20.19.3-linux-x64-glibc-217.tar.xz
779+
tar -xf node-v20.19.3-linux-x64-glibc-217.tar.xz --strip-components 1 -C /node20217
782780
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
783781
- uses: ./.github/actions/node
784782
with:

packages/datadog-instrumentations/src/oracledb.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,34 @@ addHook({ name: 'oracledb', versions: ['>=5'] }, oracledb => {
3838
}
3939

4040
return new AsyncResource('apm:oracledb:inner-scope').runInAsyncScope(() => {
41+
// The connAttrs are used to pass through the argument to the potential
42+
// serviceName method a user might have passed through as well as parsing
43+
// the connection string in v5.
4144
const connAttrs = connectionAttributes.get(this)
42-
startChannel.publish({ query: dbQuery, connAttrs })
45+
46+
const details = typeof this.hostName === 'string' ? this : this._impl
47+
48+
let hostname
49+
let port
50+
let dbInstance
51+
52+
if (details) {
53+
dbInstance = details.serviceName
54+
hostname = details.hostName ?? details.nscon?.ntAdapter?.hostName
55+
port = String(details.port ?? details.nscon?.ntAdapter?.port ?? '')
56+
}
57+
58+
startChannel.publish({
59+
query: dbQuery,
60+
connAttrs,
61+
dbInstance,
62+
port,
63+
hostname,
64+
})
4365
try {
4466
let result = execute.apply(this, arguments)
4567

46-
if (result && typeof result.then === 'function') {
68+
if (typeof result?.then === 'function') {
4769
result = result.then(
4870
x => {
4971
finish()
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const { URL } = require('url')
2+
const log = require('../../dd-trace/src/log')
3+
4+
function parseOracleDescriptor (descriptor) {
5+
const hostnameMatch = descriptor.match(/HOST\s*=\s*([^)]+)/i)
6+
const hostname = hostnameMatch?.[1] || 'localhost' // Default Oracle hostname
7+
8+
const portMatch = descriptor.match(/PORT\s*=\s*([^)]+)/i)
9+
const port = portMatch?.[1] || '1521' // Default Oracle port
10+
11+
const sidMatch = descriptor.match(/SID\s*=\s*([^)]+)/i)
12+
13+
const dbInstance = sidMatch?.[1] || descriptor.match(/SERVICE_NAME\s*=\s*([^)]+)/i)?.[1] || 'XEPDB1' // Default Oracle service name
14+
15+
return { hostname, port, dbInstance }
16+
}
17+
18+
module.exports = function getDBInformation (connAttrs) {
19+
// Users can pass either connectString or connectionString
20+
const connectString = ((connAttrs.connectString || connAttrs.connectionString) ?? '').trim()
21+
if (connectString.startsWith('(')) {
22+
return parseOracleDescriptor(connectString)
23+
}
24+
try {
25+
const url = new URL(`oracle://${connectString}`)
26+
return {
27+
hostname: url.hostname || 'localhost', // Default Oracle hostname
28+
port: url.port || '1521', // Default Oracle port
29+
dbInstance: url.pathname && url.pathname.slice(1) || 'XEPDB1' // Default Oracle service name
30+
}
31+
} catch (error) {
32+
log.error('Invalid oracle connection string', error)
33+
return {}
34+
}
35+
}

packages/datadog-plugin-oracledb/src/index.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,30 @@
22

33
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
44
const DatabasePlugin = require('../../dd-trace/src/plugins/database')
5-
const log = require('../../dd-trace/src/log')
5+
6+
let parser
67

78
class OracledbPlugin extends DatabasePlugin {
89
static get id () { return 'oracledb' }
910
static get system () { return 'oracle' }
1011
static get peerServicePrecursors () { return ['db.instance', 'db.hostname'] }
1112

12-
start ({ query, connAttrs }) {
13-
const service = this.serviceName({ pluginConfig: this.config, params: connAttrs })
14-
// Users can pass either connectString or connectionString
15-
const url = getUrl(connAttrs.connectString || connAttrs.connectionString)
13+
start ({ query, connAttrs, port, hostname, dbInstance }) {
14+
let service = this.serviceName({ pluginConfig: this.config, params: connAttrs })
15+
16+
if (hostname === undefined) {
17+
// Lazy load for performance. This is not needed in v6 and up
18+
parser ??= require('./connection-parser')
19+
const dbInfo = parser(connAttrs)
20+
hostname = dbInfo.hostname
21+
port ??= dbInfo.port
22+
dbInstance ??= dbInfo.dbInstance
23+
}
24+
25+
if (service === undefined && hostname) {
26+
// Fallback for users not providing the service properly in a serviceName method
27+
service = `${hostname}:${port}/${dbInstance}`
28+
}
1629

1730
this.startSpan(this.operationName(), {
1831
service,
@@ -21,22 +34,12 @@ class OracledbPlugin extends DatabasePlugin {
2134
kind: 'client',
2235
meta: {
2336
'db.user': this.config.user,
24-
'db.instance': url.pathname && url.pathname.slice(1),
25-
'db.hostname': url.hostname,
26-
[CLIENT_PORT_KEY]: url.port
37+
'db.instance': dbInstance,
38+
'db.hostname': hostname,
39+
[CLIENT_PORT_KEY]: port,
2740
}
2841
})
2942
}
3043
}
3144

32-
// TODO: Avoid creating an error since it's a heavy operation.
33-
function getUrl (connectString) {
34-
try {
35-
return new URL(`http://${connectString}`)
36-
} catch (e) {
37-
log.error('Invalid oracle connection string', e)
38-
return {}
39-
}
40-
}
41-
4245
module.exports = OracledbPlugin

0 commit comments

Comments
 (0)