NewRelicHandler.php 6.1 KB

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