server-runtime-client.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import { resolvedSyncPromise, eventFromUnknownInput, eventFromMessage, logger, uuid4 } from '@sentry/utils';
  2. import { BaseClient } from './baseclient.js';
  3. import { createCheckInEnvelope } from './checkin.js';
  4. import { DEBUG_BUILD } from './debug-build.js';
  5. import { getClient } from './exports.js';
  6. import { MetricsAggregator } from './metrics/aggregator.js';
  7. import { SessionFlusher } from './sessionflusher.js';
  8. import { addTracingExtensions } from './tracing/hubextensions.js';
  9. import { spanToTraceContext } from './utils/spanUtils.js';
  10. import { getRootSpan } from './utils/getRootSpan.js';
  11. import './tracing/spanstatus.js';
  12. import { getDynamicSamplingContextFromSpan, getDynamicSamplingContextFromClient } from './tracing/dynamicSamplingContext.js';
  13. /**
  14. * The Sentry Server Runtime Client SDK.
  15. */
  16. class ServerRuntimeClient
  17. extends BaseClient {
  18. /**
  19. * Creates a new Edge SDK instance.
  20. * @param options Configuration options for this SDK.
  21. */
  22. constructor(options) {
  23. // Server clients always support tracing
  24. addTracingExtensions();
  25. super(options);
  26. if (options._experiments && options._experiments['metricsAggregator']) {
  27. this.metricsAggregator = new MetricsAggregator(this);
  28. }
  29. }
  30. /**
  31. * @inheritDoc
  32. */
  33. eventFromException(exception, hint) {
  34. return resolvedSyncPromise(eventFromUnknownInput(getClient(), this._options.stackParser, exception, hint));
  35. }
  36. /**
  37. * @inheritDoc
  38. */
  39. eventFromMessage(
  40. message,
  41. // eslint-disable-next-line deprecation/deprecation
  42. level = 'info',
  43. hint,
  44. ) {
  45. return resolvedSyncPromise(
  46. eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace),
  47. );
  48. }
  49. /**
  50. * @inheritDoc
  51. */
  52. // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
  53. captureException(exception, hint, scope) {
  54. // Check if the flag `autoSessionTracking` is enabled, and if `_sessionFlusher` exists because it is initialised only
  55. // when the `requestHandler` middleware is used, and hence the expectation is to have SessionAggregates payload
  56. // sent to the Server only when the `requestHandler` middleware is used
  57. if (this._options.autoSessionTracking && this._sessionFlusher && scope) {
  58. const requestSession = scope.getRequestSession();
  59. // Necessary checks to ensure this is code block is executed only within a request
  60. // Should override the status only if `requestSession.status` is `Ok`, which is its initial stage
  61. if (requestSession && requestSession.status === 'ok') {
  62. requestSession.status = 'errored';
  63. }
  64. }
  65. return super.captureException(exception, hint, scope);
  66. }
  67. /**
  68. * @inheritDoc
  69. */
  70. captureEvent(event, hint, scope) {
  71. // Check if the flag `autoSessionTracking` is enabled, and if `_sessionFlusher` exists because it is initialised only
  72. // when the `requestHandler` middleware is used, and hence the expectation is to have SessionAggregates payload
  73. // sent to the Server only when the `requestHandler` middleware is used
  74. if (this._options.autoSessionTracking && this._sessionFlusher && scope) {
  75. const eventType = event.type || 'exception';
  76. const isException =
  77. eventType === 'exception' && event.exception && event.exception.values && event.exception.values.length > 0;
  78. // If the event is of type Exception, then a request session should be captured
  79. if (isException) {
  80. const requestSession = scope.getRequestSession();
  81. // Ensure that this is happening within the bounds of a request, and make sure not to override
  82. // Session Status if Errored / Crashed
  83. if (requestSession && requestSession.status === 'ok') {
  84. requestSession.status = 'errored';
  85. }
  86. }
  87. }
  88. return super.captureEvent(event, hint, scope);
  89. }
  90. /**
  91. *
  92. * @inheritdoc
  93. */
  94. close(timeout) {
  95. if (this._sessionFlusher) {
  96. this._sessionFlusher.close();
  97. }
  98. return super.close(timeout);
  99. }
  100. /** Method that initialises an instance of SessionFlusher on Client */
  101. initSessionFlusher() {
  102. const { release, environment } = this._options;
  103. if (!release) {
  104. DEBUG_BUILD && logger.warn('Cannot initialise an instance of SessionFlusher if no release is provided!');
  105. } else {
  106. this._sessionFlusher = new SessionFlusher(this, {
  107. release,
  108. environment,
  109. });
  110. }
  111. }
  112. /**
  113. * Create a cron monitor check in and send it to Sentry.
  114. *
  115. * @param checkIn An object that describes a check in.
  116. * @param upsertMonitorConfig An optional object that describes a monitor config. Use this if you want
  117. * to create a monitor automatically when sending a check in.
  118. */
  119. captureCheckIn(checkIn, monitorConfig, scope) {
  120. const id = 'checkInId' in checkIn && checkIn.checkInId ? checkIn.checkInId : uuid4();
  121. if (!this._isEnabled()) {
  122. DEBUG_BUILD && logger.warn('SDK not enabled, will not capture checkin.');
  123. return id;
  124. }
  125. const options = this.getOptions();
  126. const { release, environment, tunnel } = options;
  127. const serializedCheckIn = {
  128. check_in_id: id,
  129. monitor_slug: checkIn.monitorSlug,
  130. status: checkIn.status,
  131. release,
  132. environment,
  133. };
  134. if ('duration' in checkIn) {
  135. serializedCheckIn.duration = checkIn.duration;
  136. }
  137. if (monitorConfig) {
  138. serializedCheckIn.monitor_config = {
  139. schedule: monitorConfig.schedule,
  140. checkin_margin: monitorConfig.checkinMargin,
  141. max_runtime: monitorConfig.maxRuntime,
  142. timezone: monitorConfig.timezone,
  143. };
  144. }
  145. const [dynamicSamplingContext, traceContext] = this._getTraceInfoFromScope(scope);
  146. if (traceContext) {
  147. serializedCheckIn.contexts = {
  148. trace: traceContext,
  149. };
  150. }
  151. const envelope = createCheckInEnvelope(
  152. serializedCheckIn,
  153. dynamicSamplingContext,
  154. this.getSdkMetadata(),
  155. tunnel,
  156. this.getDsn(),
  157. );
  158. DEBUG_BUILD && logger.info('Sending checkin:', checkIn.monitorSlug, checkIn.status);
  159. // _sendEnvelope should not throw
  160. // eslint-disable-next-line @typescript-eslint/no-floating-promises
  161. this._sendEnvelope(envelope);
  162. return id;
  163. }
  164. /**
  165. * Method responsible for capturing/ending a request session by calling `incrementSessionStatusCount` to increment
  166. * appropriate session aggregates bucket
  167. */
  168. _captureRequestSession() {
  169. if (!this._sessionFlusher) {
  170. DEBUG_BUILD && logger.warn('Discarded request mode session because autoSessionTracking option was disabled');
  171. } else {
  172. this._sessionFlusher.incrementSessionStatusCount();
  173. }
  174. }
  175. /**
  176. * @inheritDoc
  177. */
  178. _prepareEvent(
  179. event,
  180. hint,
  181. scope,
  182. isolationScope,
  183. ) {
  184. if (this._options.platform) {
  185. event.platform = event.platform || this._options.platform;
  186. }
  187. if (this._options.runtime) {
  188. event.contexts = {
  189. ...event.contexts,
  190. runtime: (event.contexts || {}).runtime || this._options.runtime,
  191. };
  192. }
  193. if (this._options.serverName) {
  194. event.server_name = event.server_name || this._options.serverName;
  195. }
  196. return super._prepareEvent(event, hint, scope, isolationScope);
  197. }
  198. /** Extract trace information from scope */
  199. _getTraceInfoFromScope(
  200. scope,
  201. ) {
  202. if (!scope) {
  203. return [undefined, undefined];
  204. }
  205. // eslint-disable-next-line deprecation/deprecation
  206. const span = scope.getSpan();
  207. if (span) {
  208. const samplingContext = getRootSpan(span) ? getDynamicSamplingContextFromSpan(span) : undefined;
  209. return [samplingContext, spanToTraceContext(span)];
  210. }
  211. const { traceId, spanId, parentSpanId, dsc } = scope.getPropagationContext();
  212. const traceContext = {
  213. trace_id: traceId,
  214. span_id: spanId,
  215. parent_span_id: parentSpanId,
  216. };
  217. if (dsc) {
  218. return [dsc, traceContext];
  219. }
  220. return [getDynamicSamplingContextFromClient(traceId, this, scope), traceContext];
  221. }
  222. }
  223. export { ServerRuntimeClient };
  224. //# sourceMappingURL=server-runtime-client.js.map