Skip to content

Commit

Permalink
Merge branch 'master' into ida613/baggage-extraction-patch
Browse files Browse the repository at this point in the history
  • Loading branch information
ida613 authored Nov 25, 2024
2 parents 20b7a9d + cd0b922 commit a78ddd3
Show file tree
Hide file tree
Showing 51 changed files with 289 additions and 324 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/appsec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ jobs:
version:
- 18
- latest
range: ['9.5.0', '11.1.4', '13.2.0', '>=14.0.0 <=14.2.6', '>=14.2.7 <15']
range: ['9.5.0', '11.1.4', '13.2.0', '>=14.0.0 <=14.2.6', '>=14.2.7 <15', '>=15.0.0']
runs-on: ubuntu-latest
env:
PLUGINS: next
Expand Down
15 changes: 13 additions & 2 deletions .github/workflows/plugins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,18 @@ jobs:
PLUGINS: langchain
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/plugins/test
- uses: ./.github/actions/testagent/start
- uses: ./.github/actions/node/setup
- uses: ./.github/actions/install
- uses: ./.github/actions/node/18 # langchain doesn't support Node 16
- run: yarn test:plugins:ci
shell: bash
- uses: ./.github/actions/node/latest
- run: yarn test:plugins:ci
shell: bash
- uses: codecov/codecov-action@v3
- if: always()
uses: ./.github/actions/testagent/logs

