Skip to content

Commit

Permalink
Added ability to filter incoming requests (#45)
Browse files Browse the repository at this point in the history
* [BUGFIX] #36

Changed `getCallerClass`, on Backtracer trait to return null if the backtrace is empty.
Changed DatebaseQueryCollector and CacheCollector to add '(too deeply nested)' to their respective segment name, if the caller name is deeper than 50 calls in the backtrace.

Added tests for all affected classes.

* [BUGFIX] #36

Applied suggested diff.

* [BUGFIX] #44

WIP: working on adding support to filter request to capture.

* [BUGFIX] #44

Finished adding support to filter requests.

* [BUGFIX] #44

Added new filtering option to the doc.
Added `addRequestFilterCallback` as an available function on the Xray facade.

* [BUGFIX] #44

Changed XrayServiceProvider to check request in the `boot` function rather than the `registerCollectors`.

* Applied suggested changes.

---------

Co-authored-by: Nicolas D'Amours <[email protected]>
  • Loading branch information
nicDamours and Nicolas D'Amours authored Dec 27, 2023
1 parent c7c6c8f commit 2e7176b
Show file tree
Hide file tree
Showing 6 changed files with 378 additions and 0 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,35 @@ The above results in:

![XML-example](https://raw.githubusercontent.com/Napp/xray-laravel/master/docs/xray-xml-example.png)

## Request filtering
There might be some requests you wish not to track in Amazon X-Ray. In order to filter out those requests, you can call the `addRequestFilterCallback` function, on the `Xray` facade.
This function takes a callback as a parameter. The callback takes a `Symfony\Component\HttpFoundation\Request` as a parameter and is expected to return a boolean.

Please note that this function call needs to be done in the `register` function of your `AppServiceProvider`. You should, also, keep the checks relatively simple, since most of the application service providers won't be booted when calling the filtering callbacks.

When LaravelXray is booting, the request is checked against each registered callbacks. If none returns false, the request is captured. Otherwise, it is not.

```php
use Symfony\Component\HttpFoundation\Request;
use Napp\Xray\Facades\Xray;

class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
Xray::addRequestFilterCallback(function(Request $request) {
/* some conditions */
return true;
});
}
}
```

## Daemon support

The X-Ray daemon is automatically run in a Lambda environment. Use this over the default `Napp\Xray\Submission\APISegmentSubmitter` to relay requests to Amazon X-Ray.
Expand Down
1 change: 1 addition & 0 deletions src/Facades/Xray.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* @method static void initCliTracer(string $name)
* @method static void submitHttpTracer($response)
* @method static void submitCliTracer()
* @method static void addRequestFilterCallback(callable $callback)
*
* @see \Napp\Xray\Xray
*/
Expand Down
19 changes: 19 additions & 0 deletions src/Xray.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ class Xray
{
private $collector;

private $requestFilterCallables;

public function __construct(SegmentCollector $collector)
{
$this->collector = $collector;
$this->requestFilterCallables = [];
}

public function tracer(): Trace
Expand Down Expand Up @@ -82,4 +85,20 @@ public function submitCliTracer(): void
{
$this->collector->submitCliTracer();
}

public function addRequestFilterCallback(callable $callback): void
{
$this->requestFilterCallables[] = $callback;
}

public function shouldCaptureRequest(Request $request): bool
{
foreach ($this->requestFilterCallables as $requestFilterCallable) {
if (! $requestFilterCallable($request)) {
return false;
}
}

return true;
}
}
6 changes: 6 additions & 0 deletions src/XrayServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public function boot()
return;
}

$xraySingleton = $this->app->make('xray');

if (! $this->app->runningInConsole() && ! $xraySingleton->shouldCaptureRequest($this->app->make('request'))) {
return;
}

$this->registerCollectors();
}

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

namespace Napp\Xray\Tests\Facades;

use Napp\Xray\Collectors\SegmentCollector;
use Napp\Xray\Xray;
use Symfony\Component\HttpFoundation\Request;
use PHPUnit\Framework\TestCase;

class XrayTest extends TestCase
{
public function test_it_should_register_request_filtering_callback() {
// given an Xray object
/** @var Xray $xrayObject */
$xrayObject = new Xray(new SegmentCollector());

// and a mock of a callback to check if the current request should be captured.
$callbackCalled = false;
$requestCapturingCallbackMock = function(Request $request) use (&$callbackCalled) {
$callbackCalled = true;
};

// and the callback has been registered on the facade as a filter callback
$xrayObject->addRequestFilterCallback($requestCapturingCallbackMock);

// when checking, on the facade, if the request should be captured
$request = new Request();
$xrayObject->shouldCaptureRequest($request);

// then the callback should have been called
$this->assertTrue($callbackCalled);
}

public function test_it_should_return_true_when_no_request_filter_callable_returns_false() {
// given a xray object
/** @var Xray $xrayObject */
$xrayObject = new Xray(new SegmentCollector());

// and some request filter callback that return true
$givenRequestCallbackCount = 3;
for($i = 0; $i < $givenRequestCallbackCount; $i ++) {
$xrayObject->addRequestFilterCallback(function() {
return true;
});
}

// when checking, on the facade, if the request should be captured
$request = new Request();
$result = $xrayObject->shouldCaptureRequest($request);

// then it should return true
$this->assertTrue($result);
}

public function test_it_should_return_false_if_at_least_one_request_filter_callback_returns_false() {
// given a xray object
/** @var Xray $xrayObject */
$xrayObject = new Xray(new SegmentCollector());

// and some request filter callback that return true
$givenRequestCallbackCount = 3;
for($i = 0; $i < $givenRequestCallbackCount; $i ++) {
$xrayObject->addRequestFilterCallback(function() {
return true;
});
}

// and one request filter callback that returns false
$xrayObject->addRequestFilterCallback(function() {
return false;
});

// when checking, on the facade, if the request should be captured
$request = new Request();
$result = $xrayObject->shouldCaptureRequest($request);

// then it should return false
$this->assertFalse($result);
}

public function test_it_should_return_true_if_no_request_filtering_callback_are_defined() {
// given a xray object
/** @var Xray $xrayObject */
$xrayObject = new Xray(new SegmentCollector());

// and no request filtering callback defined

// when checking, on the facade, if the request should be captured
$request = new Request();
$result = $xrayObject->shouldCaptureRequest($request);

// then it should return true
$this->assertTrue($result);
}
}
Loading

0 comments on commit 2e7176b

Please sign in to comment.