vendor/sentry/sentry/src/State/Scope.php line 85

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Sentry\State;
  4. use Sentry\Breadcrumb;
  5. use Sentry\Event;
  6. use Sentry\EventHint;
  7. use Sentry\Options;
  8. use Sentry\Severity;
  9. use Sentry\Tracing\DynamicSamplingContext;
  10. use Sentry\Tracing\PropagationContext;
  11. use Sentry\Tracing\Span;
  12. use Sentry\Tracing\Transaction;
  13. use Sentry\UserDataBag;
  14. /**
  15. * The scope holds data that should implicitly be sent with Sentry events. It
  16. * can hold context data, extra parameters, level overrides, fingerprints etc.
  17. */
  18. final class Scope
  19. {
  20. /**
  21. * @var PropagationContext
  22. */
  23. private $propagationContext;
  24. /**
  25. * @var Breadcrumb[] The list of breadcrumbs recorded in this scope
  26. */
  27. private $breadcrumbs = [];
  28. /**
  29. * @var UserDataBag|null The user data associated to this scope
  30. */
  31. private $user;
  32. /**
  33. * @var array<string, array<string, mixed>> The list of contexts associated to this scope
  34. */
  35. private $contexts = [];
  36. /**
  37. * @var array<string, string> The list of tags associated to this scope
  38. */
  39. private $tags = [];
  40. /**
  41. * @var array<string, mixed> A set of extra data associated to this scope
  42. */
  43. private $extra = [];
  44. /**
  45. * @var string[] List of fingerprints used to group events together in
  46. * Sentry
  47. */
  48. private $fingerprint = [];
  49. /**
  50. * @var Severity|null The severity to associate to the events captured in
  51. * this scope
  52. */
  53. private $level;
  54. /**
  55. * @var callable[] List of event processors
  56. *
  57. * @psalm-var array<callable(Event, EventHint): ?Event>
  58. */
  59. private $eventProcessors = [];
  60. /**
  61. * @var Span|null Set a Span on the Scope
  62. */
  63. private $span;
  64. /**
  65. * @var callable[] List of event processors
  66. *
  67. * @psalm-var array<callable(Event, EventHint): ?Event>
  68. */
  69. private static $globalEventProcessors = [];
  70. public function __construct(PropagationContext $propagationContext = null)
  71. {
  72. $this->propagationContext = $propagationContext ?? PropagationContext::fromDefaults();
  73. }
  74. /**
  75. * Sets a new tag in the tags context.
  76. *
  77. * @param string $key The key that uniquely identifies the tag
  78. * @param string $value The value
  79. *
  80. * @return $this
  81. */
  82. public function setTag(string $key, string $value): self
  83. {
  84. $this->tags[$key] = $value;
  85. return $this;
  86. }
  87. /**
  88. * Merges the given tags into the current tags context.
  89. *
  90. * @param array<string, string> $tags The tags to merge into the current context
  91. *
  92. * @return $this
  93. */
  94. public function setTags(array $tags): self
  95. {
  96. $this->tags = array_merge($this->tags, $tags);
  97. return $this;
  98. }
  99. /**
  100. * Removes a given tag from the tags context.
  101. *
  102. * @param string $key The key that uniquely identifies the tag
  103. *
  104. * @return $this
  105. */
  106. public function removeTag(string $key): self
  107. {
  108. unset($this->tags[$key]);
  109. return $this;
  110. }
  111. /**
  112. * Sets data to the context by a given name.
  113. *
  114. * @param string $name The name that uniquely identifies the context
  115. * @param array<string, mixed> $value The value
  116. *
  117. * @return $this
  118. */
  119. public function setContext(string $name, array $value): self
  120. {
  121. if (!empty($value)) {
  122. $this->contexts[$name] = $value;
  123. }
  124. return $this;
  125. }
  126. /**
  127. * Removes the context from the scope.
  128. *
  129. * @param string $name The name that uniquely identifies the context
  130. *
  131. * @return $this
  132. */
  133. public function removeContext(string $name): self
  134. {
  135. unset($this->contexts[$name]);
  136. return $this;
  137. }
  138. /**
  139. * Sets a new information in the extra context.
  140. *
  141. * @param string $key The key that uniquely identifies the information
  142. * @param mixed $value The value
  143. *
  144. * @return $this
  145. */
  146. public function setExtra(string $key, $value): self
  147. {
  148. $this->extra[$key] = $value;
  149. return $this;
  150. }
  151. /**
  152. * Merges the given data into the current extras context.
  153. *
  154. * @param array<string, mixed> $extras Data to merge into the current context
  155. *
  156. * @return $this
  157. */
  158. public function setExtras(array $extras): self
  159. {
  160. $this->extra = array_merge($this->extra, $extras);
  161. return $this;
  162. }
  163. /**
  164. * Get the user context.
  165. */
  166. public function getUser(): ?UserDataBag
  167. {
  168. return $this->user;
  169. }
  170. /**
  171. * Merges the given data in the user context.
  172. *
  173. * @param array<string, mixed>|UserDataBag $user The user data
  174. *
  175. * @return $this
  176. */
  177. public function setUser($user): self
  178. {
  179. if (!\is_array($user) && !$user instanceof UserDataBag) {
  180. throw new \TypeError(sprintf('The $user argument must be either an array or an instance of the "%s" class. Got: "%s".', UserDataBag::class, get_debug_type($user)));
  181. }
  182. if (\is_array($user)) {
  183. $user = UserDataBag::createFromArray($user);
  184. }
  185. if (null === $this->user) {
  186. $this->user = $user;
  187. } else {
  188. $this->user = $this->user->merge($user);
  189. }
  190. return $this;
  191. }
  192. /**
  193. * Removes all data of the user context.
  194. *
  195. * @return $this
  196. */
  197. public function removeUser(): self
  198. {
  199. $this->user = null;
  200. return $this;
  201. }
  202. /**
  203. * Sets the list of strings used to dictate the deduplication of this event.
  204. *
  205. * @param string[] $fingerprint The fingerprint values
  206. *
  207. * @return $this
  208. */
  209. public function setFingerprint(array $fingerprint): self
  210. {
  211. $this->fingerprint = $fingerprint;
  212. return $this;
  213. }
  214. /**
  215. * Sets the severity to apply to all events captured in this scope.
  216. *
  217. * @param Severity|null $level The severity
  218. *
  219. * @return $this
  220. */
  221. public function setLevel(?Severity $level): self
  222. {
  223. $this->level = $level;
  224. return $this;
  225. }
  226. /**
  227. * Add the given breadcrumb to the scope.
  228. *
  229. * @param Breadcrumb $breadcrumb The breadcrumb to add
  230. * @param int $maxBreadcrumbs The maximum number of breadcrumbs to record
  231. *
  232. * @return $this
  233. */
  234. public function addBreadcrumb(Breadcrumb $breadcrumb, int $maxBreadcrumbs = 100): self
  235. {
  236. $this->breadcrumbs[] = $breadcrumb;
  237. $this->breadcrumbs = \array_slice($this->breadcrumbs, -$maxBreadcrumbs);
  238. return $this;
  239. }
  240. /**
  241. * Clears all the breadcrumbs.
  242. *
  243. * @return $this
  244. */
  245. public function clearBreadcrumbs(): self
  246. {
  247. $this->breadcrumbs = [];
  248. return $this;
  249. }
  250. /**
  251. * Adds a new event processor that will be called after {@see Scope::applyToEvent}
  252. * finished its work.
  253. *
  254. * @param callable $eventProcessor The event processor
  255. *
  256. * @return $this
  257. */
  258. public function addEventProcessor(callable $eventProcessor): self
  259. {
  260. $this->eventProcessors[] = $eventProcessor;
  261. return $this;
  262. }
  263. /**
  264. * Adds a new event processor that will be called after {@see Scope::applyToEvent}
  265. * finished its work.
  266. *
  267. * @param callable $eventProcessor The event processor
  268. */
  269. public static function addGlobalEventProcessor(callable $eventProcessor): void
  270. {
  271. self::$globalEventProcessors[] = $eventProcessor;
  272. }
  273. /**
  274. * Clears the scope and resets any data it contains.
  275. *
  276. * @return $this
  277. */
  278. public function clear(): self
  279. {
  280. $this->user = null;
  281. $this->level = null;
  282. $this->span = null;
  283. $this->fingerprint = [];
  284. $this->breadcrumbs = [];
  285. $this->tags = [];
  286. $this->extra = [];
  287. $this->contexts = [];
  288. return $this;
  289. }
  290. /**
  291. * Applies the current context and fingerprint to the event. If the event has
  292. * already some breadcrumbs on it, the ones from this scope won't get merged.
  293. *
  294. * @param Event $event The event object that will be enriched with scope data
  295. */
  296. public function applyToEvent(Event $event, ?EventHint $hint = null, ?Options $options = null): ?Event
  297. {
  298. $event->setFingerprint(array_merge($event->getFingerprint(), $this->fingerprint));
  299. if (empty($event->getBreadcrumbs())) {
  300. $event->setBreadcrumb($this->breadcrumbs);
  301. }
  302. if (null !== $this->level) {
  303. $event->setLevel($this->level);
  304. }
  305. if (!empty($this->tags)) {
  306. $event->setTags(array_merge($this->tags, $event->getTags()));
  307. }
  308. if (!empty($this->extra)) {
  309. $event->setExtra(array_merge($this->extra, $event->getExtra()));
  310. }
  311. if (null !== $this->user) {
  312. $user = $event->getUser();
  313. if (null === $user) {
  314. $user = $this->user;
  315. } else {
  316. $user = $this->user->merge($user);
  317. }
  318. $event->setUser($user);
  319. }
  320. /**
  321. * Apply the trace context to errors if there is a Span on the Scope.
  322. * Else fallback to the propagation context.
  323. */
  324. if (null !== $this->span) {
  325. $event->setContext('trace', $this->span->getTraceContext());
  326. // Apply the dynamic sampling context to errors if there is a Transaction on the Scope
  327. $transaction = $this->span->getTransaction();
  328. if (null !== $transaction) {
  329. $event->setSdkMetadata('dynamic_sampling_context', $transaction->getDynamicSamplingContext());
  330. }
  331. } else {
  332. $event->setContext('trace', $this->propagationContext->getTraceContext());
  333. $dynamicSamplingContext = $this->propagationContext->getDynamicSamplingContext();
  334. if (null === $dynamicSamplingContext && null !== $options) {
  335. $dynamicSamplingContext = DynamicSamplingContext::fromOptions($options, $this);
  336. }
  337. $event->setSdkMetadata('dynamic_sampling_context', $dynamicSamplingContext);
  338. }
  339. foreach (array_merge($this->contexts, $event->getContexts()) as $name => $data) {
  340. $event->setContext($name, $data);
  341. }
  342. // We create a empty `EventHint` instance to allow processors to always receive a `EventHint` instance even if there wasn't one
  343. if (null === $hint) {
  344. $hint = new EventHint();
  345. }
  346. foreach (array_merge(self::$globalEventProcessors, $this->eventProcessors) as $processor) {
  347. $event = $processor($event, $hint);
  348. if (null === $event) {
  349. return null;
  350. }
  351. if (!$event instanceof Event) {
  352. throw new \InvalidArgumentException(sprintf('The event processor must return null or an instance of the %s class', Event::class));
  353. }
  354. }
  355. return $event;
  356. }
  357. /**
  358. * Returns the span that is on the scope.
  359. */
  360. public function getSpan(): ?Span
  361. {
  362. return $this->span;
  363. }
  364. /**
  365. * Sets the span on the scope.
  366. *
  367. * @param Span|null $span The span
  368. *
  369. * @return $this
  370. */
  371. public function setSpan(?Span $span): self
  372. {
  373. $this->span = $span;
  374. return $this;
  375. }
  376. /**
  377. * Returns the transaction attached to the scope (if there is one).
  378. */
  379. public function getTransaction(): ?Transaction
  380. {
  381. if (null !== $this->span) {
  382. return $this->span->getTransaction();
  383. }
  384. return null;
  385. }
  386. public function getPropagationContext(): PropagationContext
  387. {
  388. return $this->propagationContext;
  389. }
  390. public function setPropagationContext(PropagationContext $propagationContext): self
  391. {
  392. $this->propagationContext = $propagationContext;
  393. return $this;
  394. }
  395. public function __clone()
  396. {
  397. if (null !== $this->user) {
  398. $this->user = clone $this->user;
  399. }
  400. if (null !== $this->propagationContext) {
  401. $this->propagationContext = clone $this->propagationContext;
  402. }
  403. }
  404. }