1+ import type { Client } from '../client' ;
12import { defineIntegration } from '../integration' ;
3+ import {
4+ SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD ,
5+ SEMANTIC_ATTRIBUTE_URL_FULL ,
6+ SEMANTIC_ATTRIBUTE_URL_QUERY ,
7+ SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS ,
8+ } from '../semanticAttributes' ;
9+ import { safeSetSpanJSONAttributes } from '../spans/spanFirstUtils' ;
210import type { Event } from '../types-hoist/event' ;
311import type { IntegrationFn } from '../types-hoist/integration' ;
12+ import type { ClientOptions } from '../types-hoist/options' ;
413import type { RequestEventData } from '../types-hoist/request' ;
14+ import type { BaseTransportOptions } from '../types-hoist/transport' ;
515import { parseCookie } from '../utils/cookie' ;
16+ import { httpHeadersToSpanAttributes } from '../utils/request' ;
617import { getClientIPAddress , ipHeaderNames } from '../vendor/getIpAddress' ;
718
819interface RequestDataIncludeOptions {
@@ -40,16 +51,67 @@ const _requestDataIntegration = ((options: RequestDataIntegrationOptions = {}) =
4051
4152 return {
4253 name : INTEGRATION_NAME ,
54+ setup ( client ) {
55+ client . on ( 'processSegmentSpan' , ( spanJSON , { scopeData } ) => {
56+ const { sdkProcessingMetadata = { } } = scopeData ;
57+ const { normalizedRequest, ipAddress } = sdkProcessingMetadata ;
58+
59+ if ( ! normalizedRequest ) {
60+ return ;
61+ }
62+
63+ const includeWithDefaultPiiApplied : RequestDataIncludeOptions = getIncludeWithDefaultPiiApplied (
64+ include ,
65+ client ,
66+ ) ;
67+
68+ // no need to check for include after calling `extractNormalizedRequestData`
69+ // because it already internally only return what's permitted by `include`
70+ const { method, url, query_string, headers, data, env } = extractNormalizedRequestData (
71+ normalizedRequest ,
72+ includeWithDefaultPiiApplied ,
73+ ) ;
74+
75+ safeSetSpanJSONAttributes ( spanJSON , {
76+ ...( method ? { [ SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD ] : method } : { } ) ,
77+ ...( url ? { [ SEMANTIC_ATTRIBUTE_URL_FULL ] : url } : { } ) ,
78+ ...( query_string ? { [ SEMANTIC_ATTRIBUTE_URL_QUERY ] : query_string } : { } ) ,
79+ ...( headers ? httpHeadersToSpanAttributes ( headers , client . getOptions ( ) . sendDefaultPii ) : { } ) ,
80+ // TODO: Apparently, Relay still needs Pii rule updates, so I'm leaving this out for now
81+ // ...(cookies
82+ // ? Object.keys(cookies).reduce(
83+ // (acc, cookieName) => ({
84+ // ...acc,
85+ // [`http.request.header.cookie.${cookieName}`]: cookies[cookieName] ?? '',
86+ // }),
87+ // {} as Record<string, string>,
88+ // )
89+ // : {}),
90+ ...( include . ip
91+ ? {
92+ [ SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS ] :
93+ ( normalizedRequest . headers && getClientIPAddress ( normalizedRequest . headers ) ) || ipAddress ,
94+ }
95+ : { } ) ,
96+ ...( data ? { 'http.request.body.content' : data } : { } ) ,
97+ ...( env
98+ ? {
99+ 'http.request.env' : Object . keys ( env ) . reduce (
100+ ( acc , key ) => ( { ...acc , [ key ] : env [ key ] ?? '' } ) ,
101+ { } as Record < string , string > ,
102+ ) ,
103+ }
104+ : { } ) ,
105+ } ) ;
106+ } ) ;
107+ } ,
43108 // TODO (span-streaming): probably fine to leave as-is for errors.
44109 // For spans, we go through global context -> attribute conversion or omit this completely (TBD)
45110 processEvent ( event , _hint , client ) {
46111 const { sdkProcessingMetadata = { } } = event ;
47112 const { normalizedRequest, ipAddress } = sdkProcessingMetadata ;
48113
49- const includeWithDefaultPiiApplied : RequestDataIncludeOptions = {
50- ...include ,
51- ip : include . ip ?? client . getOptions ( ) . sendDefaultPii ,
52- } ;
114+ const includeWithDefaultPiiApplied : RequestDataIncludeOptions = getIncludeWithDefaultPiiApplied ( include , client ) ;
53115
54116 if ( normalizedRequest ) {
55117 addNormalizedRequestDataToEvent ( event , normalizedRequest , { ipAddress } , includeWithDefaultPiiApplied ) ;
@@ -66,6 +128,21 @@ const _requestDataIntegration = ((options: RequestDataIntegrationOptions = {}) =
66128 */
67129export const requestDataIntegration = defineIntegration ( _requestDataIntegration ) ;
68130
131+ const getIncludeWithDefaultPiiApplied = (
132+ include : {
133+ cookies ?: boolean ;
134+ data ?: boolean ;
135+ headers ?: boolean ;
136+ ip ?: boolean ;
137+ query_string ?: boolean ;
138+ url ?: boolean ;
139+ } ,
140+ client : Client < ClientOptions < BaseTransportOptions > > ,
141+ ) : RequestDataIncludeOptions => ( {
142+ ...include ,
143+ ip : include . ip ?? client . getOptions ( ) . sendDefaultPii ,
144+ } ) ;
145+
69146/**
70147 * Add already normalized request data to an event.
71148 * This mutates the passed in event.
@@ -105,14 +182,14 @@ function extractNormalizedRequestData(
105182
106183 // Remove the Cookie header in case cookie data should not be included in the event
107184 if ( ! include . cookies ) {
108- delete ( headers as { cookie ?: string } ) . cookie ;
185+ delete headers . cookie ;
109186 }
110187
111188 // Remove IP headers in case IP data should not be included in the event
112189 if ( ! include . ip ) {
113190 ipHeaderNames . forEach ( ipHeaderName => {
114191 // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
115- delete ( headers as Record < string , unknown > ) [ ipHeaderName ] ;
192+ delete headers [ ipHeaderName ] ;
116193 } ) ;
117194 }
118195 }
0 commit comments