Skip to content

Commit 203a7da

Browse files
committed
Monolog: Add ProcessingHandler
1 parent 9693e86 commit 203a7da

File tree

6 files changed

+503
-10
lines changed

6 files changed

+503
-10
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Lunr.Ticks.Monolog
22

3-
Lunr.Ticks.Monolog is a Monolog LogHandler that processes logs as analytics event.
3+
Lunr.Ticks.Monolog is a Monolog ProcessingHandler that processes logs as analytics event.
44

55
Installation
66
------------

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
"license": "MIT",
88
"require": {
99
"php": ">=8.1",
10-
"lunr/ticks": "dev-master"
10+
"lunr/ticks": "dev-master",
11+
"monolog/monolog": "~3.9",
12+
"psr/log": "~3.0"
1113
},
1214
"require-dev": {
1315
"phpunit/phpunit": ">=10.5 <11.0",
14-
"mockery/mockery": "~1.6",
1516
"lunr/halo": "dev-master",
1617
"ext-xdebug": "~3.1"
1718
},

decomposer.json

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,35 @@
99
},
1010
"development-only": true
1111
},
12-
"Mockery": {
13-
"url": "https://github.com/mockery/mockery.git",
14-
"version": "1.6.12",
15-
"psr0": {
16-
"path": "/library/"
17-
},
18-
"development-only": true
12+
"Lunr.Ticks": {
13+
"url": "https://github.com/lunr-php/lunr.ticks.git",
14+
"version": "0.12.0",
15+
"revision": "master",
16+
"psr4": {
17+
"prefix": "Lunr\\Ticks",
18+
"search-path": "/src/Lunr/Ticks/"
19+
}
20+
},
21+
"Psr-Log": {
22+
"url": "https://github.com/php-fig/log.git",
23+
"version": "3.0.0",
24+
"psr4": {
25+
"prefix": "Psr\\Log",
26+
"search-path": "/src/"
27+
}
28+
},
29+
"Monolog": {
30+
"url": "https://github.com/seldaek/monolog.git",
31+
"version": "3.9.0",
32+
"psr4": [
33+
{
34+
"prefix": "Monolog",
35+
"search-path": "/src/Monolog/"
36+
},
37+
{
38+
"prefix": "Monolog",
39+
"search-path": "/tests/Monolog/"
40+
}
41+
]
1942
}
2043
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
/**
4+
* This file contains a processing handler class for Monolog.
5+
*
6+
* SPDX-FileCopyrightText: Copyright 2025 Framna Netherland B.V., Zwolle, The Netherlands
7+
* SPDX-License-Identifier: MIT
8+
*/
9+
10+
namespace Lunr\Ticks\Monolog;
11+
12+
use Lunr\Ticks\EventLogging\EventLoggerInterface;
13+
use Lunr\Ticks\Precision;
14+
use Lunr\Ticks\TracingControllerInterface;
15+
use Lunr\Ticks\TracingInfoInterface;
16+
use Monolog\Handler\AbstractProcessingHandler;
17+
use Monolog\Level;
18+
use Monolog\LogRecord;
19+
use Psr\Log\LogLevel;
20+
use Throwable;
21+
22+
/**
23+
* Logs via Lunr.Ticks.
24+
*
25+
* @phpstan-type LevelValue value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*
26+
* @phpstan-type TracingInterface TracingControllerInterface&TracingInfoInterface
27+
*/
28+
class ProcessingHandler extends AbstractProcessingHandler
29+
{
30+
31+
/**
32+
* Instance of an EventLogger
33+
* @var EventLoggerInterface
34+
*/
35+
private readonly EventLoggerInterface $eventLogger;
36+
37+
/**
38+
* Shared instance of a tracing controller
39+
* @var TracingInterface
40+
*/
41+
private readonly TracingControllerInterface&TracingInfoInterface $tracingController;
42+
43+
/**
44+
* Constructor.
45+
*
46+
* @param EventLoggerInterface $eventLogger Instance of an event logger
47+
* @param TracingInterface $tracingController Instance of a tracing controller
48+
* @param LevelValue $level The minimum logging level at which this handler will be triggered
49+
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
50+
*/
51+
public function __construct(
52+
EventLoggerInterface $eventLogger,
53+
TracingControllerInterface&TracingInfoInterface $tracingController,
54+
int|string|Level $level = Level::Debug,
55+
bool $bubble = TRUE,
56+
)
57+
{
58+
parent::__construct($level, $bubble);
59+
60+
$this->eventLogger = $eventLogger;
61+
$this->tracingController = $tracingController;
62+
}
63+
64+
/**
65+
* Writes the (already formatted) record down to the log of the implementing handler
66+
*
67+
* @param LogRecord $record The log record to handle
68+
*
69+
* @return void
70+
*/
71+
protected function write(LogRecord $record): void
72+
{
73+
$fields = [
74+
'message' => is_string($record->formatted) ? $record->formatted : NULL,
75+
'level' => $record->level->value,
76+
'line' => is_string($record->extra['line'] ?? NULL) ? $record->extra['line'] : NULL,
77+
'traceID' => $this->tracingController->getTraceId(),
78+
'spanID' => $this->tracingController->getSpanId(),
79+
'parentSpanID' => $this->tracingController->getParentSpanId(),
80+
];
81+
82+
$tags = [
83+
'levelName' => $record->level->getName(),
84+
'channel' => $record->channel,
85+
'file' => is_string($record->extra['file'] ?? NULL) ? $record->extra['file'] : NULL,
86+
'class' => is_string($record->extra['class'] ?? NULL) ? $record->extra['class'] : NULL,
87+
'function' => is_string($record->extra['function'] ?? NULL) ? $record->extra['function'] : NULL,
88+
];
89+
90+
foreach ($record->extra as $key => $value)
91+
{
92+
if (isset($tags[$key]) || isset($fields[$key]))
93+
{
94+
continue;
95+
}
96+
97+
if (!is_scalar($value))
98+
{
99+
continue;
100+
}
101+
102+
$fields[(string) $key] = $value;
103+
}
104+
105+
if (isset($record->context['exception']) && $record->context['exception'] instanceof Throwable)
106+
{
107+
$tags['exception'] = get_class($record->context['exception']);
108+
$fields['stacktrace'] = $record->context['exception']->getTraceAsString();
109+
}
110+
111+
$event = $this->eventLogger->newEvent('php_log');
112+
113+
$event->setTimestamp($record->datetime->format('Uu'));
114+
$event->addTags(array_merge($this->tracingController->getSpanSpecificTags(), $tags));
115+
$event->addFields($fields);
116+
$event->record(Precision::MicroSeconds);
117+
}
118+
119+
}
120+
121+
?>

0 commit comments

Comments
 (0)