Skip to content

Commit 93e4023

Browse files
authored
feat(dynamodb): added endpoint filtering (#1484)
refs INSTA-16323
1 parent ce5c9d8 commit 93e4023

File tree

11 files changed

+370
-55
lines changed

11 files changed

+370
-55
lines changed

packages/collector/test/tracing/cloud/aws-sdk/v2/dynamodb/test.js

+81-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const { expect } = require('chai');
1212
const { fail } = expect;
1313
const supportedVersion = require('@instana/core').tracing.supportedVersion;
1414
const config = require('../../../../../../../core/test/config');
15-
const { retry, stringifyItems, delay } = require('../../../../../../../core/test/test_util');
15+
const { retry, stringifyItems, delay, expectAtLeastOneMatching } = require('../../../../../../../core/test/test_util');
1616
const ProcessControls = require('../../../../../test_util/ProcessControls');
1717
const globalAgent = require('../../../../../globalAgent');
1818
const {
@@ -252,4 +252,84 @@ mochaSuiteFn('tracing/cloud/aws-sdk/v2/dynamodb', function () {
252252
});
253253
});
254254
});
255+
describe('when ignore-endpoints enabled via tracing config', async () => {
256+
let appControls;
257+
before(async () => {
258+
appControls = new ProcessControls({
259+
dirname: __dirname,
260+
useGlobalAgent: true,
261+
env: {
262+
AWS_DYNAMODB_TABLE_NAME: tableName,
263+
INSTANA_IGNORE_ENDPOINTS: 'dynamodb:list'
264+
}
265+
});
266+
await appControls.startAndWaitForAgentConnection();
267+
});
268+
269+
beforeEach(async () => {
270+
await agentControls.clearReceivedTraceData();
271+
});
272+
273+
after(async () => {
274+
await appControls.stop();
275+
});
276+
277+
afterEach(async () => {
278+
await appControls.clearIpcMessages();
279+
});
280+
281+
const requestMethod = getNextCallMethod();
282+
it('should ignore spans for configured ignore endpoints(listTables)', async function () {
283+
const apiPath = `/listTables/${requestMethod}`;
284+
285+
await appControls.sendRequest({
286+
method: 'GET',
287+
path: `${apiPath}`
288+
});
289+
await delay(1000);
290+
const spans = await agentControls.getSpans();
291+
// 1 x http entry span
292+
// 1 x http client span
293+
expect(spans.length).to.equal(2);
294+
spans.forEach(span => {
295+
expect(span.n).not.to.equal('dynamodb');
296+
});
297+
expectAtLeastOneMatching(spans, [
298+
span => expect(span.n).to.equal('node.http.server'),
299+
span => expect(span.data.http.method).to.equal('GET')
300+
]);
301+
expectAtLeastOneMatching(spans, [
302+
span => expect(span.n).to.equal('node.http.client'),
303+
span => expect(span.data.http.method).to.equal('GET')
304+
]);
305+
});
306+
it('should not ignore spans for endpoints that are not in the ignore list', async () => {
307+
const apiPath = `/scan/${requestMethod}`;
308+
309+
await appControls.sendRequest({
310+
method: 'GET',
311+
path: `${apiPath}`
312+
});
313+
await delay(1000);
314+
const spans = await agentControls.getSpans();
315+
316+
// 1 x http entry span
317+
// 1 x http client span
318+
// 1 x dynamodb span
319+
expect(spans.length).to.equal(3);
320+
321+
expectAtLeastOneMatching(spans, [
322+
span => expect(span.n).to.equal('dynamodb'),
323+
span => expect(span.data.dynamodb.op).to.equal('scan')
324+
]);
325+
expectAtLeastOneMatching(spans, [
326+
span => expect(span.n).to.equal('node.http.server'),
327+
span => expect(span.data.http.method).to.equal('GET')
328+
]);
329+
expectAtLeastOneMatching(spans, [
330+
span => expect(span.n).to.equal('node.http.client'),
331+
span => expect(span.data.http.method).to.equal('GET')
332+
]);
333+
});
334+
});
255335
});

packages/collector/test/tracing/cloud/aws-sdk/v3/dynamodb/test_definition.js

+144-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const { expect } = require('chai');
1212
const { fail } = expect;
1313
const supportedVersion = require('@instana/core').tracing.supportedVersion;
1414
const config = require('@instana/core/test/config');
15-
const { retry, stringifyItems, delay } = require('@instana/core/test/test_util');
15+
const { retry, stringifyItems, delay, expectAtLeastOneMatching } = require('@instana/core/test/test_util');
1616
const ProcessControls = require('../../../../../test_util/ProcessControls');
1717
const globalAgent = require('../../../../../globalAgent');
1818
const {
@@ -342,6 +342,149 @@ function start(version, requestMethod, reducedTestSuite = false) {
342342
});
343343
});
344344

345+
describe('ignore-endpoints:', function () {
346+
describe('when ignore-endpoints is enabled via agent configuration', () => {
347+
const { AgentStubControls } = require('../../../../../apps/agentStubControls');
348+
const customAgentControls = new AgentStubControls();
349+
let controls;
350+
const tableName = createTableName();
351+
before(async () => {
352+
await customAgentControls.startAgent({
353+
ignoreEndpoints: { dynamodb: ['listTables'] }
354+
});
355+
356+
controls = new ProcessControls({
357+
agentControls: customAgentControls,
358+
appPath: path.join(__dirname, './app'),
359+
env: {
360+
AWS_DYNAMODB_TABLE_NAME: tableName,
361+
AWS_SDK_CLIENT_DYNAMODB_REQUIRE: version
362+
}
363+
});
364+
await controls.startAndWaitForAgentConnection();
365+
});
366+
367+
beforeEach(async () => {
368+
await customAgentControls.clearReceivedTraceData();
369+
});
370+
371+
after(async () => {
372+
await customAgentControls.stopAgent();
373+
await controls.stop();
374+
});
375+
after(() => cleanup(tableName));
376+
377+
it('should ignore dynamodb spans for ignored endpoints (listTables)', async () => {
378+
const apiPath = `/listTables/${requestMethod}`;
379+
380+
await controls.sendRequest({
381+
method: 'GET',
382+
path: `${apiPath}`
383+
});
384+
await delay(1000);
385+
const spans = await customAgentControls.getSpans();
386+
// 1 x http entry span
387+
// 1 x http client span
388+
expect(spans.length).to.equal(2);
389+
spans.forEach(span => {
390+
expect(span.n).not.to.equal('dynamodb');
391+
});
392+
expectAtLeastOneMatching(spans, [
393+
span => expect(span.n).to.equal('node.http.server'),
394+
span => expect(span.data.http.method).to.equal('GET')
395+
]);
396+
expectAtLeastOneMatching(spans, [
397+
span => expect(span.n).to.equal('node.http.client'),
398+
span => expect(span.data.http.method).to.equal('GET')
399+
]);
400+
});
401+
});
402+
describe('ignore-endpoints enabled via tracing config', async () => {
403+
const tableName = createTableName();
404+
let appControls;
405+
406+
before(async () => {
407+
appControls = new ProcessControls({
408+
useGlobalAgent: true,
409+
appPath: path.join(__dirname, './app'),
410+
env: {
411+
AWS_DYNAMODB_TABLE_NAME: tableName,
412+
AWS_SDK_CLIENT_DYNAMODB_REQUIRE: version,
413+
INSTANA_IGNORE_ENDPOINTS: 'dynamodb:listTables'
414+
}
415+
});
416+
await appControls.startAndWaitForAgentConnection();
417+
});
418+
419+
beforeEach(async () => {
420+
await agentControls.clearReceivedTraceData();
421+
});
422+
423+
after(async () => {
424+
await appControls.stop();
425+
});
426+
427+
afterEach(async () => {
428+
await appControls.clearIpcMessages();
429+
});
430+
431+
after(() => cleanup(tableName));
432+
433+
it('should ignore spans for configured ignore endpoints(listTables)', async function () {
434+
const apiPath = `/listTables/${requestMethod}`;
435+
436+
await appControls.sendRequest({
437+
method: 'GET',
438+
path: `${apiPath}`
439+
});
440+
await delay(1000);
441+
const spans = await agentControls.getSpans();
442+
// 1 x http entry span
443+
// 1 x http client span
444+
expect(spans.length).to.equal(2);
445+
spans.forEach(span => {
446+
expect(span.n).not.to.equal('dynamodb');
447+
});
448+
expectAtLeastOneMatching(spans, [
449+
span => expect(span.n).to.equal('node.http.server'),
450+
span => expect(span.data.http.method).to.equal('GET')
451+
]);
452+
expectAtLeastOneMatching(spans, [
453+
span => expect(span.n).to.equal('node.http.client'),
454+
span => expect(span.data.http.method).to.equal('GET')
455+
]);
456+
});
457+
it('should not ignore spans for endpoints that are not in the ignore list', async () => {
458+
const apiPath = `/createTable/${requestMethod}`;
459+
460+
await appControls.sendRequest({
461+
method: 'GET',
462+
path: `${apiPath}`
463+
});
464+
await delay(1000);
465+
const spans = await agentControls.getSpans();
466+
467+
// 1 x http entry span
468+
// 1 x http client span
469+
// 1 x dynamodb span
470+
expect(spans.length).to.equal(3);
471+
472+
expectAtLeastOneMatching(spans, [
473+
span => expect(span.n).to.equal('dynamodb'),
474+
span => expect(span.data.dynamodb.op).to.equal('createTable')
475+
]);
476+
expectAtLeastOneMatching(spans, [
477+
span => expect(span.n).to.equal('node.http.server'),
478+
span => expect(span.data.http.method).to.equal('GET')
479+
]);
480+
expectAtLeastOneMatching(spans, [
481+
span => expect(span.n).to.equal('node.http.client'),
482+
span => expect(span.data.http.method).to.equal('GET')
483+
]);
484+
});
485+
});
486+
});
487+
345488
function verify(controls, response, apiPath, operation, withError, tableName) {
346489
return retry(
347490
() =>

packages/core/src/tracing/backend_mappers/index.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44

55
'use strict';
66

7+
const mapper = require('./mapper');
78
module.exports = {
89
get transform() {
910
return (/** @type {import('../../core').InstanaBaseSpan} */ span) => {
1011
try {
11-
return require(`./${span.n}_mapper`).transform(span);
12-
} catch {
12+
return mapper.transform(span);
13+
} catch (error) {
1314
return span;
1415
}
1516
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* (c) Copyright IBM Corp. 2024
3+
*/
4+
5+
'use strict';
6+
7+
/**
8+
* @typedef {Object<string, string>} FieldMapping
9+
* @typedef {Object<string, FieldMapping>} FieldMappings
10+
*/
11+
12+
/**
13+
* Field mappings for different span types.
14+
*
15+
* This FieldMappings defines how internal span fields are mapped to backend fields
16+
* for various span types (e.g., dynamodb, redis).
17+
*
18+
* As new span types needs to add, simply add their respective mappings
19+
* following the same format (e.g., 'internal-field': 'backend-field').
20+
*
21+
* @type {FieldMappings}
22+
*/
23+
const fieldMappings = {
24+
dynamodb: {
25+
/// Maps internal field 'operation' to backend field 'op'
26+
operation: 'op'
27+
},
28+
redis: {
29+
operation: 'command'
30+
}
31+
};
32+
33+
/**
34+
* Transforms span data fields to match the backend format.
35+
*
36+
* @param {import('../../core').InstanaBaseSpan} span
37+
* @returns {import('../../core').InstanaBaseSpan} The transformed span.
38+
*/
39+
module.exports.transform = span => {
40+
const spanName = span.n;
41+
const mappings = fieldMappings[spanName];
42+
// If no mappings exist for the span name or the span data, return the original span
43+
if (!mappings || !span.data[spanName]) return span;
44+
45+
Object.keys(span.data[spanName]).forEach(internalField => {
46+
// Only proceed if there's a mapping for the internal field in the current span type
47+
if (internalField in mappings) {
48+
const backendField = mappings[internalField];
49+
50+
if (backendField) {
51+
span.data[spanName][backendField] = span.data[spanName][internalField];
52+
delete span.data[spanName][internalField];
53+
} else {
54+
// If backendField is falsy, remove the internalField from span data
55+
delete span.data[spanName][internalField];
56+
}
57+
}
58+
});
59+
60+
return span;
61+
};

packages/core/src/tracing/backend_mappers/redis_mapper.js

-29
This file was deleted.

packages/core/src/tracing/instrumentation/cloud/aws-sdk/v2/dynamodb.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class InstanaAWSDynamoDB extends InstanaAWSProduct {
100100
buildSpanData(ctx, operation, params) {
101101
const operationInfo = operationsInfo[operation];
102102
const spanData = {
103-
op: operationInfo.op,
103+
operation: operationInfo.op,
104104
region: ctx.config.region
105105
};
106106

packages/core/src/tracing/instrumentation/cloud/aws-sdk/v3/dynamodb.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class InstanaAWSDynamoDB extends InstanaAWSProduct {
6060

6161
buildSpanData(operation, params) {
6262
const spanData = {
63-
op: this.convertOperationName(operation)
63+
operation: this.convertOperationName(operation)
6464
};
6565

6666
if (params && params.TableName) {

0 commit comments

Comments
 (0)