Skip to content

Commit a48e65f

Browse files
committed
Fix #4 #6 #7 - Monolog 2 and optimize for Sentry 2.2.1
Also improves the doc
1 parent 68791c3 commit a48e65f

File tree

6 files changed

+226
-225
lines changed

6 files changed

+226
-225
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ composer.lock: composer.json
1515

1616
.PHONY: tests-ci tests
1717
tests-ci: composer-validate phpstan phpunit
18-
tests: tests-ci cs-check
18+
tests: tests-ci cs-check ## Run all tests
1919

2020
.PHONY: composer-validate
2121
composer-validate: vendor composer.json composer.lock ## Validate composer.json file

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ It is a [Monolog](https://github.com/Seldaek/monolog) handler for Sentry PHP SDK
1111
- Send each log record to a [Sentry](https://sentry.io) server
1212
- Send log records as breadcrumbs when they are handled in batch; the main reported log record is the one with the highest log level
1313
- Send log along with exception when one is set in the main log record context
14-
- Workaround for [an issue](https://github.com/getsentry/sentry-php/issues/811) that prevents sending logs in long running process
14+
- Compatible with Monolog 1 and 2
15+
- ~~Workaround for [an issue](https://github.com/getsentry/sentry-php/issues/811) that prevents sending logs in long running process~~
1516

1617
## Requirements
1718

@@ -46,6 +47,15 @@ $logger->error('Bar');
4647

4748
Check out the [handler constructor](src/SentryHandler.php) to know how to control the minimum logging level and bubbling.
4849

50+
>:information_source:
51+
>
52+
>- It is a good idea to combine this handler with a `FingersCrossedHandler` and a `BufferHandler`
53+
>to leverage Sentry breadcrumbs. It gives maximum context for each Sentry event and prevents slowing down http requests.
54+
>- Beware of issue [getsentry/sentry-php#878](https://github.com/getsentry/sentry-php/issues/878) that can be solved by
55+
>using another HTTP client
56+
>
57+
>Check out the symfony guide for a complete example that address all these points
58+
4959
## Documentation
5060

5161
- [Symfony guide](doc/guide-symfony.md): it gives a way to integrate this handler to your app
@@ -57,7 +67,7 @@ Check out the [handler constructor](src/SentryHandler.php) to know how to contro
5767
It is pretty much the same thing but this one captures Monolog records as breadcrumbs
5868
when flushing in batch.
5969

60-
It provides a workaround for [issue 811](https://github.com/getsentry/sentry-php/issues/811) which prevents sending events to Sentry in long running process.
70+
~~It provides a workaround for [issue getsentry/sentry-php#811](https://github.com/getsentry/sentry-php/issues/811) which prevents sending events to Sentry in long running process.~~
6171

6272
Breadcrumbs support has been proposed in a pull request that has been refused for good reasons that
6373
can be checked in the [PR](https://github.com/getsentry/sentry-php/pull/844). Basically the official one aims to be as simple as possible.

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@
2929
},
3030
"require": {
3131
"php": "^7.1.3",
32-
"monolog/monolog": "^1.6",
33-
"sentry/sentry": "^2.1"
32+
"monolog/monolog": "^1.6 || ^2.0",
33+
"sentry/sentry": "^2.2.1"
3434
},
3535
"require-dev": {
36+
"coduo/php-matcher": "^3.2",
3637
"friendsofphp/php-cs-fixer": "^2.15",
3738
"jangregor/phpstan-prophecy": "^0.4.2",
3839
"phpstan/phpstan": "^0.11.13",

doc/guide-symfony.md

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
# Symfony guide
22

3-
>ℹ️
3+
>:information_source:
44
>
55
>- It was written for Symfony 4.
66
>- Its main purpose is to give ideas of how to integrate this handler in a Symfony project
77
88
This guide proposed an opinionated solution to integrate Sentry in a Symfony project.
99
It uses a `FingersCrossedHandler` with a `BufferHandler` to leverage Sentry breadcrumbs
10-
in order to give maximum context for each Sentry event.
10+
in order to give maximum context for each Sentry event. It also prevents slowing down http requests.
1111

1212
It provides the following benefits:
1313

1414
- Flexibility: it's your code at the end so you can customize it to fit your project needs
15+
- Resolve issue [getsentry/sentry-php#878](https://github.com/getsentry/sentry-php/issues/878)
1516
- Log Monolog records of the request lifecycle (Handled message for Messenger) in Sentry breadcrumbs
1617
- Configure Sentry app path and excluded paths (cache and vendor)
1718
- Resolve log PSR placeholders
@@ -41,16 +42,34 @@ It provides the following benefits:
4142

4243
## Step 1: Configure Sentry Hub
4344

45+
Symfony http client is required:
46+
47+
```
48+
composer require symfony/http-client
49+
```
50+
4451
Let's configure the Sentry Hub with a factory. It gives full flexibility in terms of configuration.
4552

4653
```php
4754
<?php
4855

4956
use App\Kernel;
57+
use Http\Client\Exception\NetworkException;
58+
use Http\Client\Exception\RequestException;
59+
use Http\Client\HttpAsyncClient;
60+
use Http\Client\Promise\HttpFulfilledPromise;
61+
use Http\Client\Promise\HttpRejectedPromise;
62+
use Nyholm\Psr7\Factory\Psr17Factory;
63+
use Psr\Http\Client\NetworkExceptionInterface;
64+
use Psr\Http\Client\RequestExceptionInterface;
65+
use Psr\Http\Message\RequestInterface;
5066
use Sentry\ClientBuilder;
5167
use Sentry\Integration\RequestIntegration;
68+
use Sentry\SentrySdk;
5269
use Sentry\State\Hub;
5370
use Sentry\State\HubInterface;
71+
use Symfony\Component\HttpClient\HttpClient;
72+
use Symfony\Component\HttpClient\Psr18Client;
5473

5574
class SentryFactory
5675
{
@@ -69,6 +88,7 @@ class SentryFactory
6988
'prefixes' => [$projectRoot],
7089
'release' => $release,
7190
'default_integrations' => false,
91+
'send_attempts' => 1,
7292
'tags' => [
7393
'php_uname' => \PHP_OS,
7494
'php_sapi_name' => \PHP_SAPI,
@@ -78,14 +98,53 @@ class SentryFactory
7898
],
7999
]);
80100

101+
$clientBuilder->setHttpClient(new SentryHttpClient());
102+
81103
// Enable Sentry RequestIntegration
82104
$options = $clientBuilder->getOptions();
83-
$options->setIntegrations([new RequestIntegration($options)]);
105+
$options->setIntegrations([new RequestIntegration()]);
84106

85107
$client = $clientBuilder->getClient();
86108

87109
// A global HubInterface must be set otherwise some feature provided by the SDK does not work as they rely on this global state
88-
return Hub::setCurrent(new Hub($client));
110+
return SentrySdk::setCurrentHub(new Hub($client));
111+
}
112+
}
113+
114+
/**
115+
* @see https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/HttpClient/HttplugClient.php
116+
*
117+
* Beware of the following issue with the default HTTP that comes with the Sentry SDK
118+
* @see https://github.com/getsentry/sentry-php/issues/878
119+
*/
120+
class SentryHttpClient implements HttpAsyncClient
121+
{
122+
private $client;
123+
124+
public function __construct()
125+
{
126+
$factory = new Psr17Factory();
127+
128+
// DO NOT use the http client from Symfony or you could turn into an infinite loop depending on your monolog config and the if logging of HTTP request is enabled or not
129+
$client = HttpClient::create(['timeout' => 2]);
130+
131+
$this->client = new Psr18Client($client, $factory, $factory);
132+
}
133+
134+
/**
135+
* {@inheritdoc}
136+
*/
137+
public function sendAsyncRequest(RequestInterface $request)
138+
{
139+
try {
140+
$response = $this->client->sendRequest($request);
141+
} catch (RequestExceptionInterface $e) {
142+
return new HttpRejectedPromise(new RequestException($e->getMessage(), $request, $e));
143+
} catch (NetworkExceptionInterface $e) {
144+
return new HttpRejectedPromise(new NetworkException($e->getMessage(), $request, $e));
145+
}
146+
147+
return new HttpFulfilledPromise($response);
89148
}
90149
}
91150
```

src/SentryHandler.php

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88
use Monolog\Handler\AbstractProcessingHandler;
99
use Monolog\Logger;
1010
use Sentry\Breadcrumb;
11-
use Sentry\Client;
1211
use Sentry\Severity;
1312
use Sentry\State\HubInterface;
1413
use Sentry\State\Scope;
15-
use Sentry\Transport\HttpTransport;
1614

1715
class SentryHandler extends AbstractProcessingHandler
1816
{
@@ -103,36 +101,6 @@ protected function write(array $record): void
103101

104102
$this->hub->captureEvent($payload);
105103
});
106-
107-
$this->flushSentryEvents();
108-
}
109-
110-
/**
111-
* Block until all async events are processed for the HTTP transport.
112-
*
113-
* @see https://github.com/getsentry/sentry-php/issues/811
114-
*/
115-
private function flushSentryEvents(): void
116-
{
117-
// Inspired by https://github.com/getsentry/sentry-laravel/blob/14e8bf07f4254f031db3e88096ed8a8959aa34c1/src/Sentry/Laravel/Integration.php#L94-L112
118-
$client = $this->hub->getClient();
119-
120-
if (!$client instanceof Client) {
121-
return;
122-
}
123-
124-
$transportProperty = new \ReflectionProperty(Client::class, 'transport');
125-
$transportProperty->setAccessible(true);
126-
127-
$transport = $transportProperty->getValue($client);
128-
129-
if ($transport instanceof HttpTransport) {
130-
\Closure::bind(
131-
function () {$this->cleanupPendingRequests(); },
132-
$transport,
133-
$transport
134-
)();
135-
}
136104
}
137105

138106
/**

0 commit comments

Comments
 (0)