requestdata.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { extractPathForTransaction, addRequestDataToEvent } from '@sentry/utils';
  2. import { convertIntegrationFnToClass, defineIntegration } from '../integration.js';
  3. import { spanToJSON } from '../utils/spanUtils.js';
  4. const DEFAULT_OPTIONS = {
  5. include: {
  6. cookies: true,
  7. data: true,
  8. headers: true,
  9. ip: false,
  10. query_string: true,
  11. url: true,
  12. user: {
  13. id: true,
  14. username: true,
  15. email: true,
  16. },
  17. },
  18. transactionNamingScheme: 'methodPath',
  19. };
  20. const INTEGRATION_NAME = 'RequestData';
  21. const _requestDataIntegration = ((options = {}) => {
  22. const _addRequestData = addRequestDataToEvent;
  23. const _options = {
  24. ...DEFAULT_OPTIONS,
  25. ...options,
  26. include: {
  27. // @ts-expect-error It's mad because `method` isn't a known `include` key. (It's only here and not set by default in
  28. // `addRequestDataToEvent` for legacy reasons. TODO (v8): Change that.)
  29. method: true,
  30. ...DEFAULT_OPTIONS.include,
  31. ...options.include,
  32. user:
  33. options.include && typeof options.include.user === 'boolean'
  34. ? options.include.user
  35. : {
  36. ...DEFAULT_OPTIONS.include.user,
  37. // Unclear why TS still thinks `options.include.user` could be a boolean at this point
  38. ...((options.include || {}).user ),
  39. },
  40. },
  41. };
  42. return {
  43. name: INTEGRATION_NAME,
  44. // TODO v8: Remove this
  45. setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function
  46. processEvent(event, _hint, client) {
  47. // Note: In the long run, most of the logic here should probably move into the request data utility functions. For
  48. // the moment it lives here, though, until https://github.com/getsentry/sentry-javascript/issues/5718 is addressed.
  49. // (TL;DR: Those functions touch many parts of the repo in many different ways, and need to be clened up. Once
  50. // that's happened, it will be easier to add this logic in without worrying about unexpected side effects.)
  51. const { transactionNamingScheme } = _options;
  52. const { sdkProcessingMetadata = {} } = event;
  53. const req = sdkProcessingMetadata.request;
  54. if (!req) {
  55. return event;
  56. }
  57. // The Express request handler takes a similar `include` option to that which can be passed to this integration.
  58. // If passed there, we store it in `sdkProcessingMetadata`. TODO(v8): Force express and GCP people to use this
  59. // integration, so that all of this passing and conversion isn't necessary
  60. const addRequestDataOptions =
  61. sdkProcessingMetadata.requestDataOptionsFromExpressHandler ||
  62. sdkProcessingMetadata.requestDataOptionsFromGCPWrapper ||
  63. convertReqDataIntegrationOptsToAddReqDataOpts(_options);
  64. const processedEvent = _addRequestData(event, req, addRequestDataOptions);
  65. // Transaction events already have the right `transaction` value
  66. if (event.type === 'transaction' || transactionNamingScheme === 'handler') {
  67. return processedEvent;
  68. }
  69. // In all other cases, use the request's associated transaction (if any) to overwrite the event's `transaction`
  70. // value with a high-quality one
  71. const reqWithTransaction = req ;
  72. const transaction = reqWithTransaction._sentryTransaction;
  73. if (transaction) {
  74. const name = spanToJSON(transaction).description || '';
  75. // TODO (v8): Remove the nextjs check and just base it on `transactionNamingScheme` for all SDKs. (We have to
  76. // keep it the way it is for the moment, because changing the names of transactions in Sentry has the potential
  77. // to break things like alert rules.)
  78. const shouldIncludeMethodInTransactionName =
  79. getSDKName(client) === 'sentry.javascript.nextjs'
  80. ? name.startsWith('/api')
  81. : transactionNamingScheme !== 'path';
  82. const [transactionValue] = extractPathForTransaction(req, {
  83. path: true,
  84. method: shouldIncludeMethodInTransactionName,
  85. customRoute: name,
  86. });
  87. processedEvent.transaction = transactionValue;
  88. }
  89. return processedEvent;
  90. },
  91. };
  92. }) ;
  93. const requestDataIntegration = defineIntegration(_requestDataIntegration);
  94. /**
  95. * Add data about a request to an event. Primarily for use in Node-based SDKs, but included in `@sentry/integrations`
  96. * so it can be used in cross-platform SDKs like `@sentry/nextjs`.
  97. * @deprecated Use `requestDataIntegration()` instead.
  98. */
  99. // eslint-disable-next-line deprecation/deprecation
  100. const RequestData = convertIntegrationFnToClass(INTEGRATION_NAME, requestDataIntegration)
  101. ;
  102. /** Convert this integration's options to match what `addRequestDataToEvent` expects */
  103. /** TODO: Can possibly be deleted once https://github.com/getsentry/sentry-javascript/issues/5718 is fixed */
  104. function convertReqDataIntegrationOptsToAddReqDataOpts(
  105. integrationOptions,
  106. ) {
  107. const {
  108. transactionNamingScheme,
  109. include: { ip, user, ...requestOptions },
  110. } = integrationOptions;
  111. const requestIncludeKeys = [];
  112. for (const [key, value] of Object.entries(requestOptions)) {
  113. if (value) {
  114. requestIncludeKeys.push(key);
  115. }
  116. }
  117. let addReqDataUserOpt;
  118. if (user === undefined) {
  119. addReqDataUserOpt = true;
  120. } else if (typeof user === 'boolean') {
  121. addReqDataUserOpt = user;
  122. } else {
  123. const userIncludeKeys = [];
  124. for (const [key, value] of Object.entries(user)) {
  125. if (value) {
  126. userIncludeKeys.push(key);
  127. }
  128. }
  129. addReqDataUserOpt = userIncludeKeys;
  130. }
  131. return {
  132. include: {
  133. ip,
  134. user: addReqDataUserOpt,
  135. request: requestIncludeKeys.length !== 0 ? requestIncludeKeys : undefined,
  136. transaction: transactionNamingScheme,
  137. },
  138. };
  139. }
  140. function getSDKName(client) {
  141. try {
  142. // For a long chain like this, it's fewer bytes to combine a try-catch with assuming everything is there than to
  143. // write out a long chain of `a && a.b && a.b.c && ...`
  144. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  145. return client.getOptions()._metadata.sdk.name;
  146. } catch (err) {
  147. // In theory we should never get here
  148. return undefined;
  149. }
  150. }
  151. export { RequestData, requestDataIntegration };
  152. //# sourceMappingURL=requestdata.js.map