Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tracing): implement protobufjs DSM schema support #4701

Merged
merged 28 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
eb12e15
add dsm schema tracking
wconti27 Sep 13, 2024
87c5e5a
add protobuf schemas support for DSM
wconti27 Sep 18, 2024
4b187c4
Merge branch 'master' into conti/support-protobuf-schemas
wconti27 Sep 19, 2024
6b8b0b3
Merge branch 'master' into conti/support-protobuf-schemas
wconti27 Sep 19, 2024
a9cf1fc
revert fetch change
wconti27 Sep 19, 2024
38c1419
fix tests
wconti27 Sep 19, 2024
214a679
fix test
wconti27 Sep 19, 2024
c5e65b2
more testing
wconti27 Sep 19, 2024
be68bd5
clean up tests
wconti27 Sep 19, 2024
8186fbd
ensure shcmeas only added if dsm is enabled
wconti27 Sep 19, 2024
7f56ce0
more tests
wconti27 Sep 23, 2024
40a2fe8
increase schema sampler cache
wconti27 Sep 23, 2024
4fa3e81
fix sampling
wconti27 Sep 24, 2024
79e4945
fix sampling
wconti27 Sep 24, 2024
6028c36
Merge branch 'master' into conti/support-protobuf-schemas
wconti27 Sep 24, 2024
af12edb
fix protobuf google-api
wconti27 Sep 24, 2024
48fa7ff
using tracing channel and abstract schemaPlugin
wconti27 Sep 25, 2024
2748d35
Merge branch 'master' into conti/support-protobuf-schemas
wconti27 Sep 26, 2024
237c316
only use specific trace channels
wconti27 Sep 27, 2024
6b01e44
Merge branch 'master' into conti/support-protobuf-schemas
wconti27 Sep 27, 2024
1ec86fe
revert tracingChannel change
wconti27 Sep 27, 2024
1f42aab
fix channels
wconti27 Sep 27, 2024
da15fc1
fix proto lint
wconti27 Oct 3, 2024
b294d0e
more cleanup
wconti27 Oct 3, 2024
2adeb42
fix protobuf tests and use version range for testing
wconti27 Oct 3, 2024
af18e69
add necessary docs
wconti27 Oct 3, 2024
55d74fe
fix promise code
wconti27 Oct 4, 2024
af77ce5
Merge branch 'master' into conti/support-protobuf-schemas
wconti27 Oct 4, 2024
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
9 changes: 9 additions & 0 deletions .github/workflows/plugins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,15 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/plugins/test

protobufjs:
runs-on: ubuntu-latest
env:
PLUGINS: protobufjs
DD_DATA_STREAMS_ENABLED: true
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/plugins/test-and-upstream

