| 148 |
lars |
1 |
<?php declare(strict_types=1);
|
|
|
2 |
|
|
|
3 |
/*
|
|
|
4 |
* This file is part of the Monolog package.
|
|
|
5 |
*
|
|
|
6 |
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
|
|
7 |
*
|
|
|
8 |
* For the full copyright and license information, please view the LICENSE
|
|
|
9 |
* file that was distributed with this source code.
|
|
|
10 |
*/
|
|
|
11 |
|
|
|
12 |
namespace Monolog\Handler;
|
|
|
13 |
|
|
|
14 |
use Monolog\Logger;
|
|
|
15 |
use Monolog\Utils;
|
|
|
16 |
use Monolog\Formatter\NormalizerFormatter;
|
|
|
17 |
use Monolog\Formatter\FormatterInterface;
|
|
|
18 |
|
|
|
19 |
/**
|
|
|
20 |
* Class to record a log on a NewRelic application.
|
|
|
21 |
* Enabling New Relic High Security mode may prevent capture of useful information.
|
|
|
22 |
*
|
|
|
23 |
* This handler requires a NormalizerFormatter to function and expects an array in $record['formatted']
|
|
|
24 |
*
|
|
|
25 |
* @see https://docs.newrelic.com/docs/agents/php-agent
|
|
|
26 |
* @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security
|
|
|
27 |
*/
|
|
|
28 |
class NewRelicHandler extends AbstractProcessingHandler
|
|
|
29 |
{
|
|
|
30 |
/**
|
|
|
31 |
* Name of the New Relic application that will receive logs from this handler.
|
|
|
32 |
*
|
|
|
33 |
* @var ?string
|
|
|
34 |
*/
|
|
|
35 |
protected $appName;
|
|
|
36 |
|
|
|
37 |
/**
|
|
|
38 |
* Name of the current transaction
|
|
|
39 |
*
|
|
|
40 |
* @var ?string
|
|
|
41 |
*/
|
|
|
42 |
protected $transactionName;
|
|
|
43 |
|
|
|
44 |
/**
|
|
|
45 |
* Some context and extra data is passed into the handler as arrays of values. Do we send them as is
|
|
|
46 |
* (useful if we are using the API), or explode them for display on the NewRelic RPM website?
|
|
|
47 |
*
|
|
|
48 |
* @var bool
|
|
|
49 |
*/
|
|
|
50 |
protected $explodeArrays;
|
|
|
51 |
|
|
|
52 |
/**
|
|
|
53 |
* {@inheritDoc}
|
|
|
54 |
*
|
|
|
55 |
* @param string|null $appName
|
|
|
56 |
* @param bool $explodeArrays
|
|
|
57 |
* @param string|null $transactionName
|
|
|
58 |
*/
|
|
|
59 |
public function __construct(
|
|
|
60 |
$level = Logger::ERROR,
|
|
|
61 |
bool $bubble = true,
|
|
|
62 |
?string $appName = null,
|
|
|
63 |
bool $explodeArrays = false,
|
|
|
64 |
?string $transactionName = null
|
|
|
65 |
) {
|
|
|
66 |
parent::__construct($level, $bubble);
|
|
|
67 |
|
|
|
68 |
$this->appName = $appName;
|
|
|
69 |
$this->explodeArrays = $explodeArrays;
|
|
|
70 |
$this->transactionName = $transactionName;
|
|
|
71 |
}
|
|
|
72 |
|
|
|
73 |
/**
|
|
|
74 |
* {@inheritDoc}
|
|
|
75 |
*/
|
|
|
76 |
protected function write(array $record): void
|
|
|
77 |
{
|
|
|
78 |
if (!$this->isNewRelicEnabled()) {
|
|
|
79 |
throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler');
|
|
|
80 |
}
|
|
|
81 |
|
|
|
82 |
if ($appName = $this->getAppName($record['context'])) {
|
|
|
83 |
$this->setNewRelicAppName($appName);
|
|
|
84 |
}
|
|
|
85 |
|
|
|
86 |
if ($transactionName = $this->getTransactionName($record['context'])) {
|
|
|
87 |
$this->setNewRelicTransactionName($transactionName);
|
|
|
88 |
unset($record['formatted']['context']['transaction_name']);
|
|
|
89 |
}
|
|
|
90 |
|
|
|
91 |
if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) {
|
|
|
92 |
newrelic_notice_error($record['message'], $record['context']['exception']);
|
|
|
93 |
unset($record['formatted']['context']['exception']);
|
|
|
94 |
} else {
|
|
|
95 |
newrelic_notice_error($record['message']);
|
|
|
96 |
}
|
|
|
97 |
|
|
|
98 |
if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) {
|
|
|
99 |
foreach ($record['formatted']['context'] as $key => $parameter) {
|
|
|
100 |
if (is_array($parameter) && $this->explodeArrays) {
|
|
|
101 |
foreach ($parameter as $paramKey => $paramValue) {
|
|
|
102 |
$this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue);
|
|
|
103 |
}
|
|
|
104 |
} else {
|
|
|
105 |
$this->setNewRelicParameter('context_' . $key, $parameter);
|
|
|
106 |
}
|
|
|
107 |
}
|
|
|
108 |
}
|
|
|
109 |
|
|
|
110 |
if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) {
|
|
|
111 |
foreach ($record['formatted']['extra'] as $key => $parameter) {
|
|
|
112 |
if (is_array($parameter) && $this->explodeArrays) {
|
|
|
113 |
foreach ($parameter as $paramKey => $paramValue) {
|
|
|
114 |
$this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue);
|
|
|
115 |
}
|
|
|
116 |
} else {
|
|
|
117 |
$this->setNewRelicParameter('extra_' . $key, $parameter);
|
|
|
118 |
}
|
|
|
119 |
}
|
|
|
120 |
}
|
|
|
121 |
}
|
|
|
122 |
|
|
|
123 |
/**
|
|
|
124 |
* Checks whether the NewRelic extension is enabled in the system.
|
|
|
125 |
*
|
|
|
126 |
* @return bool
|
|
|
127 |
*/
|
|
|
128 |
protected function isNewRelicEnabled(): bool
|
|
|
129 |
{
|
|
|
130 |
return extension_loaded('newrelic');
|
|
|
131 |
}
|
|
|
132 |
|
|
|
133 |
/**
|
|
|
134 |
* Returns the appname where this log should be sent. Each log can override the default appname, set in this
|
|
|
135 |
* handler's constructor, by providing the appname in it's context.
|
|
|
136 |
*
|
|
|
137 |
* @param mixed[] $context
|
|
|
138 |
*/
|
|
|
139 |
protected function getAppName(array $context): ?string
|
|
|
140 |
{
|
|
|
141 |
if (isset($context['appname'])) {
|
|
|
142 |
return $context['appname'];
|
|
|
143 |
}
|
|
|
144 |
|
|
|
145 |
return $this->appName;
|
|
|
146 |
}
|
|
|
147 |
|
|
|
148 |
/**
|
|
|
149 |
* Returns the name of the current transaction. Each log can override the default transaction name, set in this
|
|
|
150 |
* handler's constructor, by providing the transaction_name in it's context
|
|
|
151 |
*
|
|
|
152 |
* @param mixed[] $context
|
|
|
153 |
*/
|
|
|
154 |
protected function getTransactionName(array $context): ?string
|
|
|
155 |
{
|
|
|
156 |
if (isset($context['transaction_name'])) {
|
|
|
157 |
return $context['transaction_name'];
|
|
|
158 |
}
|
|
|
159 |
|
|
|
160 |
return $this->transactionName;
|
|
|
161 |
}
|
|
|
162 |
|
|
|
163 |
/**
|
|
|
164 |
* Sets the NewRelic application that should receive this log.
|
|
|
165 |
*/
|
|
|
166 |
protected function setNewRelicAppName(string $appName): void
|
|
|
167 |
{
|
|
|
168 |
newrelic_set_appname($appName);
|
|
|
169 |
}
|
|
|
170 |
|
|
|
171 |
/**
|
|
|
172 |
* Overwrites the name of the current transaction
|
|
|
173 |
*/
|
|
|
174 |
protected function setNewRelicTransactionName(string $transactionName): void
|
|
|
175 |
{
|
|
|
176 |
newrelic_name_transaction($transactionName);
|
|
|
177 |
}
|
|
|
178 |
|
|
|
179 |
/**
|
|
|
180 |
* @param string $key
|
|
|
181 |
* @param mixed $value
|
|
|
182 |
*/
|
|
|
183 |
protected function setNewRelicParameter(string $key, $value): void
|
|
|
184 |
{
|
|
|
185 |
if (null === $value || is_scalar($value)) {
|
|
|
186 |
newrelic_add_custom_parameter($key, $value);
|
|
|
187 |
} else {
|
|
|
188 |
newrelic_add_custom_parameter($key, Utils::jsonEncode($value, null, true));
|
|
|
189 |
}
|
|
|
190 |
}
|
|
|
191 |
|
|
|
192 |
/**
|
|
|
193 |
* {@inheritDoc}
|
|
|
194 |
*/
|
|
|
195 |
protected function getDefaultFormatter(): FormatterInterface
|
|
|
196 |
{
|
|
|
197 |
return new NormalizerFormatter();
|
|
|
198 |
}
|
|
|
199 |
}
|