Skip to content

Commit

Permalink
feat(profiling): attach sdk info to chunks (#13145)
Browse files Browse the repository at this point in the history
Attach SDK info to individual profile chunks
  • Loading branch information
JonasBa authored Aug 1, 2024
1 parent 21b0dae commit 98160a5
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 6 deletions.
18 changes: 13 additions & 5 deletions packages/profiling-node/src/integration.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable max-lines */

import {
defineIntegration,
getCurrentScope,
Expand Down Expand Up @@ -231,11 +233,17 @@ class ContinuousProfiler {
}

DEBUG_BUILD && logger.log(`[Profiling] Profile chunk ${this._chunkData.id} sent to Sentry.`);
const chunk = createProfilingChunkEvent(this._client, this._client.getOptions(), profile, {
chunk_id: this._chunkData.id,
trace_id: this._chunkData.startTraceID,
profiler_id: this._profilerId,
});
const chunk = createProfilingChunkEvent(
this._client,
this._client.getOptions(),
profile,
this._client.getSdkMetadata()?.sdk,
{
chunk_id: this._chunkData.id,
trace_id: this._chunkData.startTraceID,
profiler_id: this._profilerId,
},
);

if (!chunk) {
DEBUG_BUILD && logger.log(`[Profiling] Failed to create profile chunk for: ${this._chunkData.id}`);
Expand Down
8 changes: 8 additions & 0 deletions packages/profiling-node/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,14 @@ function createProfileChunkPayload(
trace_id,
profiler_id,
chunk_id,
sdk,
}: {
release: string;
environment: string;
trace_id: string | undefined;
chunk_id: string;
profiler_id: string;
sdk: SdkInfo | undefined;
},
): ProfileChunk {
// Log a warning if the profile has an invalid traceId (should be uuidv4).
Expand All @@ -213,6 +215,10 @@ function createProfileChunkPayload(

const profile: ProfileChunk = {
chunk_id: chunk_id,
client_sdk: {
name: sdk?.name ?? 'sentry.javascript.node',
version: sdk?.version ?? '0.0.0',
},
profiler_id: profiler_id,
platform: 'node',
version: CONTINUOUS_FORMAT_VERSION,
Expand All @@ -235,6 +241,7 @@ export function createProfilingChunkEvent(
client: Client,
options: { release?: string; environment?: string },
profile: RawChunkCpuProfile,
sdk: SdkInfo | undefined,
identifiers: { trace_id: string | undefined; chunk_id: string; profiler_id: string },
): ProfileChunk | null {
if (!isValidProfileChunk(profile)) {
Expand All @@ -247,6 +254,7 @@ export function createProfilingChunkEvent(
trace_id: identifiers.trace_id ?? '',
chunk_id: identifiers.chunk_id,
profiler_id: identifiers.profiler_id,
sdk,
});
}

Expand Down
46 changes: 45 additions & 1 deletion packages/profiling-node/test/spanProfileUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as Sentry from '@sentry/node';

import { getMainCarrier } from '@sentry/core';
import type { NodeClientOptions } from '@sentry/node/build/types/types';
import type { Transport } from '@sentry/types';
import type { ProfileChunk, Transport } from '@sentry/types';
import { GLOBAL_OBJ, createEnvelope, logger } from '@sentry/utils';
import { CpuProfilerBindings } from '../src/cpu_profiler';
import { type ProfilingIntegration, _nodeProfilingIntegration } from '../src/integration';
Expand Down Expand Up @@ -402,6 +402,50 @@ describe('continuous profiling', () => {
delete getMainCarrier().__SENTRY__;
});

it('attaches sdk metadata to chunks', () => {
// @ts-expect-error we just mock the return type and ignore the signature
jest.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => {
return {
samples: [
{
stack_id: 0,
thread_id: '0',
elapsed_since_start_ns: '10',
},
{
stack_id: 0,
thread_id: '0',
elapsed_since_start_ns: '10',
},
],
measurements: {},
stacks: [[0]],
frames: [],
resources: [],
profiler_logging_mode: 'lazy',
};
});

const [client, transport] = makeContinuousProfilingClient();
Sentry.setCurrentClient(client);
client.init();

const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({}));

const integration = client.getIntegrationByName<ProfilingIntegration>('ProfilingIntegration');
if (!integration) {
throw new Error('Profiling integration not found');
}
integration._profiler.start();
jest.advanceTimersByTime(1000);
integration._profiler.stop();
jest.advanceTimersByTime(1000);

const profile = transportSpy.mock.calls?.[0]?.[0]?.[1]?.[0]?.[1] as ProfileChunk;
expect(profile.client_sdk.name).toBe('sentry.javascript.node');
expect(profile.client_sdk.version).toEqual(expect.stringMatching(/\d+\.\d+\.\d+/));
});

it('initializes the continuous profiler and binds the sentry client', () => {
const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling');

Expand Down
4 changes: 4 additions & 0 deletions packages/types/src/profiling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,8 @@ export interface Profile extends BaseProfile<ThreadCpuProfile> {
export interface ProfileChunk extends BaseProfile<ContinuousThreadCpuProfile> {
chunk_id: string;
profiler_id: string;
client_sdk: {
name: string;
version: string;
};
}

0 comments on commit 98160a5

Please sign in to comment.