Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
36 / 36 |
|
100.00% |
13 / 13 |
CRAP | |
100.00% |
1 / 1 |
Sentry | |
100.00% |
36 / 36 |
|
100.00% |
13 / 13 |
28 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
6 | |||
getPostProcessors | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setTag | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setExtra | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setExtras | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
setTags | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
unsetTag | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
unsetExtra | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
unsetTags | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
unsetExtras | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getTag | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getExtra | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
logExceptionsToSentry | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
7 |
1 | <?php |
2 | |
3 | namespace Miniframe\Middleware; |
4 | |
5 | use Miniframe\Core\AbstractMiddleware; |
6 | use Miniframe\Core\Config; |
7 | use Miniframe\Core\Request; |
8 | use Miniframe\Core\Response; |
9 | use Miniframe\Response\InternalServerErrorResponse; |
10 | |
11 | use function Sentry\init as InitSentry; |
12 | use function Sentry\captureException; |
13 | |
14 | class Sentry extends AbstractMiddleware |
15 | { |
16 | /** |
17 | * List of fully qualified classnames that shouldn't be logged when sent to Sentry |
18 | * |
19 | * @var string[] |
20 | */ |
21 | private $exclusions = array(); |
22 | |
23 | /** |
24 | * List of tags to be sent to Sentry |
25 | * |
26 | * @var array |
27 | */ |
28 | private $tags = array(); |
29 | |
30 | /** |
31 | * List of extra data to be sent to Sentry |
32 | * |
33 | * @var array |
34 | */ |
35 | private $extra = array(); |
36 | |
37 | /** |
38 | * Initializes the Sentry middleware |
39 | * |
40 | * @param Request $request The request object. |
41 | * @param Config $config The config object. |
42 | */ |
43 | public function __construct(Request $request, Config $config) |
44 | { |
45 | parent::__construct($request, $config); |
46 | |
47 | InitSentry([ |
48 | 'dsn' => $config->get('sentry', 'dsn'), |
49 | 'before_send' => function (\Sentry\Event $event, ?\Sentry\EventHint $hint): ?\Sentry\Event { |
50 | // Ignore excluded exceptions |
51 | if ( |
52 | $hint !== null && ( |
53 | in_array(get_class($hint->exception), $this->exclusions) |
54 | || ( |
55 | $hint->exception->getPrevious() |
56 | && in_array(get_class($hint->exception->getPrevious()), $this->exclusions) |
57 | ) |
58 | ) |
59 | ) { |
60 | return null; |
61 | } |
62 | |
63 | $event->setTags($this->tags); |
64 | $event->setExtra($this->extra); |
65 | |
66 | return $event; |
67 | }, |
68 | ]); |
69 | |
70 | $this->exclusions = $config->has('sentry', 'exclude') ? $config->get('sentry', 'exclude') : []; |
71 | } |
72 | |
73 | /** |
74 | * Returns a list of the post processors |
75 | * |
76 | * @return callable[] |
77 | */ |
78 | public function getPostProcessors(): array |
79 | { |
80 | return [[$this, 'logExceptionsToSentry']]; |
81 | } |
82 | |
83 | /** |
84 | * Defines a tag to be sent to Sentry. It's possible to filter by tag in Sentry. |
85 | * |
86 | * @param string $label Tag label. |
87 | * @param string|null $value Tag value. |
88 | * |
89 | * @return void |
90 | */ |
91 | public function setTag(string $label, ?string $value): void |
92 | { |
93 | $this->tags[$label] = $value; |
94 | } |
95 | |
96 | /** |
97 | * Defines an extra value to be sent to Sentry. It's NOT possible to filter by this value in Sentry. |
98 | * |
99 | * @param string $label Extra label. |
100 | * @param mixed $value Extra data. |
101 | * |
102 | * @return void |
103 | */ |
104 | public function setExtra(string $label, $value): void |
105 | { |
106 | $this->extra[$label] = $value; |
107 | } |
108 | |
109 | /** |
110 | * Defines a set of extra values to be sent to Sentry. It's NOT possible to filter by these values in Sentry. |
111 | * |
112 | * @param array $associativeArray Extra data (key => value pairs). |
113 | * |
114 | * @return void |
115 | */ |
116 | public function setExtras(array $associativeArray): void |
117 | { |
118 | foreach ($associativeArray as $label => $value) { |
119 | $this->setExtra($label, $value); |
120 | } |
121 | } |
122 | |
123 | /** |
124 | * Defines a set of tags to be sent to Sentry. It's possible to filter by tag in Sentry. |
125 | * |
126 | * @param array $associativeArray Tags (key => value pairs). |
127 | * |
128 | * @return void |
129 | */ |
130 | public function setTags(array $associativeArray): void |
131 | { |
132 | foreach ($associativeArray as $label => $value) { |
133 | $this->setTag($label, $value); |
134 | } |
135 | } |
136 | |
137 | /** |
138 | * Removes a tag to be sent to Sentry |
139 | * |
140 | * @param string $label Tag label. |
141 | * |
142 | * @return void |
143 | */ |
144 | public function unsetTag(string $label): void |
145 | { |
146 | unset($this->tags[$label]); |
147 | } |
148 | |
149 | /** |
150 | * Removes a set of extra data to be sent to Sentry |
151 | * |
152 | * @param string $label Extra label. |
153 | * |
154 | * @return void |
155 | */ |
156 | public function unsetExtra(string $label): void |
157 | { |
158 | unset($this->extra[$label]); |
159 | } |
160 | |
161 | /** |
162 | * Removes a set of tags to be sent to Sentry |
163 | * |
164 | * @param string ...$labels Tag labels. |
165 | * |
166 | * @return void |
167 | */ |
168 | public function unsetTags(string ...$labels): void |
169 | { |
170 | foreach ($labels as $label) { |
171 | $this->unsetTag($label); |
172 | } |
173 | } |
174 | |
175 | /** |
176 | * Removes a set of extra data to be sent to Sentry |
177 | * |
178 | * @param string ...$labels Extra labels. |
179 | * |
180 | * @return void |
181 | */ |
182 | public function unsetExtras(string ...$labels): void |
183 | { |
184 | foreach ($labels as $label) { |
185 | $this->unsetExtra($label); |
186 | } |
187 | } |
188 | |
189 | /** |
190 | * Gets a tag to be sent to Sentry |
191 | * |
192 | * @param string $label Tag label. |
193 | * |
194 | * @return string|null |
195 | */ |
196 | public function getTag(string $label): ?string |
197 | { |
198 | return $this->tags[$label] ?? null; |
199 | } |
200 | |
201 | /** |
202 | * Gets an extra to be sent to Sentry |
203 | * |
204 | * @param string $label Extra label. |
205 | * |
206 | * @return mixed|null |
207 | */ |
208 | public function getExtra(string $label) |
209 | { |
210 | return $this->extra[$label] ?? null; |
211 | } |
212 | |
213 | /** |
214 | * Sentry Post processor |
215 | * |
216 | * @param Response $response The current response. |
217 | * @param boolean $thrown True when the response is thrown, otherwise false. |
218 | * |
219 | * @return Response |
220 | */ |
221 | public function logExceptionsToSentry(Response $response, bool $thrown): Response |
222 | { |
223 | // Log all thrown responses, and responses with an exit code different from 0 |
224 | if (!$thrown && $response->getExitCode() === 0) { |
225 | return $response; |
226 | } |
227 | |
228 | if (is_a($response, InternalServerErrorResponse::class) && $response->getPrevious()) { |
229 | if (!in_array(get_class($response->getPrevious()), $this->exclusions)) { |
230 | captureException($response->getPrevious()); |
231 | } |
232 | } else { |
233 | if (!in_array(get_class($response), $this->exclusions)) { |
234 | captureException($response); |
235 | } |
236 | } |
237 | |
238 | return $response; |
239 | } |
240 | } |