diff --git a/src/AbstractFormatter.php b/src/AbstractFormatter.php new file mode 100644 index 0000000000..083c928748 --- /dev/null +++ b/src/AbstractFormatter.php @@ -0,0 +1,65 @@ + + */ + +namespace NewRelic\Monolog\Enricher; + +use Monolog\Formatter\JsonFormatter; +use Monolog\Logger; + +/** + * Formats record as a JSON object with transformations necessary for + * ingestion by New Relic Logs + */ +abstract class AbstractFormatter extends JsonFormatter +{ + /** + * @param int $batchMode + * @param bool $appendNewline + */ + public function __construct( + $batchMode = self::BATCH_MODE_NEWLINES, + $appendNewline = true + ) { + // BATCH_MODE_NEWLINES is required for batch compatibility with New + // Relic log forwarder plugins, which handle batching records. When + // using the New Relic Monolog handler along side a batching handler + // such as the BufferHandler, BATCH_MODE_JSON is required to adhere + // to the New Relic logs API bulk ingest format. + parent::__construct($batchMode, $appendNewline); + } + + + /** + * Moves New Relic context information from the + * `$data['extra']['newrelic-context']` array to top level of record, + * converts `datetime` object to `timestamp` top level element represented + * as milliseconds since the UNIX epoch, and finally, normalizes the data + * + * @param mixed $data + * @param int $depth + * @return mixed + */ + protected function normalize($data, $depth = 0) + { + if ($depth == 0) { + if (isset($data['extra']['newrelic-context'])) { + $data = array_merge($data, $data['extra']['newrelic-context']); + unset($data['extra']['newrelic-context']); + } + $data['timestamp'] = intval( + $data['datetime']->format('U.u') * 1000 + ); + } + return parent::normalize($data, $depth); + } +} diff --git a/src/AbstractHandler.php b/src/AbstractHandler.php new file mode 100644 index 0000000000..a66d426af2 --- /dev/null +++ b/src/AbstractHandler.php @@ -0,0 +1,165 @@ + + */ + +namespace NewRelic\Monolog\Enricher; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Handler\Curl; +use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Handler\HandlerInterface; +use Monolog\Handler\MissingExtensionException; +use Monolog\Logger; +use Monolog\Util; + +abstract class AbstractHandler extends AbstractProcessingHandler +{ + protected $host = null; + protected $endpoint = 'log/v1'; + protected $licenseKey; + protected $protocol = 'https://'; + + /** + * @param string|int $level The minimum logging level to trigger handler + * @param bool $bubble Whether messages should bubble up the stack. + * + * @throws MissingExtensionException If the curl extension is missing + */ + public function __construct($level = Logger::DEBUG, $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException( + 'The curl extension is required to use this Handler' + ); + } + + $this->licenseKey = ini_get('newrelic.license'); + if (!$this->licenseKey) { + $this->licenseKey = "NO_LICENSE_KEY_FOUND"; + } + + parent::__construct($level, $bubble); + } + + /** + * Sets the New Relic license key. Defaults to the New Relic INI's + * value for 'newrelic.license' if available. + * + * @param string $key + */ + public function setLicenseKey($key) + { + $this->licenseKey = $key; + } + + /** + * Sets the hostname of the New Relic Logging API. Defaults to the + * US Prod endpoint (log-api.newrelic.com). Another useful value is + * log-api.eu.newrelic.com for the EU production endpoint. + * + * @param string $host + */ + public function setHost($host) + { + $this->host = $host; + } + + /** + * Obtains a curl handler initialized to POST to the host specified by + * $this->setHost() + * + * @return resource $ch curl handler + */ + protected function getCurlHandler() + { + $host = is_null($this->host) + ? self::getDefaultHost($this->licenseKey) + : $this->host; + + $url = "{$this->protocol}{$host}/{$this->endpoint}"; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + return $ch; + } + + /** + * Augments JSON-formatted data with New Relic license key and other + * necessary headers, and POSTs the log to the New Relic logging + * endpoint via Curl + * + * @param string $data + */ + protected function send($data) + { + $ch = $this->getCurlHandler(); + + $headers = array( + 'Content-Type: application/json', + 'X-License-Key: ' . $this->licenseKey + ); + + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + Curl\Util::execute($ch, 5, false); + } + + /** + * Augments a JSON-formatted array data with New Relic license key + * and other necessary headers, and POSTs the log to the New Relic + * logging endpoint via Curl + * + * @param string $data + */ + protected function sendBatch($data) + { + $ch = $this->getCurlHandler(); + + $headers = array( + 'Content-Type: application/json', + 'X-License-Key: ' . $this->licenseKey + ); + + $postData = '[{"logs":' . $data . '}]'; + + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + Curl\Util::execute($ch, 5, false); + } + + /** + * Given a licence key, returns the default log API host for that region. + * + * @param string $licenseKey + * @return string + */ + protected static function getDefaultHost($licenseKey) + { + if (!is_string($licenseKey)) { + throw new \InvalidArgumentException( + 'Unknown license key of type ' . gettype($licenseKey) + ); + } + + $matches = array(); + if (preg_match('/^([a-z]{2,3})[0-9]{2}x/', $licenseKey, $matches)) { + $region = ".{$matches[1]}"; + } else { + // US licence keys generally don't include region identifiers, so + // we'll default to that. + $region = ''; + } + + return "log-api$region.newrelic.com"; + } +} diff --git a/src/Formatter.php b/src/Formatter.php index c76ba6593c..66be6212ff 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -13,57 +13,8 @@ namespace NewRelic\Monolog\Enricher; -use Monolog\Formatter\JsonFormatter; use Monolog\Logger; -/** - * Formats record as a JSON object with transformations necessary for - * ingestion by New Relic Logs - */ -abstract class AbstractFormatter extends JsonFormatter -{ - /** - * @param int $batchMode - * @param bool $appendNewline - */ - public function __construct( - $batchMode = self::BATCH_MODE_NEWLINES, - $appendNewline = true - ) { - // BATCH_MODE_NEWLINES is required for batch compatibility with New - // Relic log forwarder plugins, which handle batching records. When - // using the New Relic Monolog handler along side a batching handler - // such as the BufferHandler, BATCH_MODE_JSON is required to adhere - // to the New Relic logs API bulk ingest format. - parent::__construct($batchMode, $appendNewline); - } - - - /** - * Moves New Relic context information from the - * `$data['extra']['newrelic-context']` array to top level of record, - * converts `datetime` object to `timestamp` top level element represented - * as milliseconds since the UNIX epoch, and finally, normalizes the data - * - * @param mixed $data - * @param int $depth - * @return mixed - */ - protected function normalize($data, $depth = 0) - { - if ($depth == 0) { - if (isset($data['extra']['newrelic-context'])) { - $data = array_merge($data, $data['extra']['newrelic-context']); - unset($data['extra']['newrelic-context']); - } - $data['timestamp'] = intval( - $data['datetime']->format('U.u') * 1000 - ); - } - return parent::normalize($data, $depth); - } -} - // phpcs:disable /* * This extension to the Monolog framework supports the same PHP versions diff --git a/src/Handler.php b/src/Handler.php index 032c6b7b92..e1a68799c9 100644 --- a/src/Handler.php +++ b/src/Handler.php @@ -13,156 +13,7 @@ namespace NewRelic\Monolog\Enricher; -use Monolog\Formatter\FormatterInterface; -use Monolog\Handler\Curl; -use Monolog\Handler\AbstractProcessingHandler; -use Monolog\Handler\HandlerInterface; -use Monolog\Handler\MissingExtensionException; use Monolog\Logger; -use Monolog\Util; - -abstract class AbstractHandler extends AbstractProcessingHandler -{ - protected $host = null; - protected $endpoint = 'log/v1'; - protected $licenseKey; - protected $protocol = 'https://'; - - /** - * @param string|int $level The minimum logging level to trigger handler - * @param bool $bubble Whether messages should bubble up the stack. - * - * @throws MissingExtensionException If the curl extension is missing - */ - public function __construct($level = Logger::DEBUG, $bubble = true) - { - if (!extension_loaded('curl')) { - throw new MissingExtensionException( - 'The curl extension is required to use this Handler' - ); - } - - $this->licenseKey = ini_get('newrelic.license'); - if (!$this->licenseKey) { - $this->licenseKey = "NO_LICENSE_KEY_FOUND"; - } - - parent::__construct($level, $bubble); - } - - /** - * Sets the New Relic license key. Defaults to the New Relic INI's - * value for 'newrelic.license' if available. - * - * @param string $key - */ - public function setLicenseKey($key) - { - $this->licenseKey = $key; - } - - /** - * Sets the hostname of the New Relic Logging API. Defaults to the - * US Prod endpoint (log-api.newrelic.com). Another useful value is - * log-api.eu.newrelic.com for the EU production endpoint. - * - * @param string $host - */ - public function setHost($host) - { - $this->host = $host; - } - - /** - * Obtains a curl handler initialized to POST to the host specified by - * $this->setHost() - * - * @return resource $ch curl handler - */ - protected function getCurlHandler() - { - $host = is_null($this->host) - ? self::getDefaultHost($this->licenseKey) - : $this->host; - - $url = "{$this->protocol}{$host}/{$this->endpoint}"; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - return $ch; - } - - /** - * Augments JSON-formatted data with New Relic license key and other - * necessary headers, and POSTs the log to the New Relic logging - * endpoint via Curl - * - * @param string $data - */ - protected function send($data) - { - $ch = $this->getCurlHandler(); - - $headers = array( - 'Content-Type: application/json', - 'X-License-Key: ' . $this->licenseKey - ); - - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - Curl\Util::execute($ch, 5, false); - } - - /** - * Augments a JSON-formatted array data with New Relic license key - * and other necessary headers, and POSTs the log to the New Relic - * logging endpoint via Curl - * - * @param string $data - */ - protected function sendBatch($data) - { - $ch = $this->getCurlHandler(); - - $headers = array( - 'Content-Type: application/json', - 'X-License-Key: ' . $this->licenseKey - ); - - $postData = '[{"logs":' . $data . '}]'; - - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - Curl\Util::execute($ch, 5, false); - } - - /** - * Given a licence key, returns the default log API host for that region. - * - * @param string $licenseKey - * @return string - */ - protected static function getDefaultHost($licenseKey) - { - if (!is_string($licenseKey)) { - throw new \InvalidArgumentException( - 'Unknown license key of type ' . gettype($licenseKey) - ); - } - - $matches = array(); - if (preg_match('/^([a-z]{2,3})[0-9]{2}x/', $licenseKey, $matches)) { - $region = ".{$matches[1]}"; - } else { - // US licence keys generally don't include region identifiers, so - // we'll default to that. - $region = ''; - } - - return "log-api$region.newrelic.com"; - } -} // phpcs:disable /*