-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
272 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
composer.lock | ||
/vendor/ | ||
/.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Datadog Monolog integration | ||
|
||
This log handler for Monolog that pushes logs to datadog. | ||
|
||
## Requirements | ||
- PHP 8.0+ | ||
- PHP Curl | ||
|
||
## Installation | ||
|
||
```shell | ||
composer require sgoettsch/monolog-datadog | ||
``` | ||
|
||
### Basic Usage | ||
|
||
```php | ||
<?php | ||
|
||
use Monolog\Logger; | ||
use sgoettsch\MonologDatadog\Handler\DatadogHandler; | ||
|
||
$apiKey = 'DATADOG-API-KEY'; | ||
$host = 'https://http-intake.logs.datadoghq.com'; // could be set to other domains for example for EU hosted accounts | ||
$attributes = [ | ||
'hostname' => 'YOUR_HOSTNAME', | ||
'source' => 'php', | ||
'service' => 'YOUR-SERVICE' | ||
]; | ||
|
||
$logger = new Logger('datadog-channel'); | ||
|
||
$datadogLogs = new DatadogHandler($apiKey, $host, $attributes, Logger::INFO); | ||
|
||
$logger->pushHandler($datadogLogs); | ||
|
||
$logger->info('i am an info'); | ||
$logger->warning('i am a warning..'); | ||
$logger->error('i am an error '); | ||
$logger->notice('i am a notice'); | ||
$logger->emergency('i am an emergency'); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "sgoettsch/monolog-datadog", | ||
"description": "Monolog Handler to forward logs to Datadog", | ||
"type": "library", | ||
"license": "MIT", | ||
"keywords": [ | ||
"monolog" | ||
], | ||
"homepage": "https://github.com/sgoettsch/monolog-datadog", | ||
"authors": [ | ||
{ | ||
"name": "Sebastian Goettsch" | ||
} | ||
], | ||
"require": { | ||
"php": "^8.0", | ||
"ext-curl": "*", | ||
"monolog/monolog": ">=1.0 <3.0" | ||
}, | ||
"autoload": { | ||
"psr-4": { | ||
"sgoettsch\\MonologDatadog\\": "src" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?php | ||
|
||
namespace sgoettsch\MonologDatadog\Formatter; | ||
|
||
use Monolog\Formatter\JsonFormatter; | ||
use Monolog\Logger; | ||
use stdClass; | ||
|
||
class DatadogFormatter extends JsonFormatter | ||
{ | ||
protected $includeStacktraces = true; | ||
|
||
/** | ||
* Map Monolog\Logger levels to Datadog's default status type | ||
*/ | ||
private const DATADOG_LEVEL_MAP = [ | ||
Logger::DEBUG => 'info', | ||
Logger::INFO => 'info', | ||
Logger::NOTICE => 'warning', | ||
Logger::WARNING => 'warning', | ||
Logger::ERROR => 'error', | ||
Logger::ALERT => 'error', | ||
Logger::CRITICAL => 'error', | ||
Logger::EMERGENCY => 'error', | ||
]; | ||
|
||
public function format(array $record): string | ||
{ | ||
$normalized = $this->normalize($record); | ||
|
||
if (isset($normalized['context']) && $normalized['context'] === []) { | ||
$normalized['context'] = new stdClass; | ||
} | ||
|
||
if (isset($normalized['extra']) && $normalized['extra'] === []) { | ||
$normalized['extra'] = new stdClass; | ||
} | ||
|
||
$normalized['status'] = static::DATADOG_LEVEL_MAP[$record['level']]; | ||
|
||
return $this->toJson($normalized, true); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
<?php | ||
|
||
namespace sgoettsch\MonologDatadog\Handler; | ||
|
||
use Monolog\Handler\MissingExtensionException; | ||
use Monolog\Logger; | ||
use Monolog\Handler\AbstractProcessingHandler; | ||
use Monolog\Handler\Curl\Util; | ||
use Monolog\Formatter\FormatterInterface; | ||
use sgoettsch\MonologDatadog\Formatter\DatadogFormatter; | ||
|
||
class DatadogHandler extends AbstractProcessingHandler | ||
{ | ||
/** @var string Datadog API host */ | ||
private string $host; | ||
|
||
/** @var string Datadog API-Key */ | ||
private string $apiKey; | ||
|
||
/** @var array Datadog optional attributes */ | ||
private array $attributes; | ||
|
||
/** | ||
* @param string $apiKey Datadog API-Key | ||
* @param string $host Datadog API host | ||
* @param array $attributes Datadog optional attributes | ||
* @param int|string $level The minimum logging level at which this handler will be triggered | ||
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not | ||
* @throws MissingExtensionException | ||
*/ | ||
public function __construct( | ||
string $apiKey, | ||
string $host = 'https://http-intake.logs.datadoghq.com', | ||
array $attributes = [], | ||
int|string $level = Logger::DEBUG, | ||
bool $bubble = true | ||
) { | ||
if (!extension_loaded('curl')) { | ||
throw new MissingExtensionException('The curl extension is needed to use the DatadogHandler'); | ||
} | ||
|
||
parent::__construct($level, $bubble); | ||
|
||
$this->apiKey = $apiKey; | ||
$this->host = $host; | ||
$this->attributes = $attributes; | ||
} | ||
|
||
/** | ||
* Writes the record down to the log of the implementing handler | ||
* | ||
* @param array $record | ||
* @return void | ||
*/ | ||
protected function write(array $record): void | ||
{ | ||
$this->send($record); | ||
} | ||
|
||
/** | ||
* Send request to Datadog | ||
* | ||
* @param array $record | ||
*/ | ||
protected function send(array $record): void | ||
{ | ||
$headers = ['Content-Type:application/json']; | ||
|
||
$source = $this->getSource(); | ||
$hostname = $this->getHostname(); | ||
$service = $this->getService($record); | ||
$tags = $this->getTags($record); | ||
|
||
$url = $this->host . '/v1/input/'; | ||
$url .= $this->apiKey; | ||
/** @noinspection SpellCheckingInspection */ | ||
$url .= '?ddsource=' . $source . '&service=' . $service . '&hostname=' . $hostname . '&ddtags=' . $tags; | ||
|
||
$ch = curl_init(); | ||
|
||
curl_setopt($ch, CURLOPT_URL, $url); | ||
curl_setopt($ch, CURLOPT_POST, true); | ||
curl_setopt($ch, CURLOPT_POSTFIELDS, $record['formatted']); | ||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); | ||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | ||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); | ||
|
||
Util::execute($ch); | ||
} | ||
|
||
/** | ||
* Get Datadog Source from $attributes params. | ||
* | ||
* @return string | ||
*/ | ||
protected function getSource(): string | ||
{ | ||
return $this->attributes['source'] ?? 'php'; | ||
} | ||
|
||
/** | ||
* Get Datadog Service from $attributes params. | ||
* | ||
* @param array $record | ||
* | ||
* @return string | ||
*/ | ||
protected function getService(array $record): string | ||
{ | ||
return $this->attributes['service'] ?? $record['channel']; | ||
} | ||
|
||
/** | ||
* Get Datadog Hostname from $attributes params. | ||
* | ||
* @return string | ||
*/ | ||
protected function getHostname(): string | ||
{ | ||
return $this->attributes['hostname'] ?? $_SERVER['SERVER_NAME']; | ||
} | ||
|
||
/** | ||
* Get Datadog Tags from $attributes params. | ||
* | ||
* @param array $record | ||
* | ||
* @return string | ||
*/ | ||
protected function getTags(array $record): string | ||
{ | ||
$defaultTag = 'level:' . $record['level_name']; | ||
|
||
if (!isset($this->attributes['tags']) || !$this->attributes['tags']) { | ||
return $defaultTag; | ||
} | ||
|
||
if ( | ||
(is_array($this->attributes['tags']) || is_object($this->attributes['tags'])) | ||
&& !empty($this->attributes['tags']) | ||
) { | ||
$imploded = implode(',', (array)$this->attributes['tags']); | ||
|
||
return $imploded . ',' . $defaultTag; | ||
} | ||
|
||
return $defaultTag; | ||
} | ||
|
||
/** | ||
* Returns the default formatter to use with this handler | ||
* | ||
* @return DatadogFormatter | ||
*/ | ||
protected function getDefaultFormatter(): FormatterInterface | ||
{ | ||
return new DatadogFormatter(); | ||
} | ||
} |