Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,10 @@ describe('MongoDB experimental Test', () => {
'db.connection_string': expect.any(String),
'net.peer.name': expect.any(String),
'net.peer.port': expect.any(Number),
'db.statement':
'{"title":"?","_id":{"_bsontype":"?","id":{"0":"?","1":"?","2":"?","3":"?","4":"?","5":"?","6":"?","7":"?","8":"?","9":"?","10":"?","11":"?"}}}',
'db.statement': '{"title":"?","_id":{"_bsontype":"?","id":"?"}}',
'otel.kind': 'CLIENT',
},
description:
'{"title":"?","_id":{"_bsontype":"?","id":{"0":"?","1":"?","2":"?","3":"?","4":"?","5":"?","6":"?","7":"?","8":"?","9":"?","10":"?","11":"?"}}}',
description: '{"title":"?","_id":{"_bsontype":"?","id":"?"}}',
op: 'db',
origin: 'auto.db.otel.mongo',
}),
Expand Down Expand Up @@ -162,12 +160,10 @@ describe('MongoDB experimental Test', () => {
'db.connection_string': expect.any(String),
'net.peer.name': expect.any(String),
'net.peer.port': expect.any(Number),
'db.statement':
'{"endSessions":[{"id":{"_bsontype":"?","sub_type":"?","position":"?","buffer":{"0":"?","1":"?","2":"?","3":"?","4":"?","5":"?","6":"?","7":"?","8":"?","9":"?","10":"?","11":"?","12":"?","13":"?","14":"?","15":"?"}}}]}',
'db.statement': '{"endSessions":[{"id":{"_bsontype":"?","sub_type":"?","position":"?","buffer":"?"}}]}',
'otel.kind': 'CLIENT',
},
description:
'{"endSessions":[{"id":{"_bsontype":"?","sub_type":"?","position":"?","buffer":{"0":"?","1":"?","2":"?","3":"?","4":"?","5":"?","6":"?","7":"?","8":"?","9":"?","10":"?","11":"?","12":"?","13":"?","14":"?","15":"?"}}}]}',
description: '{"endSessions":[{"id":{"_bsontype":"?","sub_type":"?","position":"?","buffer":"?"}}]}',
op: 'db',
origin: 'auto.db.otel.mongo',
}),
Expand Down
46 changes: 46 additions & 0 deletions packages/node/src/integrations/tracing/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,58 @@ export const instrumentMongo = generateInstrumentOnce(
INTEGRATION_NAME,
() =>
new MongoDBInstrumentation({
dbStatementSerializer: _defaultDbStatementSerializer,
responseHook(span) {
addOriginToSpan(span, 'auto.db.otel.mongo');
},
}),
);

/**
* Replaces values in document with '?', hiding PII and helping grouping.
*/
export function _defaultDbStatementSerializer(commandObj: Record<string, unknown>): string {
const resultObj = _scrubStatement(commandObj);
return JSON.stringify(resultObj);
}

function _scrubStatement(value: unknown): unknown {
if (Array.isArray(value)) {
return value.map(element => _scrubStatement(element));
}

if (isCommandObj(value)) {
const initial: Record<string, unknown> = {};
return Object.entries(value)
.map(([key, element]) => [key, _scrubStatement(element)])
.reduce((prev, current) => {
if (isCommandEntry(current)) {
prev[current[0]] = current[1];
}
return prev;
}, initial);
}

// A value like string or number, possible contains PII, scrub it
return '?';
}

function isCommandObj(value: Record<string, unknown> | unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !isBuffer(value);
}

function isBuffer(value: unknown): boolean {
let isBuffer = false;
if (typeof Buffer !== 'undefined') {
isBuffer = Buffer.isBuffer(value);
}
return isBuffer;
Comment on lines +55 to +59
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary, all our node versions should support this so let's just check normally here :)

}

function isCommandEntry(value: [string, unknown] | unknown): value is [string, unknown] {
return Array.isArray(value);
}

const _mongoIntegration = (() => {
return {
name: INTEGRATION_NAME,
Expand Down
72 changes: 72 additions & 0 deletions packages/node/test/integrations/tracing/mongo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb';

import {
_defaultDbStatementSerializer,
instrumentMongo,
mongoIntegration,
} from '../../../src/integrations/tracing/mongo';
import { INSTRUMENTED } from '../../../src/otel/instrument';

jest.mock('@opentelemetry/instrumentation-mongodb');

describe('Mongo', () => {
beforeEach(() => {
jest.clearAllMocks();
delete INSTRUMENTED.Mongo;

(MongoDBInstrumentation as unknown as jest.SpyInstance).mockImplementation(() => {
return {
setTracerProvider: () => undefined,
setMeterProvider: () => undefined,
getConfig: () => ({}),
setConfig: () => ({}),
enable: () => undefined,
};
});
});

it('defaults are correct for instrumentMongo', () => {
instrumentMongo();

expect(MongoDBInstrumentation).toHaveBeenCalledTimes(1);
expect(MongoDBInstrumentation).toHaveBeenCalledWith({
dbStatementSerializer: expect.any(Function),
responseHook: expect.any(Function),
});
});

it('defaults are correct for mongoIntegration', () => {
mongoIntegration().setupOnce!();

expect(MongoDBInstrumentation).toHaveBeenCalledTimes(1);
expect(MongoDBInstrumentation).toHaveBeenCalledWith({
responseHook: expect.any(Function),
dbStatementSerializer: expect.any(Function),
});
});

describe('_defaultDbStatementSerializer', () => {
it('rewrites strings as ?', () => {
const serialized = _defaultDbStatementSerializer({
find: 'foo',
});
expect(JSON.parse(serialized).find).toBe('?');
});

it('rewrites nested strings as ?', () => {
const serialized = _defaultDbStatementSerializer({
find: {
inner: 'foo',
},
});
expect(JSON.parse(serialized).find.inner).toBe('?');
});

it('rewrites Buffer as ?', () => {
const serialized = _defaultDbStatementSerializer({
find: Buffer.from('foo', 'utf8'),
});
expect(JSON.parse(serialized).find).toBe('?');
});
});
});
Loading