Skip to content

Commit

Permalink
Create plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
janvanicek committed Aug 7, 2020
1 parent 04837b7 commit e1839fa
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
vendor
composer.lock
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Nette Logger

Tracy logger extension capable of logging messages and errors to API.

*Note*: If you have debug mode enabled in your application, logger will only send `\Tracy\Debugger::log()` messages to sentry. Any exception ending with Tracy's blue screen is not going to be logged as you can see the exception details directly.

You can disable debug mode by inserting the lines below in file *bootstrap.php*

```php
$configurator->setDebugMode(false);
```

## Installation

Install package via Composer:

```
composer require residit/nette-logger
```

## Configuration

Enable and configure the extension in Nette config file:

```neon
extensions:
# ...
nette-logger: Residit\NetteLogger\DI\NetteLoggerExtension
nette-logger:
dsn: https://[email protected]/123 # required
environment: production # optional, defaults to "local"
user_fields: # optional, defaults to empty array; Nette's identity ID is being sent automatically
- email
priority_mapping:
mypriority: warning
```

### Priority-Severity mapping

Sometimes you might need to use custom *priority* when logging the error in Nette:

```php
\Tracy\Debugger::log('foo', 'mypriority');
```

Sentry only allows strict set of severities. By default any message with unknown (non-standard) severity is not being logged.

You can map your custom *priority* to Sentry's *severity* in config by using `priority_mapping` as shown in the example.

The allowed set of Sentry severities can be checked in [Sentry's PHP repository](https://github.com/getsentry/sentry-php/blob/master/src/Severity.php).

## Usage

Once enabled as extension, you can continue to throw exceptions without any change. To log message please use `\Tracy\Debugger::log()` method.
30 changes: 27 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
{
"name": "residit/nette-logger",
"require": {}
}
"name": "residit/nette-logger",
"description": "Custom Nette logger library",
"homepage": "https://github.com/residit/nette-logger",
"license": "MIT",
"keywords": ["nette", "errorlogger", "errors"],
"authors": [
{
"name": "Jan Vaníček",
"email": "[email protected]",
"homepage": "https://janvanicek.com/",
"role": "Developer"
}
],
"type": "library",
"require": {
"sentry/sdk": "^2.1",
"tracy/tracy": "~2.4",
"nette/di": "^2.4 || ^3.0",
"nette/security": "^2.4 || ^3.0",
"nette/http": "^2.4 || ^3.0"
},
"autoload": {
"psr-4": {
"Residit\\NetteLogger\\": "/src"
}
}
}
87 changes: 87 additions & 0 deletions src/DI/NetteLoggerExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace Residit\NetteLogger\DI;

use Nette\DI\CompilerExtension;
use Tracy\Debugger;
use Tracy\ILogger;

class NetteLoggerExtension extends CompilerExtension
{
private const PARAM_DSN = 'dsn';
private const PARAM_ENVIRONMENT = 'environment';
private const PARAM_USER_FIELDS = 'user_fields';
private const PARAM_PRIORITIES_MAPPING = 'priority_mapping';

private $defaults = [
self::PARAM_DSN => null,
self::PARAM_ENVIRONMENT => 'local',
self::PARAM_USER_FIELDS => [],
self::PARAM_PRIORITIES_MAPPING => [],
];

private $enabled = false;

public function loadConfiguration()
{
$this->validateConfig($this->defaults);
if (!$this->config[self::PARAM_DSN]) {
Debugger::log('Unable to initialize SentryExtension, dsn config option is missing', ILogger::WARNING);
return;
}
$this->enabled = true;

$this->getContainerBuilder()
->addDefinition($this->prefix('logger'))
->setFactory(\Residit\NetteLogger\NetteLogger::class)
->addSetup(
'register',
[
$this->config[self::PARAM_DSN],
$this->config[self::PARAM_ENVIRONMENT],
]
)->addSetup(
'setUserFields',
[
$this->config[self::PARAM_USER_FIELDS],
]
)->addSetup(
'setPriorityMapping',
[
$this->config[self::PARAM_PRIORITIES_MAPPING],
]
);
}

public function beforeCompile()
{
if (!$this->enabled) {
return;
}

$builder = $this->getContainerBuilder();
if ($builder->hasDefinition('tracy.logger')) {
$builder->getDefinition('tracy.logger')->setAutowired(false);
}
if ($builder->hasDefinition('security.user')) {
$builder->getDefinition($this->prefix('logger'))
->addSetup('setUser', [$builder->getDefinition('security.user')]);
}
if ($builder->hasDefinition('session.session')) {
$builder->getDefinition($this->prefix('logger'))
->addSetup('setSession', [$builder->getDefinition('session.session')]);
}
}

public function afterCompile(\Nette\PhpGenerator\ClassType $class)
{
if (!$this->enabled) {
return;
}

$class->getMethod('initialize')
->addBody('Tracy\Debugger::setLogger($this->getService(?));', [ $this->prefix('logger') ]);
}
}
144 changes: 144 additions & 0 deletions src/NetteLogger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

