Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: extract AbstractFormatter and AbstractHandler into separate files #38

Open
wants to merge 1 commit into
base: v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions src/AbstractFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/**
* Copyright [2019] New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* This file contains the abstract parent of the Formatter class for
* the New Relic Monolog Enricher. This class implements all functionality
* that is compatible with all Monolog API versions
*
* @author New Relic PHP <[email protected]>
*/

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);
}
}
165 changes: 165 additions & 0 deletions src/AbstractHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<?php

/**
* Copyright [2019] New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* This file contains the abstract parent of the Handler class for
* the New Relic Monolog Enricher. This class implements all functionality
* that is compatible with all Monolog API versions
*
* @author New Relic PHP <[email protected]>
*/

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";
}
}
49 changes: 0 additions & 49 deletions src/Formatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading