Skip to content

ref: Rename baggage env header to trace #5128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 23, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
72 changes: 36 additions & 36 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,42 +111,42 @@ jobs:
# `job_build` can't see `job_install_deps` and what it returned)
dependency_cache_key: ${{ needs.job_install_deps.outputs.dependency_cache_key }}

job_build_aws_lambda_layer:
name: Build AWS Lambda Layer
needs: job_build
runs-on: ubuntu-latest
steps:
- name: Check out current commit (${{ env.HEAD_COMMIT }})
uses: actions/checkout@v2
with:
ref: ${{ env.HEAD_COMMIT }}
- name: Set up Node
uses: actions/setup-node@v1
with:
node-version: ${{ env.DEFAULT_NODE_VERSION }}
- name: Check dependency cache
uses: actions/cache@v2
with:
path: ${{ env.CACHED_DEPENDENCY_PATHS }}
key: ${{ needs.job_build.outputs.dependency_cache_key }}
- name: Check build cache
uses: actions/cache@v2
with:
path: ${{ env.CACHED_BUILD_PATHS }}
key: ${{ env.BUILD_CACHE_KEY }}
- name: Get SDK version
run: |
export SDK_VERSION=$(cat dist-serverless/version)
echo "SDK_VERSION=$SDK_VERSION" | tee -a $GITHUB_ENV
- uses: actions/upload-artifact@v3
with:
name: ${{ env.HEAD_COMMIT }}
path: |
dist-serverless/*
- uses: getsentry/action-build-aws-lambda-extension@v1
with:
artifact_name: ${{ env.HEAD_COMMIT }}
zip_file_name: sentry-node-serverless-${{ env.SDK_VERSION }}.zip
# job_build_aws_lambda_layer:
# name: Build AWS Lambda Layer
# needs: job_build
# runs-on: ubuntu-latest
# steps:
# - name: Check out current commit (${{ env.HEAD_COMMIT }})
# uses: actions/checkout@v2
# with:
# ref: ${{ env.HEAD_COMMIT }}
# - name: Set up Node
# uses: actions/setup-node@v1
# with:
# node-version: ${{ env.DEFAULT_NODE_VERSION }}
# - name: Check dependency cache
# uses: actions/cache@v2
# with:
# path: ${{ env.CACHED_DEPENDENCY_PATHS }}
# key: ${{ needs.job_build.outputs.dependency_cache_key }}
# - name: Check build cache
# uses: actions/cache@v2
# with:
# path: ${{ env.CACHED_BUILD_PATHS }}
# key: ${{ env.BUILD_CACHE_KEY }}
# - name: Get SDK version
# run: |
# export SDK_VERSION=$(cat dist-serverless/version)
# echo "SDK_VERSION=$SDK_VERSION" | tee -a $GITHUB_ENV
# - uses: actions/upload-artifact@v3
# with:
# name: ${{ env.HEAD_COMMIT }}
# path: |
# dist-serverless/*
# - uses: getsentry/action-build-aws-lambda-extension@v1
# with:
# artifact_name: ${{ env.HEAD_COMMIT }}
# zip_file_name: sentry-node-serverless-${{ env.SDK_VERSION }}.zip

job_size_check:
name: Size Check
Expand Down
44 changes: 21 additions & 23 deletions packages/core/src/envelope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,7 @@ import {
SessionEnvelope,
SessionItem,
} from '@sentry/types';
import {
BaggageObj,
createBaggage,
createEnvelope,
dropUndefinedKeys,
dsnToString,
isBaggageEmpty,
serializeBaggage,
} from '@sentry/utils';
import { createEnvelope, dropUndefinedKeys, dsnToString } from '@sentry/utils';

/** Extract sdk info from from the API metadata */
function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined {
Expand Down Expand Up @@ -128,24 +120,30 @@ function createEventEnvelopeHeaders(
tunnel: string | undefined,
dsn: DsnComponents,
): EventEnvelopeHeaders {
const baggage =
event.type === 'transaction' &&
createBaggage(
dropUndefinedKeys({
environment: event.environment,
release: event.release,
transaction: event.transaction,
userid: event.user && event.user.id,
// user.segment currently doesn't exist explicitly in interface User (just as a record key)
usersegment: event.user && event.user.segment,
} as BaggageObj),
);

return {
event_id: event.event_id as string,
sent_at: new Date().toISOString(),
...(sdkInfo && { sdk: sdkInfo }),
...(!!tunnel && { dsn: dsnToString(dsn) }),
...(baggage && !isBaggageEmpty(baggage) && { baggage: serializeBaggage(baggage) }),
...(event.type === 'transaction' &&
event.contexts &&
event.contexts.trace && {
// TODO: Grab this from baggage
trace: dropUndefinedKeys({
// Trace context must be defined for transactions
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
trace_id: event.contexts!.trace.trace_id as string,
environment: event.environment,
release: event.release,
transaction: event.transaction,
user:
event.user &&
dropUndefinedKeys({
id: event.user.id,
segment: event.user.segment,
}),
public_key: dsn.publicKey,
}),
}),
};
}
67 changes: 53 additions & 14 deletions packages/core/test/lib/envelope.test.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,62 @@
import { DsnComponents, Event } from '@sentry/types';
import { EventTraceContext } from '@sentry/types/build/types/envelope';

import { createEventEnvelope } from '../../src/envelope';

const testDsn: DsnComponents = { protocol: 'https', projectId: 'abc', host: 'testry.io' };
const testDsn: DsnComponents = { protocol: 'https', projectId: 'abc', host: 'testry.io', publicKey: 'pubKey123' };

describe('createEventEnvelope', () => {
describe('baggage header', () => {
it("doesn't add baggage header if event is not a transaction", () => {
describe('trace header', () => {
it("doesn't add trace header if event is not a transaction", () => {
const event: Event = {};
const envelopeHeaders = createEventEnvelope(event, testDsn)[0];

expect(envelopeHeaders).toBeDefined();
expect(envelopeHeaders.baggage).toBeUndefined();
expect(envelopeHeaders.trace).toBeUndefined();
});

it("doesn't add baggage header if no baggage data is available", () => {
it('adds minimal trace data if event is a transaction and no other baggage-related data is available', () => {
const event: Event = {
type: 'transaction',
contexts: {
trace: {
trace_id: '1234',
},
},
};
const envelopeHeaders = createEventEnvelope(event, testDsn)[0];

expect(envelopeHeaders).toBeDefined();
expect(envelopeHeaders.baggage).toBeUndefined();
expect(envelopeHeaders.trace).toEqual({ trace_id: '1234', public_key: 'pubKey123' });
});

const testTable: Array<[string, Event, string]> = [
['adds only baggage item', { type: 'transaction', release: '1.0.0' }, 'sentry-release=1.0.0'],
const testTable: Array<[string, Event, EventTraceContext]> = [
[
'adds only baggage item',
{
type: 'transaction',
release: '1.0.0',
contexts: {
trace: {
trace_id: '1234',
},
},
},
{ release: '1.0.0', trace_id: '1234', public_key: 'pubKey123' },
],
[
'adds two baggage items',
{ type: 'transaction', release: '1.0.0', environment: 'prod' },
'sentry-environment=prod,sentry-release=1.0.0',
{
type: 'transaction',
release: '1.0.0',
environment: 'prod',
contexts: {
trace: {
trace_id: '1234',
},
},
},
{ release: '1.0.0', environment: 'prod', trace_id: '1234', public_key: 'pubKey123' },
],
[
'adds all baggageitems',
Expand All @@ -39,16 +66,28 @@ describe('createEventEnvelope', () => {
environment: 'prod',
user: { id: 'bob', segment: 'segmentA' },
transaction: 'TX',
contexts: {
trace: {
trace_id: '1234',
},
},
},
{
release: '1.0.0',
environment: 'prod',
user: { id: 'bob', segment: 'segmentA' },
transaction: 'TX',
Comment on lines +76 to +79
Copy link
Member

Choose a reason for hiding this comment

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

Thanks, this schema looks correct now based on the reference struct. However, we currently also expect the trace_id and the public_key of the DSN where the trace originated. Is this just missing in this test?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's missing in the test, can add!

trace_id: '1234',
public_key: 'pubKey123',
},
'sentry-environment=prod,sentry-release=1.0.0,sentry-transaction=TX,sentry-userid=bob,sentry-usersegment=segmentA',
],
];
it.each(testTable)('%s', (_: string, event, serializedBaggage) => {
it.each(testTable)('%s', (_: string, event, trace) => {
const envelopeHeaders = createEventEnvelope(event, testDsn)[0];

expect(envelopeHeaders).toBeDefined();
expect(envelopeHeaders.baggage).toBeDefined();
expect(envelopeHeaders.baggage).toEqual(serializedBaggage);
expect(envelopeHeaders.trace).toBeDefined();
expect(envelopeHeaders.trace).toEqual(trace);
});
});
});
19 changes: 13 additions & 6 deletions packages/integration-tests/suites/tracing/baggage/test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { expect } from '@playwright/test';
import { Event, EventEnvelopeHeaders } from '@sentry/types';
import { EventEnvelopeHeaders } from '@sentry/types';

import { sentryTest } from '../../../utils/fixtures';
import { envelopeHeaderRequestParser, getFirstSentryEnvelopeRequest } from '../../../utils/helpers';

sentryTest('should send baggage data in transaction envelope header', async ({ getLocalTestPath, page }) => {
sentryTest('should send trace context data in transaction envelope header', async ({ getLocalTestPath, page }) => {
const url = await getLocalTestPath({ testDir: __dirname });

const envHeader = await getFirstSentryEnvelopeRequest<EventEnvelopeHeaders>(page, url, envelopeHeaderRequestParser);

expect(envHeader.baggage).toBeDefined();
expect(envHeader.baggage).toEqual(
'sentry-environment=production,sentry-transaction=testTransactionBaggage,sentry-userid=user123,sentry-usersegment=segmentB',
);
expect(envHeader.trace).toBeDefined();
expect(envHeader.trace).toMatchObject({
environment: 'production',
transaction: 'testTransactionBaggage',
user: {
id: 'user123',
segment: 'segmentB',
},
public_key: 'public',
trace_id: expect.any(String),
});
});
17 changes: 17 additions & 0 deletions packages/types/src/baggage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type AllowedBaggageKeys = 'environment' | 'release' | 'userid' | 'transaction' | 'usersegment';
export type BaggageObj = Partial<Record<AllowedBaggageKeys, string> & Record<string, string>>;

/**
* The baggage data structure represents key,value pairs based on the baggage
* spec: https://www.w3.org/TR/baggage
*
* It is expected that users interact with baggage using the helpers methods:
* `createBaggage`, `getBaggageValue`, and `setBaggageValue`.
*
* Internally, the baggage data structure is a tuple of length 2, separating baggage values
* based on if they are related to Sentry or not. If the baggage values are
* set/used by sentry, they will be stored in an object to be easily accessed.
* If they are not, they are kept as a string to be only accessed when serialized
* at baggage propagation time.
*/
export type Baggage = [BaggageObj, string];
18 changes: 16 additions & 2 deletions packages/types/src/envelope.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { ClientReport } from './clientreport';
import { DsnComponents } from './dsn';
import { Event } from './event';
import { SdkInfo } from './sdkinfo';
import { Session, SessionAggregates } from './session';
import { TransactionSamplingMethod } from './transaction';
import { Transaction, TransactionSamplingMethod } from './transaction';
import { UserFeedback } from './user';

// Based on: https://develop.sentry.dev/sdk/envelopes/

// Based on https://github.com/getsentry/relay/blob/b23b8d3b2360a54aaa4d19ecae0231201f31df5e/relay-sampling/src/lib.rs#L685-L707
export type EventTraceContext = {
trace_id: Transaction['traceId'];
public_key: DsnComponents['publicKey'];
release?: string;
user?: {
id?: string;
segment?: string;
};
environment?: string;
transaction?: string;
};

export type EnvelopeItemType =
| 'client_report'
| 'user_report'
Expand Down Expand Up @@ -59,7 +73,7 @@ export type SessionItem =
| BaseEnvelopeItem<SessionAggregatesItemHeaders, SessionAggregates>;
export type ClientReportItem = BaseEnvelopeItem<ClientReportItemHeaders, ClientReport>;

export type EventEnvelopeHeaders = { event_id: string; sent_at: string; baggage?: string };
export type EventEnvelopeHeaders = { event_id: string; sent_at: string; trace?: EventTraceContext };
type SessionEnvelopeHeaders = { sent_at: string };
type ClientReportEnvelopeHeaders = BaseEnvelopeHeaders;

Expand Down
1 change: 1 addition & 0 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type { Attachment } from './attachment';
export type { AllowedBaggageKeys, Baggage, BaggageObj } from './baggage';
export type { Breadcrumb, BreadcrumbHint } from './breadcrumb';
export type { Client } from './client';
export type { ClientReport, Outcome, EventDropReason } from './clientreport';
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface User {
ip_address?: string;
email?: string;
username?: string;
segment?: string;
}

export interface UserFeedback {
Expand Down
25 changes: 7 additions & 18 deletions packages/utils/src/baggage.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@
import { Baggage, BaggageObj } from '@sentry/types';

import { IS_DEBUG_BUILD } from './flags';
import { logger } from './logger';

export type AllowedBaggageKeys = 'environment' | 'release' | 'userid' | 'transaction' | 'usersegment';
export type BaggageObj = Partial<Record<AllowedBaggageKeys, string> & Record<string, string>>;

/**
* The baggage data structure represents key,value pairs based on the baggage
* spec: https://www.w3.org/TR/baggage
*
* It is expected that users interact with baggage using the helpers methods:
* `createBaggage`, `getBaggageValue`, and `setBaggageValue`.
*
* Internally, the baggage data structure is a tuple of length 2, separating baggage values
* based on if they are related to Sentry or not. If the baggage values are
* set/used by sentry, they will be stored in an object to be easily accessed.
* If they are not, they are kept as a string to be only accessed when serialized
* at baggage propagation time.
*/
export type Baggage = [BaggageObj, string];

export const BAGGAGE_HEADER_NAME = 'baggage';

export const SENTRY_BAGGAGE_KEY_PREFIX = 'sentry-';
Expand Down Expand Up @@ -52,6 +36,11 @@ export function isBaggageEmpty(baggage: Baggage): boolean {
return Object.keys(baggage[0]).length === 0;
}

/** Returns Sentry specific baggage values */
export function getSentryBaggageItems(baggage: Baggage): BaggageObj {
return baggage[0];
}

/** Serialize a baggage object */
export function serializeBaggage(baggage: Baggage): string {
return Object.keys(baggage[0]).reduce((prev, key: keyof BaggageObj) => {
Expand Down