limitd-client:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -747,7 +758,7 @@ jobs:
version:
- 18
- latest
range: ['9.5.0', '11.1.4', '13.2.0', '>=14.0.0 <=14.2.6', '>=14.2.7 <15']
range: ['9.5.0', '11.1.4', '13.2.0', '>=14.0.0 <=14.2.6', '>=14.2.7 <15', '>=15.0.0']
runs-on: ubuntu-latest
env:
PLUGINS: next
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
# setting fail-fast to false in an attempt to prevent this from happening
fail-fast: false
matrix:
version: [18, 20, latest]
version: [18, 20, 22, latest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -34,7 +34,7 @@ jobs:
integration-guardrails:
strategy:
matrix:
version: [12, 14, 16]
version: [12.0.0, 12, 14.0.0, 14, 16.0.0, 16, 18.0.0, 18.1.0, 20.0.0, 22.0.0]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
6 changes: 4 additions & 2 deletions init.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

/* eslint-disable no-var */

var NODE_MAJOR = require('./version').NODE_MAJOR
var nodeVersion = require('./version')
var NODE_MAJOR = nodeVersion.NODE_MAJOR
var NODE_MINOR = nodeVersion.NODE_MINOR

// We use several things that are not supported by older versions of Node:
// - AsyncLocalStorage
Expand All @@ -11,7 +13,7 @@ var NODE_MAJOR = require('./version').NODE_MAJOR
// - Mocha (for testing)
// and probably others.
// TODO: Remove all these dependencies so that we can report telemetry.
if (NODE_MAJOR >= 12) {
if ((NODE_MAJOR === 12 && NODE_MINOR >= 17) || NODE_MAJOR > 12) {
var path = require('path')
var Module = require('module')
var semver = require('semver')
Expand Down
9 changes: 7 additions & 2 deletions initialize.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,16 @@ ${result.source}`
return result
}

const [NODE_MAJOR, NODE_MINOR] = process.versions.node.split('.').map(x => +x)

const brokenLoaders = NODE_MAJOR === 18 && NODE_MINOR === 0

export async function load (...args) {
return insertInit(await origLoad(...args))
const loadHook = brokenLoaders ? args[args.length - 1] : origLoad
return insertInit(await loadHook(...args))
}

export const resolve = origResolve
export const resolve = brokenLoaders ? undefined : origResolve

export const getFormat = origGetFormat

Expand Down
39 changes: 34 additions & 5 deletions integration-tests/init.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const telemetryGood = ['complete', 'injection_forced:false']
const { engines } = require('../package.json')
const supportedRange = engines.node
const currentVersionIsSupported = semver.satisfies(process.versions.node, supportedRange)
const currentVersionCanLog = semver.satisfies(process.versions.node, '>=12.17.0')

// These are on by default in release tests, so we'll turn them off for
// more fine-grained control of these variables in these tests.
Expand Down Expand Up @@ -83,7 +84,30 @@ function testRuntimeVersionChecks (arg, filename) {
}
}

if (!currentVersionIsSupported) {
if (!currentVersionCanLog) {
context('when node version is too low for AsyncLocalStorage', () => {
useEnv({ NODE_OPTIONS })

it('should initialize the tracer, if no DD_INJECTION_ENABLED', () =>
doTest('false\n'))
context('with DD_INJECTION_ENABLED', () => {
useEnv({ DD_INJECTION_ENABLED })

context('without debug', () => {
it('should not initialize the tracer', () => doTest('false\n'))
it('should not, if DD_INJECT_FORCE', () => doTestForced('false\n'))
})
context('with debug', () => {
useEnv({ DD_TRACE_DEBUG })

it('should not initialize the tracer', () =>
doTest('false\n'))
it('should initialize the tracer, if DD_INJECT_FORCE', () =>
doTestForced('false\n'))
})
})
})
} else if (!currentVersionIsSupported) {
context('when node version is less than engines field', () => {
useEnv({ NODE_OPTIONS })

Expand Down Expand Up @@ -165,17 +189,22 @@ describe('init.js', () => {
testRuntimeVersionChecks('require', 'init.js')
})

// ESM is not supportable prior to Node.js 12
if (semver.satisfies(process.versions.node, '>=12')) {
// ESM is not supportable prior to Node.js 12.17.0, 14.13.1 on the 14.x line,
// or on 18.0.0 in particular.
if (
semver.satisfies(process.versions.node, '>=12.17.0') &&
semver.satisfies(process.versions.node, '>=14.13.1')
) {
describe('initialize.mjs', () => {
useSandbox()
stubTracerIfNeeded()

context('as --loader', () => {
testInjectionScenarios('loader', 'initialize.mjs', true)
testInjectionScenarios('loader', 'initialize.mjs',
process.versions.node !== '18.0.0')
testRuntimeVersionChecks('loader', 'initialize.mjs')
})
if (Number(process.versions.node.split('.')[0]) >= 18) {
if (semver.satisfies(process.versions.node, '>=20.6.0')) {
context('as --import', () => {
testInjectionScenarios('import', 'initialize.mjs', true)
testRuntimeVersionChecks('loader', 'initialize.mjs')
Expand Down
2 changes: 1 addition & 1 deletion packages/datadog-instrumentations/src/azure-functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const {
const shimmer = require('../../datadog-shimmer')
const dc = require('dc-polyfill')

const azureFunctionsChannel = dc.tracingChannel('datadog:azure-functions:invoke')
const azureFunctionsChannel = dc.tracingChannel('datadog:azure:functions:invoke')

addHook({ name: '@azure/functions', versions: ['>=4'] }, azureFunction => {
const { app } = azureFunction
Expand Down
10 changes: 5 additions & 5 deletions packages/datadog-instrumentations/src/next.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ function finish (ctx, result, err) {
// however, it is not provided as a class function or exported property
addHook({
name: 'next',
versions: ['>=13.3.0 <15'],
versions: ['>=13.3.0'],
file: 'dist/server/web/spec-extension/adapters/next-request.js'
}, NextRequestAdapter => {
shimmer.wrap(NextRequestAdapter.NextRequestAdapter, 'fromNodeNextRequest', fromNodeNextRequest => {
Expand All @@ -215,7 +215,7 @@ addHook({

addHook({
name: 'next',
versions: ['>=11.1 <15'],
versions: ['>=11.1'],
file: 'dist/server/serve-static.js'
}, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))

Expand All @@ -225,7 +225,7 @@ addHook({
file: 'dist/next-server/server/serve-static.js'
}, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))

addHook({ name: 'next', versions: ['>=11.1 <15'], file: 'dist/server/next-server.js' }, nextServer => {
addHook({ name: 'next', versions: ['>=11.1'], file: 'dist/server/next-server.js' }, nextServer => {
const Server = nextServer.default

shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
Expand All @@ -242,7 +242,7 @@ addHook({ name: 'next', versions: ['>=11.1 <15'], file: 'dist/server/next-server
})

// `handleApiRequest` changes parameters/implementation at 13.2.0
addHook({ name: 'next', versions: ['>=13.2 <15'], file: 'dist/server/next-server.js' }, nextServer => {
addHook({ name: 'next', versions: ['>=13.2'], file: 'dist/server/next-server.js' }, nextServer => {
const Server = nextServer.default
shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequestWithMatch)
return nextServer
Expand Down Expand Up @@ -276,7 +276,7 @@ addHook({

addHook({
name: 'next',
versions: ['>=13 <15'],
versions: ['>=13'],
file: 'dist/server/web/spec-extension/request.js'
}, request => {
const nextUrlDescriptor = Object.getOwnPropertyDescriptor(request.NextRequest.prototype, 'nextUrl')
Expand Down
2 changes: 1 addition & 1 deletion packages/datadog-plugin-azure-functions/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class AzureFunctionsPlugin extends TracingPlugin {
static get kind () { return 'server' }
static get type () { return 'serverless' }

static get prefix () { return 'tracing:datadog:azure-functions:invoke' }
static get prefix () { return 'tracing:datadog:azure:functions:invoke' }

bindStart (ctx) {
const { functionName, methodName } = ctx
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('esm', () => {
assert.strictEqual(payload.length, 1)
assert.isArray(payload[0])
assert.strictEqual(payload[0].length, 1)
assert.propertyVal(payload[0][0], 'name', 'azure-functions.invoke')
assert.propertyVal(payload[0][0], 'name', 'azure.functions.invoke')
})
}).timeout(50000)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class LangChainChatModelHandler extends LangChainLanguageModelHandler {

this.extractTokenMetrics(ctx.currentStore?.span, result)

for (const messageSetIdx in result.generations) {
for (const messageSetIdx in result?.generations) {
const messageSet = result.generations[messageSetIdx]

for (const chatCompletionIdx in messageSet) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class LangChainLLMHandler extends LangChainLanguageModelHandler {

this.extractTokenMetrics(ctx.currentStore?.span, result)

for (const completionIdx in result.generations) {
for (const completionIdx in result?.generations) {
const completion = result.generations[completionIdx]
if (this.isPromptCompletionSampled()) {
tags[`langchain.response.completions.${completionIdx}.text`] = this.normalize(completion[0].text) || ''
Expand Down
108 changes: 108 additions & 0 deletions packages/datadog-plugin-langchain/test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,33 @@ describe('Plugin', () => {
})

describe('llm', () => {
it('does not tag output on error', async () => {
nock('https://api.openai.com').post('/v1/completions').reply(403)

const checkTraces = agent
.use(traces => {
expect(traces[0].length).to.equal(1)

const span = traces[0][0]

const langchainResponseRegex = /^langchain\.response\.completions\./
const hasMatching = Object.keys(span.meta).some(key => langchainResponseRegex.test(key))

expect(hasMatching).to.be.false

expect(span.meta).to.have.property('error.message')
expect(span.meta).to.have.property('error.type')
expect(span.meta).to.have.property('error.stack')
})

try {
const llm = new langchainOpenai.OpenAI({ model: 'gpt-3.5-turbo-instruct', maxRetries: 0 })
await llm.generate(['what is 2 + 2?'])
} catch {}

await checkTraces
})

it('instruments a langchain llm call for a single prompt', async () => {
stubCall({
...openAiBaseCompletionInfo,
Expand Down Expand Up @@ -270,6 +297,32 @@ describe('Plugin', () => {
})

describe('chat model', () => {
it('does not tag output on error', async () => {
nock('https://api.openai.com').post('/v1/chat/completions').reply(403)

const checkTraces = agent
.use(traces => {
expect(traces[0].length).to.equal(1)

const span = traces[0][0]

const langchainResponseRegex = /^langchain\.response\.completions\./
const hasMatching = Object.keys(span.meta).some(key => langchainResponseRegex.test(key))
expect(hasMatching).to.be.false

expect(span.meta).to.have.property('error.message')
expect(span.meta).to.have.property('error.type')
expect(span.meta).to.have.property('error.stack')
})

try {
const chatModel = new langchainOpenai.ChatOpenAI({ model: 'gpt-4', maxRetries: 0 })
await chatModel.invoke('Hello!')
} catch {}

await checkTraces
})

it('instruments a langchain openai chat model call for a single string prompt', async () => {
stubCall({
...openAiBaseChatInfo,
Expand Down Expand Up @@ -546,6 +599,37 @@ describe('Plugin', () => {
})

describe('chain', () => {
it('does not tag output on error', async () => {
nock('https://api.openai.com').post('/v1/chat/completions').reply(403)

const checkTraces = agent
.use(traces => {
expect(traces[0].length).to.equal(2)

const chainSpan = traces[0][0]

const langchainResponseRegex = /^langchain\.response\.outputs\./

const hasMatching = Object.keys(chainSpan.meta).some(key => langchainResponseRegex.test(key))
expect(hasMatching).to.be.false

expect(chainSpan.meta).to.have.property('error.message')
expect(chainSpan.meta).to.have.property('error.type')
expect(chainSpan.meta).to.have.property('error.stack')
})

try {
const model = new langchainOpenai.ChatOpenAI({ model: 'gpt-4', maxRetries: 0 })
const parser = new langchainOutputParsers.StringOutputParser()

const chain = model.pipe(parser)

await chain.invoke('Hello!')
} catch {}

await checkTraces
})

it('instruments a langchain chain with a single openai chat model call', async () => {
stubCall({
...openAiBaseChatInfo,
Expand Down Expand Up @@ -790,6 +874,30 @@ describe('Plugin', () => {

describe('embeddings', () => {
describe('@langchain/openai', () => {
it('does not tag output on error', async () => {
nock('https://api.openai.com').post('/v1/embeddings').reply(403)

const checkTraces = agent
.use(traces => {
expect(traces[0].length).to.equal(1)

const span = traces[0][0]

expect(span.meta).to.not.have.property('langchain.response.outputs.embedding_length')

expect(span.meta).to.have.property('error.message')
expect(span.meta).to.have.property('error.type')
expect(span.meta).to.have.property('error.stack')
})

try {
const embeddings = new langchainOpenai.OpenAIEmbeddings()
await embeddings.embedQuery('Hello, world!')
} catch {}

await checkTraces
})

it('instruments a langchain openai embedQuery call', async () => {
stubCall({
...openAiBaseEmbeddingInfo,
Expand Down
2 changes: 1 addition & 1 deletion packages/dd-trace/src/appsec/api_security_sampler.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function computeKey (req, res) {
const status = res.statusCode

if (!method || !status) {
log.warn('Unsupported groupkey for API security')
log.warn('[ASM] Unsupported groupkey for API security')
return null
}
return method + route + status
Expand Down
Loading

0 comments on commit a78ddd3

Please sign in to comment.