declare(strict_types=1);

namespace Residit\NetteLogger;

use Nette\Http\Session;
use Nette\Security\IIdentity;
use Nette\Security\User;
use Sentry\ClientBuilder;
use Sentry\Integration\RequestIntegration;
use Sentry\Severity;
use Sentry\State\Hub;
use Tracy\Debugger;
use Tracy\ILogger;
use Tracy\Logger;
use function Sentry\captureException;
use function Sentry\captureMessage;
use function Sentry\configureScope;

class NetteLogger extends Logger
{
/** @var IIdentity */
private $identity;

/** @var Session */
private $session;

/** @var array */
private $userFields = [];

/** @var array */
private $priorityMapping = [];

public function register(string $dsn, string $environment)
{
$options = new \Sentry\Options([
'dsn' => $dsn,
'environment' => $environment,
'default_integrations' => false,
]);

$options->setIntegrations([
new RequestIntegration($options),
]);

$builder = new ClientBuilder($options);
$client = $builder->getClient();
Hub::setCurrent(new Hub($client));

$this->email = & Debugger::$email;
$this->directory = Debugger::$logDirectory;
}

public function setUser(User $user)
{
$this->identity = $user->getIdentity();
}

public function setUserFields(array $userFields)
{
$this->userFields = $userFields;
}

public function setPriorityMapping(array $priorityMapping)
{
$this->priorityMapping = $priorityMapping;
}

public function setSession(Session $session)
{
$this->session = $session;
}

public function log($value, $priority = ILogger::INFO)
{
$response = parent::log($value, $priority);
$severity = $this->tracyPriorityToSentrySeverity($priority);

// if it's non-default severity, let's try configurable mapping
if (!$severity) {
$mappedSeverity = $this->priorityMapping[$priority] ?? null;
if ($mappedSeverity) {
$severity = new Severity((string) $mappedSeverity);
}
}
// if we still don't have severity, don't log anything
if (!$severity) {
return $response;
}

configureScope(function (\Sentry\State\Scope $scope) use ($severity) {
if (!$severity) {
return;
}
$scope->setLevel($severity);
if ($this->identity) {
$userFields = [
'id' => $this->identity->getId(),
];
foreach ($this->userFields as $name) {
$userFields[$name] = $this->identity->{$name} ?? null;
}
$scope->setUser($userFields);
}
if ($this->session) {
$data = [];
foreach ($this->session->getIterator() as $section) {
foreach ($this->session->getSection($section)->getIterator() as $key => $val) {
$data[$section][$key] = $val;
}
}
$scope->setExtra('session', $data);
}
});

if ($value instanceof \Throwable) {
captureException($value);
} else {
captureMessage($value);
}

return $response;
}

private function tracyPriorityToSentrySeverity(string $priority): ?Severity
{
switch ($priority) {
case ILogger::DEBUG:
return Severity::debug();
case ILogger::INFO:
return Severity::info();
case ILogger::WARNING:
return Severity::warning();
case ILogger::ERROR:
case ILogger::EXCEPTION:
return Severity::error();
case ILogger::CRITICAL:
return Severity::fatal();
default:
return null;
}
}
}

0 comments on commit e1839fa

Please sign in to comment.