Skip to content

Commit

Permalink
feat(core): Add getTraceMetaTags function (#13201)
Browse files Browse the repository at this point in the history
Export function `getTraceMetaTags` that gives users an easy way to get stringified Html
meta tags for server->client trace propagation.

---------

Co-authored-by: Andrei <168741329+andreiborza@users.noreply.github.com>
  • Loading branch information
Lms24 and andreiborza committed Aug 6, 2024
1 parent 64d80dd commit 05684d4
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
const Sentry = require('@sentry/node');

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
tracesSampleRate: 1.0,
transport: loggingTransport,
});

// express must be required after Sentry is initialized
const express = require('express');
const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests');

const app = express();

app.get('/test', (_req, res) => {
res.send({
response: `
<html>
<head>
${Sentry.getTraceMetaTags()}
</head>
<body>
Hi :)
</body>
</html>
`,
});
});

Sentry.setupExpressErrorHandler(app);

startExpressServerAndSendPortToRunner(app);
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { cleanupChildProcesses, createRunner } from '../../../utils/runner';

describe('getTraceMetaTags', () => {
afterAll(() => {
cleanupChildProcesses();
});

test('injects sentry tracing <meta> tags', async () => {
const traceId = 'cd7ee7a6fe3ebe7ab9c3271559bc203c';
const parentSpanId = '100ff0980e7a4ead';

const runner = createRunner(__dirname, 'server.js').start();

const response = await runner.makeRequest('get', '/test', {
'sentry-trace': `${traceId}-${parentSpanId}-1`,
baggage: 'sentry-environment=production',
});

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const html = response?.response as unknown as string;

expect(html).toMatch(/<meta name="sentry-trace" content="cd7ee7a6fe3ebe7ab9c3271559bc203c-[a-z0-9]{16}-1"\/>/);
expect(html).toContain('<meta name="baggage" content="sentry-environment=production"/>');
});
});
1 change: 1 addition & 0 deletions packages/astro/src/index.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export {
getSpanDescendants,
getSpanStatusFromHttpCode,
getTraceData,
getTraceMetaTags,
graphqlIntegration,
hapiIntegration,
httpIntegration,
Expand Down
12 changes: 4 additions & 8 deletions packages/astro/src/server/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getActiveSpan,
getClient,
getCurrentScope,
getTraceMetaTags,
setHttpStatus,
startSpan,
withIsolationScope,
Expand All @@ -14,8 +15,6 @@ import type { Client, Scope, Span, SpanAttributes } from '@sentry/types';
import { addNonEnumerableProperty, objectify, stripUrlQueryAndFragment } from '@sentry/utils';
import type { APIContext, MiddlewareResponseHandler } from 'astro';

import { getTraceData } from '@sentry/node';

type MiddlewareOptions = {
/**
* If true, the client IP will be attached to the event by calling `setUser`.
Expand Down Expand Up @@ -189,16 +188,13 @@ function addMetaTagToHead(htmlChunk: string, scope: Scope, client: Client, span?
if (typeof htmlChunk !== 'string') {
return htmlChunk;
}
const { 'sentry-trace': sentryTrace, baggage } = getTraceData(span, scope, client);
const metaTags = getTraceMetaTags(span, scope, client);

if (!sentryTrace) {
if (!metaTags) {
return htmlChunk;
}

const sentryTraceMeta = `<meta name="sentry-trace" content="${sentryTrace}"/>`;
const baggageMeta = baggage && `<meta name="baggage" content="${baggage}"/>`;

const content = `<head>\n${sentryTraceMeta}`.concat(baggageMeta ? `\n${baggageMeta}` : '', '\n');
const content = `<head>${metaTags}`;

return htmlChunk.replace('<head>', content);
}
Expand Down
10 changes: 6 additions & 4 deletions packages/astro/test/server/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ describe('sentryMiddleware', () => {
});
vi.spyOn(SentryNode, 'getActiveSpan').mockImplementation(getSpanMock);
vi.spyOn(SentryNode, 'getClient').mockImplementation(() => ({}) as Client);
vi.spyOn(SentryNode, 'getTraceData').mockImplementation(() => ({
'sentry-trace': '123',
baggage: 'abc',
}));
vi.spyOn(SentryNode, 'getTraceMetaTags').mockImplementation(
() => `
<meta name="sentry-trace" content="123">
<meta name="baggage" content="abc">
`,
);
vi.spyOn(SentryCore, 'getDynamicSamplingContextFromSpan').mockImplementation(() => ({
transaction: 'test',
}));
Expand Down
1 change: 1 addition & 0 deletions packages/aws-serverless/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export {
getGlobalScope,
getIsolationScope,
getTraceData,
getTraceMetaTags,
setCurrentClient,
Scope,
SDK_VERSION,
Expand Down
1 change: 1 addition & 0 deletions packages/bun/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export {
getGlobalScope,
getIsolationScope,
getTraceData,
getTraceMetaTags,
setCurrentClient,
Scope,
SDK_VERSION,
Expand Down
1 change: 1 addition & 0 deletions packages/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export {
getActiveSpan,
getRootSpan,
getTraceData,
getTraceMetaTags,
startSpan,
startInactiveSpan,
startSpanManual,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export {
export { parseSampleRate } from './utils/parseSampleRate';
export { applySdkMetadata } from './utils/sdkMetadata';
export { getTraceData } from './utils/traceData';
export { getTraceMetaTags } from './utils/meta';
export { DEFAULT_ENVIRONMENT } from './constants';
export { addBreadcrumb } from './breadcrumbs';
export { functionToStringIntegration } from './integrations/functiontostring';
Expand Down
29 changes: 29 additions & 0 deletions packages/core/src/utils/meta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Client, Scope, Span } from '@sentry/types';
import { getTraceData } from './traceData';

/**
* Returns a string of meta tags that represent the current trace data.
*
* You can use this to propagate a trace from your server-side rendered Html to the browser.
* This function returns up to two meta tags, `sentry-trace` and `baggage`, depending on the
* current trace data state.
*
* @example
* Usage example:
*
* ```js
* function renderHtml() {
* return `
* <head>
* ${getTraceMetaTags()}
* </head>
* `;
* }
* ```
*
*/
export function getTraceMetaTags(span?: Span, scope?: Scope, client?: Client): string {
return Object.entries(getTraceData(span, scope, client))
.map(([key, value]) => `<meta name="${key}" content="${value}"/>`)
.join('\n');
}
30 changes: 30 additions & 0 deletions packages/core/test/lib/utils/meta.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { getTraceMetaTags } from '../../../src/utils/meta';
import * as TraceDataModule from '../../../src/utils/traceData';

describe('getTraceMetaTags', () => {
it('renders baggage and sentry-trace values to stringified Html meta tags', () => {
jest.spyOn(TraceDataModule, 'getTraceData').mockReturnValueOnce({
'sentry-trace': '12345678901234567890123456789012-1234567890123456-1',
baggage: 'sentry-environment=production',
});

expect(getTraceMetaTags()).toBe(`<meta name="sentry-trace" content="12345678901234567890123456789012-1234567890123456-1"/>
<meta name="baggage" content="sentry-environment=production"/>`);
});

it('renders just sentry-trace values to stringified Html meta tags', () => {
jest.spyOn(TraceDataModule, 'getTraceData').mockReturnValueOnce({
'sentry-trace': '12345678901234567890123456789012-1234567890123456-1',
});

expect(getTraceMetaTags()).toBe(
'<meta name="sentry-trace" content="12345678901234567890123456789012-1234567890123456-1"/>',
);
});

it('returns an empty string if neither sentry-trace nor baggage values are available', () => {
jest.spyOn(TraceDataModule, 'getTraceData').mockReturnValueOnce({});

expect(getTraceMetaTags()).toBe('');
});
});
1 change: 1 addition & 0 deletions packages/deno/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export {
getActiveSpan,
getRootSpan,
getTraceData,
getTraceMetaTags,
startSpan,
startInactiveSpan,
startSpanManual,
Expand Down
1 change: 1 addition & 0 deletions packages/google-cloud-serverless/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export {
getGlobalScope,
getIsolationScope,
getTraceData,
getTraceMetaTags,
setCurrentClient,
Scope,
SDK_VERSION,
Expand Down
1 change: 1 addition & 0 deletions packages/node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export {
getCurrentScope,
getIsolationScope,
getTraceData,
getTraceMetaTags,
withScope,
withIsolationScope,
captureException,
Expand Down
1 change: 1 addition & 0 deletions packages/sveltekit/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export {
getSpanDescendants,
getSpanStatusFromHttpCode,
getTraceData,
getTraceMetaTags,
graphqlIntegration,
hapiIntegration,
httpIntegration,
Expand Down
1 change: 1 addition & 0 deletions packages/vercel-edge/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export {
getActiveSpan,
getRootSpan,
getTraceData,
getTraceMetaTags,
startSpan,
startInactiveSpan,
startSpanManual,
Expand Down

0 comments on commit 05684d4

Please sign in to comment.