6
6
7
7
use Monolog \Handler \AbstractProcessingHandler ;
8
8
use Monolog \Logger ;
9
+ use Sentry \Breadcrumb ;
9
10
use Sentry \Severity ;
10
11
use Sentry \State \HubInterface ;
11
12
use Sentry \State \Scope ;
@@ -23,6 +24,11 @@ final class Handler extends AbstractProcessingHandler
23
24
*/
24
25
private $ hub ;
25
26
27
+ /**
28
+ * @var array
29
+ */
30
+ protected $ breadcrumbsBuffer = [];
31
+
26
32
/**
27
33
* Constructor.
28
34
*
@@ -37,6 +43,50 @@ public function __construct(HubInterface $hub, $level = Logger::DEBUG, bool $bub
37
43
$ this ->hub = $ hub ;
38
44
39
45
parent ::__construct ($ level , $ bubble );
46
+
47
+ $ this ->hub = $ hub ;
48
+ }
49
+
50
+ /**
51
+ * {@inheritdoc}
52
+ */
53
+ public function handleBatch (array $ records ): void
54
+ {
55
+ if (!$ records ) {
56
+ return ;
57
+ }
58
+
59
+ // filter records
60
+ $ records = array_filter (
61
+ $ records ,
62
+ function ($ record ) {
63
+ return $ record ['level ' ] >= $ this ->level ;
64
+ }
65
+ );
66
+
67
+ // the record with the highest severity is the "main" one
68
+ $ main = array_reduce (
69
+ $ records ,
70
+ static function ($ highest , $ record ) {
71
+ if ($ record ['level ' ] > $ highest ['level ' ]) {
72
+ return $ record ;
73
+ }
74
+
75
+ return $ highest ;
76
+ }
77
+ );
78
+
79
+ // the other ones are added as a context items
80
+ foreach ($ records as $ record ) {
81
+ $ record = $ this ->processRecord ($ record );
82
+ $ record ['formatted ' ] = $ this ->getFormatter ()->format ($ record );
83
+
84
+ $ this ->breadcrumbsBuffer [] = $ record ;
85
+ }
86
+
87
+ $ this ->handle ($ main );
88
+
89
+ $ this ->breadcrumbsBuffer = [];
40
90
}
41
91
42
92
/**
@@ -56,6 +106,7 @@ protected function write(array $record): void
56
106
$ this ->hub ->withScope (function (Scope $ scope ) use ($ record , $ payload ): void {
57
107
$ scope ->setExtra ('monolog.channel ' , $ record ['channel ' ]);
58
108
$ scope ->setExtra ('monolog.level ' , $ record ['level_name ' ]);
109
+ $ scope ->setExtra ('monolog.formatted ' , $ record ['formatted ' ]);
59
110
60
111
if (isset ($ record ['context ' ]['extra ' ]) && \is_array ($ record ['context ' ]['extra ' ])) {
61
112
foreach ($ record ['context ' ]['extra ' ] as $ key => $ value ) {
@@ -65,10 +116,19 @@ protected function write(array $record): void
65
116
66
117
if (isset ($ record ['context ' ]['tags ' ]) && \is_array ($ record ['context ' ]['tags ' ])) {
67
118
foreach ($ record ['context ' ]['tags ' ] as $ key => $ value ) {
68
- $ scope ->setTag ($ key , $ value );
119
+ $ scope ->setTag (( string ) $ key , $ value ?? ' N/A ' );
69
120
}
70
121
}
71
122
123
+ foreach ($ this ->breadcrumbsBuffer as $ breadcrumbRecord ) {
124
+ $ scope ->addBreadcrumb (new Breadcrumb (
125
+ $ this ->getBreadcrumbLevelFromLevel ($ breadcrumbRecord ['level ' ]),
126
+ $ this ->getBreadcrumbTypeFromLevel ($ breadcrumbRecord ['level ' ]),
127
+ $ breadcrumbRecord ['channel ' ] ?? 'N/A ' ,
128
+ $ breadcrumbRecord ['formatted ' ]
129
+ ));
130
+ }
131
+
72
132
$ this ->hub ->captureEvent ($ payload );
73
133
});
74
134
}
@@ -100,4 +160,48 @@ private function getSeverityFromLevel(int $level): Severity
100
160
return Severity::info ();
101
161
}
102
162
}
163
+
164
+ /**
165
+ * Translates the Monolog level into the Sentry breadcrumb level.
166
+ *
167
+ * @param int $level The Monolog log level
168
+ *
169
+ * @return string
170
+ */
171
+ private function getBreadcrumbLevelFromLevel (int $ level ): string
172
+ {
173
+ switch ($ level ) {
174
+ case Logger::DEBUG :
175
+ return Breadcrumb::LEVEL_DEBUG ;
176
+ case Logger::INFO :
177
+ case Logger::NOTICE :
178
+ return Breadcrumb::LEVEL_INFO ;
179
+ case Logger::WARNING :
180
+ return Breadcrumb::LEVEL_WARNING ;
181
+ case Logger::ERROR :
182
+ return Breadcrumb::LEVEL_ERROR ;
183
+ case Logger::CRITICAL :
184
+ case Logger::ALERT :
185
+ case Logger::EMERGENCY :
186
+ return Breadcrumb::LEVEL_CRITICAL ;
187
+ default :
188
+ return Breadcrumb::LEVEL_CRITICAL ;
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Translates the Monolog level into the Sentry breadcrumb type.
194
+ *
195
+ * @param int $level The Monolog log level
196
+ *
197
+ * @return string
198
+ */
199
+ private function getBreadcrumbTypeFromLevel (int $ level ): string
200
+ {
201
+ if ($ level >= Logger::ERROR ) {
202
+ return Breadcrumb::TYPE_ERROR ;
203
+ }
204
+
205
+ return Breadcrumb::TYPE_DEFAULT ;
206
+ }
103
207
}
0 commit comments