Skip to content
Merged
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
50 changes: 25 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,66 @@
# 🪢 Telemetry
# 🪢 Request Tracker

![the dragon code telemetry](https://preview.dragon-code.pro/the%20dragon%20code/telemetry.svg?brand=symfony&mode=auto)
![the dragon code request-tracker](https://preview.dragon-code.pro/the%20dragon%20code/request-tracker.svg?brand=symfony&mode=auto)

[![Stable Version][badge_stable]][link_packagist]
[![Total Downloads][badge_downloads]][link_packagist]
[![License][badge_license]][link_license]

End-to-end telemetry of inter-service communication.
Request tracking across microservices.

## Installation

You can install the **Telemetry** package via [Composer](https://getcomposer.org):
You can install the **Request Tracker** package via [Composer](https://getcomposer.org):

```Bash
composer require dragon-code/telemetry
composer require dragon-code/request-tracker
```

## Basic Usage

### Using Default Header Names

```php
use DragonCode\Telemetry\TelemetryHeader;
use DragonCode\Telemetry\TelemetryRequest;
use DragonCode\RequestTracker\TrackerHeader;
use DragonCode\RequestTracker\TrackerRequest;
use Symfony\Component\HttpFoundation\Request;

/** @var Request $request */
$request = /* ... */;

$telemetry = new TelemetryRequest($request, new TelemetryHeader);
$tracker = new TrackerRequest($request, new TrackerHeader);

function telemetry(Request $request, ?int $userId = null): Request
function tracker(Request $request, ?int $userId = null): Request
{
return (new TelemetryRequest($request, new TelemetryHeader))
return (new TrackerRequest($request, new TrackerHeader))
->userId($userId)
->ip()
->traceId()
->getRequest();
}

// For the first call
telemetry($request, $user->id);
tracker($request, $user->id);

// For subsequent services
telemetry($request);
tracker($request);
```

### Custom Headers

```php
use DragonCode\Telemetry\TelemetryHeader;
use DragonCode\Telemetry\TelemetryRequest;
use DragonCode\RequestTracker\TrackerHeader;
use DragonCode\RequestTracker\TrackerRequest;
use Symfony\Component\HttpFoundation\Request;

/** @var Request $request */
$request = /* ... */;

$telemetry = new TelemetryRequest($request, new TelemetryHeader);
$tracker = new TrackerRequest($request, new TrackerHeader);

function telemetry(Request $request, ?int $userId = null): Request
function tracker(Request $request, ?int $userId = null): Request
{
return (new TelemetryRequest($request, new TelemetryHeader))
return (new TrackerRequest($request, new TrackerHeader))
->userId($userId)
->ip()
->traceId()
Expand All @@ -70,25 +70,25 @@ function telemetry(Request $request, ?int $userId = null): Request
```

```php
$item = telemetry($request);
$item = tracker($request);

return $item->headers->get('Some-Header'); // 1234
```

```php
$request->headers->set('Some-Header', 'qwerty');

$item = telemetry($request);
$item = tracker($request);

return $item->headers->get('Some-Header'); // qwerty
```

### Custom Header Names

```php
use DragonCode\Telemetry\TelemetryHeader;
use DragonCode\RequestTracker\TrackerHeader;

return new TelemetryHeader(
return new TrackerHeader(
userId: 'Some-User-Id',
ip: 'Some-IP',
traceId: 'Some-Trace-Id',
Expand All @@ -100,12 +100,12 @@ return new TelemetryHeader(
This package is licensed under the [MIT License](LICENSE).


[badge_downloads]: https://img.shields.io/packagist/dt/dragon-code/telemetry.svg?style=flat-square
[badge_downloads]: https://img.shields.io/packagist/dt/dragon-code/request-tracker.svg?style=flat-square

[badge_license]: https://img.shields.io/packagist/l/dragon-code/telemetry.svg?style=flat-square
[badge_license]: https://img.shields.io/packagist/l/dragon-code/request-tracker.svg?style=flat-square

[badge_stable]: https://img.shields.io/github/v/release/TheDragonCode/telemetry?label=packagist&style=flat-square
[badge_stable]: https://img.shields.io/github/v/release/TheDragonCode/request-tracker?label=packagist&style=flat-square

[link_license]: LICENSE

[link_packagist]: https://packagist.org/packages/dragon-code/telemetry
[link_packagist]: https://packagist.org/packages/dragon-code/request-tracker
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dragon-code/telemetry",
"description": "End-to-end telemetry of inter-service communication",
"name": "dragon-code/request-tracker",
"description": "Request tracking across microservices",
"license": "MIT",
"type": "library",
"authors": [
Expand All @@ -23,7 +23,7 @@
"minimum-stability": "stable",
"autoload": {
"psr-4": {
"DragonCode\\Telemetry\\": "src/"
"DragonCode\\RequestTracker\\": "src/"
}
},
"autoload-dev": {
Expand Down
14 changes: 0 additions & 14 deletions src/TelemetryHeader.php

This file was deleted.

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

declare(strict_types=1);

namespace DragonCode\RequestTracker;

readonly class TrackerHeader
{
public function __construct(
public string $userId = 'X-Tracker-User-Id',
public string $ip = 'X-Tracker-Ip',
public string $traceId = 'X-Tracker-Trace-Id',
) {}
}
6 changes: 3 additions & 3 deletions src/TelemetryRequest.php → src/TrackerRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

declare(strict_types=1);

namespace DragonCode\Telemetry;
namespace DragonCode\RequestTracker;

use Closure;
use Ramsey\Uuid\UuidFactory;
use Symfony\Component\HttpFoundation\Request;

use function is_int;

class TelemetryRequest
class TrackerRequest
{
public function __construct(
protected Request $request,
protected TelemetryHeader $header,
protected TrackerHeader $header,
) {}

public function userId(int|string|null $id): static
Expand Down
12 changes: 6 additions & 6 deletions tests/Unit/HeaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

declare(strict_types=1);

use DragonCode\Telemetry\TelemetryHeader;
use DragonCode\RequestTracker\TrackerHeader;

it('uses default header names', function () {
$header = new TelemetryHeader;
$header = new TrackerHeader;

expect($header->userId)->toBe('X-Telemetry-User-Id')
->and($header->ip)->toBe('X-Telemetry-Ip')
->and($header->traceId)->toBe('X-Telemetry-Trace-Id');
expect($header->userId)->toBe('X-Tracker-User-Id')
->and($header->ip)->toBe('X-Tracker-Ip')
->and($header->traceId)->toBe('X-Tracker-Trace-Id');
});

it('accepts custom header names', function () {
$header = new TelemetryHeader(
$header = new TrackerHeader(
userId : 'Some-User-Id',
ip : 'Some-IP',
traceId: 'Some-Trace-Id',
Expand Down
12 changes: 6 additions & 6 deletions tests/Unit/Request/CustomTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

declare(strict_types=1);

use DragonCode\Telemetry\TelemetryHeader;
use DragonCode\Telemetry\TelemetryRequest;
use DragonCode\RequestTracker\TrackerHeader;
use DragonCode\RequestTracker\TrackerRequest;
use Symfony\Component\HttpFoundation\Request;

it('sets header from callback when header is absent and casts ints to strings', function () {
$headerName = 'Some-Header';

$request = makeRequest();
$header = new TelemetryHeader;
$telemetry = new TelemetryRequest($request, $header);
$header = new TrackerHeader;
$telemetry = new TrackerRequest($request, $header);

$telemetry->custom($headerName, function (Request $req) {
expect($req)->toBeInstanceOf(Request::class);
Expand All @@ -28,8 +28,8 @@
$request = makeRequest([$headerName => 'qwerty']);

$called = false;
$header = new TelemetryHeader;
$telemetry = new TelemetryRequest($request, $header);
$header = new TrackerHeader;
$telemetry = new TrackerRequest($request, $header);

$telemetry->custom($headerName, function () use (&$called) {
$called = true; // must remain false if existing header is used
Expand Down
8 changes: 4 additions & 4 deletions tests/Unit/Request/GetRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

declare(strict_types=1);

use DragonCode\Telemetry\TelemetryHeader;
use DragonCode\Telemetry\TelemetryRequest;
use DragonCode\RequestTracker\TrackerHeader;
use DragonCode\RequestTracker\TrackerRequest;

it('returns the same Request instance via getRequest()', function () {
$header = new TelemetryHeader;
$header = new TrackerHeader;
$request = makeRequest();
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);

expect($telemetry->getRequest())->toBe($request);
});
12 changes: 6 additions & 6 deletions tests/Unit/Request/IpTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@

declare(strict_types=1);

use DragonCode\Telemetry\TelemetryHeader;
use DragonCode\Telemetry\TelemetryRequest;
use DragonCode\RequestTracker\TrackerHeader;
use DragonCode\RequestTracker\TrackerRequest;

it('gets ip with correct precedence and sets header via ip()', function () {
$header = new TelemetryHeader;
$header = new TrackerHeader;

// 1) If telemetry header exists, it wins
$request = makeRequest([$header->ip => '203.0.113.10']);
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);
expect($telemetry->getIp())->toBe('203.0.113.10');

// 2) Else HTTP_X_REAL_IP (non-standard header name checked by the class)
$request = makeRequest();
$request->headers->set('HTTP_X_REAL_IP', '198.51.100.20');
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);
expect($telemetry->getIp())->toBe('198.51.100.20');

// 3) Else client ip (REMOTE_ADDR)
$request = makeRequest([], ['REMOTE_ADDR' => '192.0.2.30']);
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);
expect($telemetry->getIp())->toBe('192.0.2.30');

// 4) ip() without argument sets header from getIp()
Expand Down
10 changes: 5 additions & 5 deletions tests/Unit/Request/TrackIdTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

declare(strict_types=1);

use DragonCode\Telemetry\TelemetryHeader;
use DragonCode\Telemetry\TelemetryRequest;
use DragonCode\RequestTracker\TrackerHeader;
use DragonCode\RequestTracker\TrackerRequest;
use Ramsey\Uuid\Uuid;

it('gets or sets trace id, generating a UUID v4 when absent', function () {
$header = new TelemetryHeader;
$header = new TrackerHeader;

// 1) If header exists, return it
$request = makeRequest([$header->traceId => 'trace-123']);
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);
expect($telemetry->getTraceId())->toBe('trace-123');

// 2) When absent, generate UUID v4
$request = makeRequest();
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);
$generated = $telemetry->getTraceId();
expect(Uuid::isValid($generated))->toBeTrue()
->and(Uuid::fromString($generated)->getVersion())->toBe(4);
Expand Down
14 changes: 7 additions & 7 deletions tests/Unit/Request/UserIdTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,34 @@

declare(strict_types=1);

use DragonCode\Telemetry\TelemetryHeader;
use DragonCode\Telemetry\TelemetryRequest;
use DragonCode\RequestTracker\TrackerHeader;
use DragonCode\RequestTracker\TrackerRequest;

it('sets and gets user id with string and int, falling back to existing header or 0', function () {
$header = new TelemetryHeader;
$header = new TrackerHeader;

// 1) Explicit string
$request = makeRequest();
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);
$telemetry->userId('42');
expect($request->headers->get($header->userId))->toBe('42')
->and($telemetry->getUserId())->toBe('42');

// 2) Explicit int should be cast to string
$request = makeRequest();
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);
$telemetry->userId(7);
expect($request->headers->get($header->userId))->toBe('7');

// 3) Fallback to existing header when null
$request = makeRequest([$header->userId => '555']);
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);
$telemetry->userId(null);
expect($request->headers->get($header->userId))->toBe('555')
->and($telemetry->getUserId())->toBe('555');

// 4) getUserId() returns '0' when nothing present
$request = makeRequest();
$telemetry = new TelemetryRequest($request, $header);
$telemetry = new TrackerRequest($request, $header);
expect($telemetry->getUserId())->toBe('0');
});