Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 2.3.0
### Added
- Added sharing correlation ID between services.

## 2.2.0
### Added
- Added Symfony ^6 support.
Expand Down
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ the following features:
- adds correlation_id to correlate messages in Sentry with messages in Graylog from the same process;
- allows grouping some exceptions by their class, independently from where they were thrown at or what are their message;
- removes root prefix from messages (usually included in some exception messages);
- maps context to be available with logged sentry event.
- maps context to be available with logged sentry event.
- allows sharing the same correlation_id across multiple services via an HTTP header

Also recommended configuration is given to allow nice synergy between Graylog and Sentry.

Expand Down Expand Up @@ -96,7 +97,15 @@ sentry:
send_attempts: 1

paysera_logging_extra:
application_name: app-something # customise this to know which project message was sent from
application_name: app-something # customize this to know which project message was sent from
fetch_correlation_id_from_request: true # try to fetch correlation id from request header. false by default

# Enable sharing correlation ID between requests if needed
Paysera\LoggingExtraBundle\Service\CorrelationIdHttpClientDecorator:
decorates: http_client
arguments:
- '@paysera_logging_extra.correlation_id_provider'
- '@.inner'
```

## Usage
Expand Down Expand Up @@ -145,7 +154,7 @@ docker-compose up -d
docker-compose exec sentry sentry upgrade
```