q:
runs-on: ubuntu-latest
env:
Expand Down
2 changes: 2 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ tracer.use('pg', {
<h5 id="pg"></h5>
<h5 id="pg-tags"></h5>
<h5 id="pg-config"></h5>
<h5 id="protobufjs"></h5>
<h5 id="redis"></h5>
<h5 id="redis-tags"></h5>
<h5 id="redis-config"></h5>
Expand Down Expand Up @@ -142,6 +143,7 @@ tracer.use('pg', {
* [pg](./interfaces/export_.plugins.pg.html)
* [promise](./interfaces/export_.plugins.promise.html)
* [promise-js](./interfaces/export_.plugins.promise_js.html)
* [protobufjs](./interfaces/export_.plugins.protobufjs.html)
* [q](./interfaces/export_.plugins.q.html)
* [redis](./interfaces/export_.plugins.redis.html)
* [restify](./interfaces/export_.plugins.restify.html)
Expand Down
1 change: 1 addition & 0 deletions docs/add-redirects.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ declare -a plugins=(
"pg"
"promise"
"promise_js"
"protobufjs"
"q"
"redis"
"restify"
Expand Down
1 change: 1 addition & 0 deletions docs/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ tracer.use('playwright');
tracer.use('pg');
tracer.use('pg', { service: params => `${params.host}-${params.database}` });
tracer.use('pino');
tracer.use('protobufjs');
tracer.use('redis');
tracer.use('redis', redisOptions);
tracer.use('restify');
Expand Down
7 changes: 7 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ interface Plugins {
"playwright": tracer.plugins.playwright;
"pg": tracer.plugins.pg;
"pino": tracer.plugins.pino;
"protobufjs": tracer.plugins.protobufjs;
"redis": tracer.plugins.redis;
"restify": tracer.plugins.restify;
"rhea": tracer.plugins.rhea;
Expand Down Expand Up @@ -1731,6 +1732,12 @@ declare namespace tracer {
* on the tracer.
*/
interface pino extends Integration {}

/**
* This plugin automatically patches the [protobufjs](https://protobufjs.github.io/protobuf.js/)
* to collect protobuf message schemas when Datastreams Monitoring is enabled.
*/
interface protobufjs extends Integration {}

/**
* This plugin automatically instruments the
Expand Down
1 change: 1 addition & 0 deletions packages/datadog-instrumentations/src/helpers/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ module.exports = {
playwright: () => require('../playwright'),
'promise-js': () => require('../promise-js'),
promise: () => require('../promise'),
protobufjs: () => require('../protobufjs'),
q: () => require('../q'),
qs: () => require('../qs'),
redis: () => require('../redis'),
Expand Down
127 changes: 127 additions & 0 deletions packages/datadog-instrumentations/src/protobufjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
const shimmer = require('../../datadog-shimmer')
const { addHook } = require('./helpers/instrument')

const dc = require('dc-polyfill')
const serializeChannel = dc.channel('apm:protobufjs:serialize-start')
const deserializeChannel = dc.channel('apm:protobufjs:deserialize-end')

function wrapSerialization (messageClass) {
if (messageClass?.encode) {
shimmer.wrap(messageClass, 'encode', original => function () {
if (!serializeChannel.hasSubscribers) {
return original.apply(this, arguments)
}
serializeChannel.publish({ messageClass: this })
return original.apply(this, arguments)
})
}
}

function wrapDeserialization (messageClass) {
if (messageClass?.decode) {
shimmer.wrap(messageClass, 'decode', original => function () {
if (!deserializeChannel.hasSubscribers) {
return original.apply(this, arguments)
}
const result = original.apply(this, arguments)
deserializeChannel.publish({ messageClass: result })
return result
})
}
}

function wrapSetup (messageClass) {
if (messageClass?.setup) {
shimmer.wrap(messageClass, 'setup', original => function () {
const result = original.apply(this, arguments)

wrapSerialization(messageClass)
wrapDeserialization(messageClass)

return result
})
}
}

function wrapProtobufClasses (root) {
if (!root) {
return
}

if (root.decode) {
wrapSetup(root)
}

if (root.nestedArray) {
for (const subRoot of root.nestedArray) {
wrapProtobufClasses(subRoot)
}
}
}

function wrapReflection (protobuf) {
const reflectionMethods = [
{
target: protobuf.Root,
name: 'fromJSON'
},
{
target: protobuf.Type.prototype,
name: 'fromObject'
}
]

reflectionMethods.forEach(method => {
shimmer.wrap(method.target, method.name, original => function () {
const result = original.apply(this, arguments)
if (result.nested) {
for (const type in result.nested) {
wrapSetup(result.nested[type])
}
}
if (result.$type) {
wrapSetup(result.$type)
}
return result
})
})
}

function isPromise (obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
}

addHook({
name: 'protobufjs',
versions: ['>=6.8.0']
}, protobuf => {
shimmer.wrap(protobuf.Root.prototype, 'load', original => function () {
const result = original.apply(this, arguments)
if (isPromise(result)) {
return result.then(root => {
wrapProtobufClasses(root)
return root
})
} else {
// If result is not a promise, directly wrap the protobuf classes
wrapProtobufClasses(result)
return result
}
})

shimmer.wrap(protobuf.Root.prototype, 'loadSync', original => function () {
const root = original.apply(this, arguments)
wrapProtobufClasses(root)
return root
})

shimmer.wrap(protobuf, 'Type', Original => function () {
const typeInstance = new Original(...arguments)
wrapSetup(typeInstance)
return typeInstance
})

wrapReflection(protobuf)

return protobuf
})
14 changes: 14 additions & 0 deletions packages/datadog-plugin-protobufjs/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const SchemaPlugin = require('../../dd-trace/src/plugins/schema')
const SchemaExtractor = require('./schema_iterator')

class ProtobufjsPlugin extends SchemaPlugin {
static get id () {
return 'protobufjs'
}

static get schemaExtractor () {
return SchemaExtractor
}
}

module.exports = ProtobufjsPlugin
Loading
Loading