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

Monolog V3 Support #23

Merged
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
vendor
composer.lock
.idea
*.iml
phpunit.xml
*.cache
*.bak
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@

# Stackify Monolog v2 Handler
# Stackify Monolog v3 Handler

Monolog handler for sending log messages and exceptions to Stackify.
Monolog >= 2.0.0 is supported.
Monolog >= 3.0.0 is supported.

> For Monolog v1, use the [1.x branch](https://github.com/stackify/stackify-log-monolog/tree/1.x)
For Monolog V1

> Use the [1.x branch](https://github.com/stackify/stackify-log-monolog/tree/1.x)

For Monolog V2

> Use the [2.x branch](https://github.com/stackify/stackify-log-monolog/tree/2.x)

* **Errors and Logs Overview:** http://support.stackify.com/errors-and-logs-overview/
* **Sign Up for a Trial:** http://www.stackify.com/sign-up/
Expand Down
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pool:
vmImage: ubuntu-latest

variables:
phpVersion: 7.2
phpVersion: 8.1

steps:
- script: |
Expand Down
15 changes: 12 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@
"type": "library",
"license": "Apache-2.0",
"require": {
"php": ">=7.0",
"stackify/logger": "~1.4",
"monolog/monolog": "~2.0"
"php": ">=8.1",
"stackify/logger": "~1.6",
"monolog/monolog": "~3.0"
},
"require-dev": {
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-strict-rules": "^1.4",
"phpunit/phpunit": "^10.1"
},
"autoload": {
"psr-4": {
"Stackify\\Log\\Monolog\\": "src/Stackify/Log/Monolog"
},
"autoload-dev": {
"psr-4": {"Stackify\\Log\\Monolog\\Tests": "tests/"}
}
},
"minimum-stability": "dev",
Expand Down
8 changes: 8 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" beStrictAboutTestsThatDoNotTestAnything="false" bootstrap="tests/bootstrap.php" colors="true" processIsolation="false" stopOnError="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Stackify PHP Monolog Handler Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
</phpunit>
39 changes: 26 additions & 13 deletions src/Stackify/Log/Monolog/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

use Monolog\Logger;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Level;
use Monolog\LogRecord;

class Handler extends AbstractProcessingHandler
{
Expand All @@ -16,7 +18,12 @@ class Handler extends AbstractProcessingHandler
*
* @var \Stackify\Log\Transport\TransportInterface
*/
private $_transport;
private TransportInterface $_transport;

/**
* Include channel inside message as hashtag
*/
private bool $includeChannel;

/**
* Stackify monolog handler
Expand All @@ -29,14 +36,15 @@ class Handler extends AbstractProcessingHandler
* @param boolean $bubble
*/
public function __construct(
$appName,
$environmentName = null,
string $appName,
string $environmentName = null,
TransportInterface $transport = null,
$logServerVariables = false,
$config = null,
$level = Logger::DEBUG,
$bubble = true
bool $logServerVariables = false,
array $config = null,
int|string|Level $level = Level::Debug,
bool $bubble = true
) {

parent::__construct($level, $bubble);

if ($config) {
Expand All @@ -45,34 +53,39 @@ public function __construct(
AgentConfig::getInstance()->extract($config);
}

$messageBuilder = new MessageBuilder('Stackify Monolog v.2.0', $appName, $environmentName, $logServerVariables);
$messageBuilder = new MessageBuilder('Stackify Monolog v.3.0', $appName, $environmentName, $logServerVariables);

if (null === $transport) {
$transport = new AgentSocketTransport();
}

$transport->setMessageBuilder($messageBuilder);
$this->_transport = $transport;
$this->includeChannel = false;

if ($config && $config['includeChannel']) {
$this->includeChannel = true;
}
}

/**
* {@inheritdoc}
*
* @param array $record
* @param LogRecord $record
*
* @return void
*/
public function write(array $record): void
public function write(LogRecord $record): void
{
$this->_transport->addEntry(new LogEntry($record));
$this->_transport->addEntry(new LogEntry($record, $this->includeChannel));
}

/**
* Flush logs to API
*
* @return void
*/
public function flush()
public function flush(): void
{
$this->_transport->finish();
}
Expand All @@ -93,7 +106,7 @@ public function close(): void
*
* @return void
*/
public function reset()
public function reset(): void
{
$this->flush();
parent::reset();
Expand Down
16 changes: 15 additions & 1 deletion src/Stackify/Log/Monolog/LogEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Stackify\Log\Entities\NativeError;

use Monolog\Logger as MonologLogger;
use Monolog\LogRecord as MonologLogRecord;

final class LogEntry implements LogEntryInterface
{
Expand All @@ -14,10 +15,13 @@ final class LogEntry implements LogEntryInterface
private $exception;
private $context;
private $nativeError;
private $includeChannel;
private $channel;

public function __construct(array $record)
public function __construct(MonologLogRecord $record, bool $includeChannel = false)
{
$this->record = $record;

$context = $record['context'];
// find exception and remove from context
foreach ($context as $key => $value) {
Expand All @@ -44,6 +48,12 @@ public function __construct(array $record)
if (!empty($context)) {
$this->context = $context;
}

$this->includeChannel = $includeChannel;
$this->channel = null;
if ($record && $record['channel']) {
$this->channel = $record['channel'];
}
}

public function getContext()
Expand All @@ -63,6 +73,10 @@ public function getLevel()

public function getMessage()
{
if ($this->includeChannel && $this->channel) {
return $this->record['message']." #{$this->channel}";
}

return $this->record['message'];
}

Expand Down
139 changes: 139 additions & 0 deletions tests/HandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php

namespace Stackify\Log\Monolog\Tests;

use Monolog\Level;
use Monolog\Test\TestCase;
use Psr\Log\LogLevel;
use Stackify\Log\Builder\BuilderInterface;
use Stackify\Log\Entities\LogEntryInterface;
use Stackify\Log\Monolog\Handler;
use Stackify\Log\Transport\TransportInterface;

class HandlerTest extends TestCase {

protected $appName = 'Test App';
protected $environmentName = 'Test Environment';

public static function logLevelProvider()
{
return array_map(
fn (Level $level) => [$level],
Level::cases()
);
}

/**
* @dataProvider logLevelProvider
*/
public function testHandlesAllLevels(Level $level)
{
$message = 'Hello, world! ' . $level->value;
$context = ['foo' => 'bar', 'level' => $level->value];

$transport = $this->createDummyTransport();

$handler = $this->createDummyHandler($transport, $level);
$record = $this->getRecord($level, $message, context: $context);
$handler->handle($record);

$logEntries = $transport->getEntries();

$firstLogEntry = $logEntries[0];

$this->assertEquals(1, count($logEntries));
$this->assertEquals($firstLogEntry->getLevel(), strtoupper($record->level->name));
$this->assertEquals($firstLogEntry->getMessage(), $record->message);

// Reset every call
$transport->reset();
}

public function testLogRecordChannel()
{
$level = Level::Info;
$message = 'Hello, world! ' . $level->value;
$context = ['foo' => 'bar', 'level' => $level->value];

$transport = $this->createDummyTransport();

$includeChannel = true;
$channel = "test";
$handler = $this->createDummyHandler($transport, $level, $includeChannel);
$record = $this->getRecord($level, $message, context: $context, channel: $channel);
$handler->handle($record);

$logEntries = $transport->getEntries();

$firstLogEntry = $logEntries[0];
$this->assertEquals(1, count($logEntries));
$this->assertEquals($firstLogEntry->getLevel(), strtoupper($record->level->name));
$this->assertEquals($firstLogEntry->getMessage(), $record->message . " #{$channel}");

// Reset every call
$transport->reset();
}

private function createDummyHandler($transport, Level $level = null, $includeChannel = false) {
return $this->createHandler($this->appName, $this->environmentName, $transport, $level, $includeChannel);
}

private function createHandler($appName, $environmentName, $transport, Level $level = null, bool $includeChannel = false): Handler
{
$config = [];
if ($includeChannel) {
$config['includeChannel'] = true;
}

if (null === $level) {
$handler = new Handler($appName, $environmentName, $transport, false, $config, $level);
} else {
$handler = new Handler($appName, $environmentName, $transport, false, $config);
}

return $handler;
}

private function createDummyTransport()
{
return new SpyTransport();
}
}


class SpyTransport implements TransportInterface {
protected BuilderInterface $messageBuilder;
protected array $logEntries;
protected bool $hasFinished;

public function __construct() {
$this->logEntries = [];
$this->hasFinished = false;
}

public function setMessageBuilder(BuilderInterface $messageBuilder) {
$this->messageBuilder = $messageBuilder;
}

public function addEntry(LogEntryInterface $logEntry): int {
$this->logEntries[] = $logEntry;
return count($this->logEntries) - 1;
}

public function finish() {
$this->hasFinished = true;
}

public function hasFinish() {
return $this->hasFinished;
}

public function getEntries() {
return $this->logEntries;
}

public function reset() {
$this->logEntries = [];
$this->hasFinished = false;
}
}
3 changes: 3 additions & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

require __DIR__.'/../vendor/autoload.php';
Loading