You'll find Graylog at [http://localhost:9001/](http://localhost:9001/) and Sentry at
You'll find Graylog at [http://localhost:9001/](http://localhost:9001/) and Sentry at
[http://localhost:9002/](http://localhost:9002/).

Open Graylog, login with `admin` `admin`, choose `System` -> `Inputs` -> `GELF UDP` -> `Launch new input` ->
Expand All @@ -166,7 +175,7 @@ php test.php
```

View logged data in Graylog and Sentry instances. Change the code for further
test scenarios or just use Graylog and Sentry to set-up and test your real
test scenarios or just use Graylog and Sentry to set-up and test your real
project.

Cleanup afterwards:
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"symfony/dependency-injection": "^3.4|^4.0|^5.0|^6.0",
"symfony/expression-language": "^3.0 || ^4.0 || ^5.0 || ^6.0",
"symfony/framework-bundle": "^3.4.26|^4.2.7|^5.0|^6.0",
"symfony/http-client-contracts": "^3.5",
"symfony/http-kernel": "^3.4|^4.0|^5.0|^6.0",
"symfony/monolog-bundle": "^3.4|^5.0"
},
Expand Down
1 change: 1 addition & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function getConfigTreeBuilder()
$children = $rootNode->children();
$children->scalarNode('application_name')->isRequired();
$children->arrayNode('grouped_exceptions')->prototype('scalar');
$children->booleanNode('fetch_correlation_id_from_request')->defaultFalse();

return $treeBuilder;
}
Expand Down
1 change: 1 addition & 0 deletions src/DependencyInjection/PayseraLoggingExtraExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ public function load(array $configs, ContainerBuilder $container)

$container->setParameter('paysera_logging_extra.application_name', $config['application_name']);
$container->setParameter('paysera_logging_extra.grouped_exceptions', $config['grouped_exceptions']);
$container->setParameter('paysera_logging_extra.fetch_correlation_id_from_request', $config['fetch_correlation_id_from_request']);
}
}
2 changes: 1 addition & 1 deletion src/Listener/CorrelationIdListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Paysera\LoggingExtraBundle\Listener;

use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider;
use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent as LegacyResponseEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
Expand Down
7 changes: 2 additions & 5 deletions src/Listener/IterationEndListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@

namespace Paysera\LoggingExtraBundle\Listener;

use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider;
use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider;
use Sentry\ClientInterface;

/**
* Intended for cases where the same process is reused for separate job or request processing, like in PHPPM.
*
* Changes correlation_id while still maintaining same prefix to be able to find relations in cases of bugs happening
* due to shared state between different processing cycles
*/
class IterationEndListener
{
Expand All @@ -28,7 +25,7 @@ public function __construct(

public function afterIteration()
{
$this->correlationIdProvider->incrementIdentifier();
$this->correlationIdProvider->reset();
if ($this->sentryClient !== null) {
$this->sentryClient->flush();
}
Expand Down
5 changes: 0 additions & 5 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@
</imports>

<services>
<service id="paysera_logging_extra.correlation_id_provider"
class="Paysera\LoggingExtraBundle\Service\CorrelationIdProvider">
<argument>%paysera_logging_extra.application_name%</argument>
</service>

<service id="paysera_logging_extra.sentry_handler" alias="paysera_logging_extra.handler.sentry"/>
</services>
</container>
26 changes: 26 additions & 0 deletions src/Resources/config/services/correlation_id_provider.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="paysera_logging_extra.correlation_id_provider.generated"
class="Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\GeneratedCorrelationIdProvider">
<argument>%paysera_logging_extra.application_name%</argument>
</service>

<service id="paysera_logging_extra.correlation_id_provider.request_header"
class="Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\RequestHeaderCorrelationIdProvider">
<argument type="service" id="request_stack"></argument>
</service>


<service id="paysera_logging_extra.correlation_id_provider"
class="Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider">
<argument type="service" id="paysera_logging_extra.correlation_id_provider.generated"></argument>
<argument type="service" id="paysera_logging_extra.correlation_id_provider.request_header"></argument>
<argument>%paysera_logging_extra.fetch_correlation_id_from_request%</argument>
</service>
</services>
</container>
35 changes: 35 additions & 0 deletions src/Service/CorrelationIdHttpClientDecorator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Paysera\LoggingExtraBundle\Service;

use Paysera\LoggingExtraBundle\Listener\CorrelationIdListener;
use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider;
use Symfony\Component\HttpClient\DecoratorTrait;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;

class CorrelationIdHttpClientDecorator implements HttpClientInterface
{
use DecoratorTrait;

private $correlationIdProvider;

public function __construct(
CorrelationIdProvider $correlationIdProvider,
?HttpClientInterface $client = null
) {
$this->client = $client ?? HttpClient::create();
$this->correlationIdProvider = $correlationIdProvider;
}

public function request(string $method, string $url, array $options = []): ResponseInterface
{
$options['headers'] = $options['headers'] ?? [];
$options['headers'][CorrelationIdListener::HEADER_NAME] = $this->correlationIdProvider->getCorrelationId();

return $this->client->request($method, $url, $options);
}
}
31 changes: 0 additions & 31 deletions src/Service/CorrelationIdProvider.php

This file was deleted.

54 changes: 54 additions & 0 deletions src/Service/CorrelationIdProvider/CorrelationIdProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Paysera\LoggingExtraBundle\Service\CorrelationIdProvider;

/**
* @internal
*/
class CorrelationIdProvider
{
private $generatedCorrelationIdProvider;
private $requestHeaderCorrelationIdProvider;
private $fetchFromRequest;
private $isFetchedFromRequest;

public function __construct(
GeneratedCorrelationIdProvider $generatedCorrelationIdProvider,
RequestHeaderCorrelationIdProvider $requestHeaderCorrelationIdProvider,
bool $fetchFromRequest
) {
$this->generatedCorrelationIdProvider = $generatedCorrelationIdProvider;
$this->requestHeaderCorrelationIdProvider = $requestHeaderCorrelationIdProvider;
$this->fetchFromRequest = $fetchFromRequest;
$this->isFetchedFromRequest = false;
}

public function getCorrelationId(): string
{
if ($this->fetchFromRequest) {
$correlationIdFromRequest = $this->requestHeaderCorrelationIdProvider->getCorrelationId();

if ($correlationIdFromRequest !== null) {
$this->isFetchedFromRequest = true;

return $correlationIdFromRequest;
}
}

return $this->generatedCorrelationIdProvider->getCorrelationId();
}

public function reset(): void
{
if ($this->isFetchedFromRequest) {
$this->requestHeaderCorrelationIdProvider->reset();
$this->isFetchedFromRequest = false;

return;
}

$this->generatedCorrelationIdProvider->increment();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Paysera\LoggingExtraBundle\Service\CorrelationIdProvider;

/**
* @internal
*/
class GeneratedCorrelationIdProvider
{
private $correlationId;
private $systemName;
private $increment;

public function __construct(string $systemName)
{
$this->systemName = $systemName;
$this->correlationId = null;
$this->increment = 0;
$this->correlationId = uniqid($this->systemName, true);
}

public function getCorrelationId(): string
{
if ($this->increment === 0) {
return $this->correlationId;
}

return $this->buildIncrementedCorrelationId();
}

private function buildIncrementedCorrelationId(): string
{
return sprintf('%s_%s', $this->correlationId, $this->increment);
}

/**
* Changes correlation_id while still maintaining same prefix to be able to find relations in
* cases of bugs happening due to shared state between different processing cycles
*/
public function increment(): void
{
if ($this->increment === PHP_INT_MAX) {
$this->increment = 0;
}

$this->increment++;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Paysera\LoggingExtraBundle\Service\CorrelationIdProvider;

use Symfony\Component\HttpFoundation\RequestStack;
use Paysera\LoggingExtraBundle\Listener\CorrelationIdListener;

/**
* @internal
*/
class RequestHeaderCorrelationIdProvider
{
private $requestStack;
private $correlationId;

public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
$this->correlationId = null;
}

public function getCorrelationId(): ?string
{
if ($this->correlationId !== null) {
return $this->correlationId;
}

$request = $this->requestStack->getCurrentRequest();
if ($request === null) {
return null;
}

$this->correlationId = $request->headers->get(CorrelationIdListener::HEADER_NAME);

return $this->correlationId;
}

public function reset(): void
{
$this->correlationId = null;
}
}
2 changes: 1 addition & 1 deletion src/Service/Processor/CorrelationIdProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Paysera\LoggingExtraBundle\Service\Processor;

use Monolog\Processor\ProcessorInterface;
use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider;
use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider;

class CorrelationIdProcessor implements ProcessorInterface
{
Expand Down
2 changes: 1 addition & 1 deletion tests/Functional/FunctionalCorrelationIdListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Paysera\LoggingExtraBundle\Tests\Functional;

use Paysera\LoggingExtraBundle\Listener\CorrelationIdListener;
use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider;
use Paysera\LoggingExtraBundle\Service\CorrelationIdProvider\CorrelationIdProvider;

class FunctionalCorrelationIdListenerTest extends FunctionalTestCase
{
Expand Down
Loading