Skip to content
This repository was archived by the owner on Nov 3, 2020. It is now read-only.

Commit f42f55c

Browse files
authored
Merge pull request #33 from nipwaayoni/7.1-dev
7.1 dev
2 parents 316f2f4 + e39bce5 commit f42f55c

File tree

9 files changed

+142
-99
lines changed

9 files changed

+142
-99
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ All additional events will be descendents of this transaction.
7777
There is currently no provision to manage additional Transaction events, or to handle a non-HTTP Request
7878
based process. We hope to address those issues in a future release.
7979

80+
#### Customizing Transaction Context
81+
82+
While the `RecordTransaction` middleware sets supported APM contexts when the transaction is created, you may wish to customize the contexts based on the completed request/response. This can be done by extending the `RecordTransaction` middleware and overriding context methods as described in the [customizing transactions](docs/customizing_transactions.md) documentation.
83+
84+
#### Excluding Requests
85+
86+
You can exclude requests from being sent to APM by using an `except` list of URI patterns. For example, you may wish to avoid sending requests for the (DebugBar)[https://github.com/barryvdh/laravel-debugbar] resources during development or testing.
87+
88+
Add the desired URI patterns to the `except` key in the `elastic-apm` configuration. Note that you must publish the file as described in the [configuration docs](docs/configuration.md).
89+
90+
If you have extended the `RecordTransaction` middleware, as described in the [customizing transactions](docs/customizing_transactions.md) documentation, you may set the `except` list class member there.
91+
8092
### Span Events
8193

8294
Spans occur within a Transaction. Spans represent events within the Transaction. Queries made through Laravel's
@@ -102,7 +114,7 @@ Refer to the [configuration docs](docs/configuration.md) for more information.
102114

103115
### HTTP Client Customization
104116

105-
It is no longer possible to provide HTTP client options through the APM PHP Agent configuration. If you need to customize the HTTP client, you must implement and configure a suitable client object and properly register it with the Laravel service container. See the "HTTP Client Configuation" section of the [configuration docs](docs/configuration.md).
117+
It is no longer possible to provide HTTP client options through the APM PHP Agent configuration. If you need to customize the HTTP client, you must implement and configure a suitable client object and provide it to the `AgentBuilder`. See the "HTTP Client Configuration" section of the [configuration docs](docs/configuration.md).
106118

107119
## Laravel Test Setup
108120

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"license": "MIT",
1313
"require": {
1414
"php": ">= 7.2",
15-
"nipwaayoni/elastic-apm-php-agent": "^7.1",
15+
"nipwaayoni/elastic-apm-php-agent": "^7.2",
1616
"ramsey/uuid": ">= 3.0 < 5.0"
1717
},
1818
"require-dev": {

docs/breaking_changes.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Breaking Changes
22

3-
## 7.x
3+
## 7.1.0
4+
5+
1. Removed the ability to bind "ElasticApm*" objects into the container to set on the AgentBuilder. Configuring the AgentBuilder is now done by binding an implementation into the container which the ElasticApmServiceProvider will resolve and use.
6+
7+
## 7.0.0
48

59
1. The [Elastic APM PHP Agent](https://github.com/nipwaayoni/elastic-apm-php-agent) minimum version has been updated.
610
2. It is no longer recommended to call `Agent::send()` in `Exceptions\Handler::report()`. This will likely result in duplicate reporting of exceptions.

docs/configuration.md

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,69 @@ php artisan vendor:publish --tag=config
2424

2525
Once published, open the `config/elastic-apm.php` file and review the various settings.
2626

27-
## HTTP Client Configuration
27+
## Customizing Agent Creation
2828

29-
If you need to customize the HTTP client, you must create a [PSR-18](https://www.php-fig.org/psr/psr-18/) compatible implementation and bind it in the Laravel service container. For now, we will use a GuzzleHttp adapter from the PHP-HTTP project.
29+
The `Agent` object used to manage APM events is created using the provided `AgentBuilder` class. You can control many aspects of the `Agent` creation by binding your own implementation into the service container. Some helpful examples are shown below. For more details, refer to the [Elastic APM PHP Agent](https://github.com/nipwaayoni/elastic-apm-php-agent/blob/master/docs/agent.md) documentation.
30+
31+
### Binding an AgentBuilder
32+
33+
Bind your implementation as a singleton in the service container:
34+
35+
```php
36+
$this->app->bind(AgentBuilder::class, function () {
37+
$builder = new AgentBuilder();
38+
39+
// configure the builder
40+
41+
return $builder;
42+
});
43+
```
44+
45+
Note that the `ElasticApmServiceProvider` will always call the `AgentBuilder::withConfig()` and `AgentBuilder::withEnvData()` methods. You must use the provided `elastic-apm` configuration options to influence those settings.
46+
47+
### HTTP Client Configuration
48+
49+
If you need to customize the HTTP client, you must create a [PSR-18](https://www.php-fig.org/psr/psr-18/) compatible implementation and provide it to the `AgentBuilder. For now, we will use a GuzzleHttp adapter from the PHP-HTTP project.
3050

3151
```bash
3252
composer require http-interop/http-factory-guzzle php-http/guzzle6-adapter
3353
```
3454

35-
The following example demonstrates how to create a GuzzleHttp client that will not verify server certificates. Once you create the client, bind it in the service container under the `ElasticApmHttpClient` abstract.
55+
The following example demonstrates how to create a GuzzleHttp client that will not verify server certificates.
3656

3757
```php
38-
$this->app->bind('ElasticApmHttpClient', function () {
39-
// Create the configured client
58+
$this->app->bind(AgentBuilder::class, function () {
59+
$builder = new AgentBuilder();
60+
4061
$client = new \GuzzleHttp\Client([
4162
'verify' => false,
4263
// other client options
4364
]);
4465

4566
// Wrap the client object in the adapter and return it
46-
return new \Http\Adapter\Guzzle6\Client($client);
47-
});
67+
$builder->withHttpClient(new \Http\Adapter\Guzzle6\Client($client));
4868

69+
return $builder;
70+
});
4971
```
5072

51-
If the service container has a binding for `ElasticApmHttpClient`, the concrete implementation will be retrieved and passed into the `Agent`.
73+
### APM Transaction Hooks
74+
75+
You can hook the APM HTTP request/response process to examine the data to be sent to APM and the response after sending. This may be helpful in troubleshooting issues.
76+
77+
```php
78+
$this->app->bind(AgentBuilder::class, function () {
79+
$builder = new AgentBuilder();
80+
81+
$builder->withPreCommitCallback(function (RequestInterface $request) {
82+
Log::info(sprintf('Pre commit url is: %s', $request->getUri()));
83+
});
84+
85+
$builder->withPostCommitCallback(function (ResponseInterface $response) {
86+
Log::info(sprintf('Post commit response status: %s', $response->getStatusCode()));
87+
Log::debug($response->getBody()->getContents());
88+
});
89+
90+
return $builder;
91+
});
92+
```

src/Exceptions/ApmAgent.php

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/Middleware/RecordTransaction.php

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class RecordTransaction
1717
* @var Agent
1818
*/
1919
protected $agent;
20+
21+
protected $except = [];
22+
2023
/**
2124
* @var Timer
2225
*/
@@ -37,6 +40,7 @@ public static function getTransactionName()
3740
/**
3841
* RecordTransaction constructor.
3942
* @param Agent $agent
43+
* @param Timer $timer
4044
*/
4145
public function __construct(Agent $agent, Timer $timer)
4246
{
@@ -52,6 +56,10 @@ public function __construct(Agent $agent, Timer $timer)
5256
*/
5357
final public function handle($request, Closure $next)
5458
{
59+
if ($this->inExceptArray($request)) {
60+
return $next($request);
61+
}
62+
5563
self::setTransactionName($this->getTransactionNameFromRequest($request));
5664
$transaction = $this->agent->startTransaction(
5765
self::getTransactionName()
@@ -85,28 +93,9 @@ final public function handle($request, Closure $next)
8593

8694
$transaction->stop($this->timer->getElapsedInMilliseconds());
8795

88-
$this->agent->send();
89-
9096
return $response;
9197
}
9298

93-
/**
94-
* Perform any final actions for the request lifecycle.
95-
*
96-
* @param \Illuminate\Http\Request $request
97-
* @param \Symfony\Component\HttpFoundation\Response $response
98-
*
99-
* @return void
100-
*/
101-
public function terminate($request, $response)
102-
{
103-
try {
104-
$this->agent->send();
105-
} catch (\Throwable $t) {
106-
Log::error($t);
107-
}
108-
}
109-
11099
protected function response(Response $response): array
111100
{
112101
return [
@@ -192,4 +181,31 @@ private function makeTransactionName(string $method, string $path): string
192181

193182
return sprintf("%s %s", $method, $path);
194183
}
184+
185+
/**
186+
* Determine if the request has a URI that should be recorded.
187+
* From \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken
188+
*
189+
* @param \Illuminate\Http\Request $request
190+
* @return bool
191+
*/
192+
protected function inExceptArray($request)
193+
{
194+
$exceptList = array_merge(
195+
$this->except,
196+
config('elastic-apm.except', [])
197+
);
198+
199+
foreach ($exceptList as $except) {
200+
if ($except !== '/') {
201+
$except = trim($except, '/');
202+
}
203+
204+
if ($request->fullUrlIs($except) || $request->is($except)) {
205+
return true;
206+
}
207+
}
208+
209+
return false;
210+
}
195211
}

src/Providers/ElasticApmServiceProvider.php

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
namespace Nipwaayoni\ElasticApmLaravel\Providers;
44

55
use Illuminate\Database\Events\QueryExecuted;
6+
use Illuminate\Http\Request;
7+
use Illuminate\Http\Response;
68
use Illuminate\Support\Collection;
9+
use Illuminate\Support\Facades\App;
710
use Illuminate\Support\ServiceProvider;
811
use Illuminate\Support\Str;
912
use Illuminate\Support\Arr;
@@ -51,16 +54,16 @@ public function register()
5154
'elastic-apm'
5255
);
5356

54-
$this->app->singleton(Agent::class, function ($app) {
55-
$container = resolve(ContainerInterface::class);
57+
$this->app->singleton(Agent::class, function () {
58+
$builder = resolve(AgentBuilder::class);
5659

57-
$builder = new AgentBuilder();
5860
$builder->withConfig(new Config(
5961
array_merge(
6062
[
6163
'active' => config('elastic-apm.active'),
6264
'framework' => 'Laravel',
6365
'frameworkVersion' => app()->version(),
66+
'environment' => config('elastic-apm.environment'),
6467
],
6568
$this->getAppConfig(),
6669
config('elastic-apm.server')
@@ -69,26 +72,6 @@ public function register()
6972

7073
$builder->withEnvData(config('elastic-apm.env'));
7174

72-
if ($container->has('ElasticApmEventFactory')) {
73-
$builder->withEventFactory($container->get('ElasticApmEventFactory'));
74-
}
75-
76-
if ($container->has('ElasticApmTransactionStore')) {
77-
$builder->withTransactionStore($container->get('ElasticApmTransactionStore'));
78-
}
79-
80-
if ($container->has('ElasticApmHttpClient')) {
81-
$builder->withHttpClient($container->get('ElasticApmHttpClient'));
82-
}
83-
84-
if ($container->has('ElasticApmRequestFactory')) {
85-
$builder->withRequestFactory($container->get('ElasticApmRequestFactory'));
86-
}
87-
88-
if ($container->has('ElasticApmStreamFactory')) {
89-
$builder->withStreamFactory($container->get('ElasticApmStreamFactory'));
90-
}
91-
9275
return $builder->build();
9376
});
9477

@@ -98,6 +81,14 @@ public function register()
9881
$this->app->alias(Agent::class, 'elastic-apm');
9982

10083
$this->app->instance('query-log', new Collection());
84+
85+
// Register a callback on terminating to send the events
86+
$this->app->terminating(function (Request $request, Response $response) {
87+
/** @var Agent $agent */
88+
$agent = resolve(Agent::class);
89+
90+
$agent->send();
91+
});
10192
}
10293

10394
/**

src/config/elastic-apm.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
'appVersion' => env('APM_APPVERSION', ''),
1414
],
1515

16+
// list of URIs not to record as transactions
17+
'except' => [],
18+
1619
// list allowed environment variables OR empty array to send everything
1720
'env' => ['DOCUMENT_ROOT', 'REMOTE_ADDR'],
1821

tests/Middleware/RecordTransactionTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,28 @@ public function testSetsTransactionNameToRouteUriLogsErrorWhenWithoutCurrentRout
120120
$recorder->handle($request, $next);
121121
}
122122

123+
protected function useExceptList($app)
124+
{
125+
$app->config->set('elastic-apm.except', ['/except/uri']);
126+
}
127+
128+
/**
129+
* @environment-setup useExceptList
130+
*/
131+
public function testDoesNotStartTransactionForUriInExceptList(): void
132+
{
133+
$this->agent->expects($this->never())->method('startTransaction');
134+
135+
$request = $this->makeRequest('GET', ['server' => ['REQUEST_URI' => '/except/uri']]);
136+
$next = function () {
137+
return new Response();
138+
};
139+
140+
$recorder = new RecordTransaction($this->agent, $this->timer);
141+
142+
$recorder->handle($request, $next);
143+
}
144+
123145
protected function makeRequest(string $method = 'GET', array $options = []): Request
124146
{
125147
$query = $options['query'] ?? [];

0 commit comments

Comments
 (0)