Skip to content

Commit 835166e

Browse files
authored
feat: improved networking and Queue mode (#118)
1 parent 4e33837 commit 835166e

File tree

6 files changed

+512
-59
lines changed

6 files changed

+512
-59
lines changed

README.md

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,38 @@ TREBLLE_DEBUG_MODE=true
253253

254254
**Warning:** Only enable in development. Never use in production.
255255

256-
#### 7. Custom API URL (Testing/Development)
256+
#### 7. Queue-Based Data Transmission
257+
258+
For optimal performance, enable asynchronous data transmission using Laravel queues. This completely removes any blocking network requests from your application's request/response cycle.
259+
260+
261+
**Configuration:**
262+
263+
```shell
264+
# .env
265+
TREBLLE_QUEUE_ENABLED=true
266+
TREBLLE_QUEUE_CONNECTION=redis # Use your queue connection (redis, sqs, beanstalkd)
267+
TREBLLE_QUEUE_NAME=default # Queue name (optional)
268+
```
269+
270+
```php
271+
// config/treblle.php
272+
'queue' => [
273+
'enabled' => env('TREBLLE_QUEUE_ENABLED', false),
274+
'connection' => env('TREBLLE_QUEUE_CONNECTION', null), // null = default connection
275+
'queue' => env('TREBLLE_QUEUE_NAME', 'default'),
276+
],
277+
```
278+
279+
**Supported Queue Connections:**
280+
-**redis** - Recommended for most applications
281+
-**sqs** - AWS SQS for cloud deployments
282+
-**beanstalkd** - Fast work queue
283+
-**database** - When properly indexed
284+
-**sync** - Not recommended (defeats the purpose)
285+
-**file** - Not recommended (slow and unreliable)
286+
287+
#### 8. Custom API URL (Testing/Development)
257288

258289
Override the Treblle API endpoint for testing:
259290

@@ -308,6 +339,13 @@ return [
308339
'*-secret-*',
309340
],
310341

342+
// Queue configuration (recommended for production)
343+
'queue' => [
344+
'enabled' => env('TREBLLE_QUEUE_ENABLED', false),
345+
'connection' => env('TREBLLE_QUEUE_CONNECTION', null),
346+
'queue' => env('TREBLLE_QUEUE_NAME', 'default'),
347+
],
348+
311349
// Debug mode (development only)
312350
'debug' => env('TREBBLE_DEBUG_MODE', false),
313351
];
@@ -320,11 +358,16 @@ return [
320358
TREBLLE_API_KEY=your_api_key_here
321359
TREBLLE_SDK_TOKEN=your_sdk_token_here
322360

323-
# Optional
361+
# Optional - Core Settings
324362
TREBLLE_ENABLE=true
325363
TREBLLE_IGNORED_ENV=local,testing,development
326364
TREBLLE_DEBUG_MODE=false
327365
TREBLLE_URL=null
366+
367+
# Optional - Queue Configuration (Recommended for Production)
368+
TREBLLE_QUEUE_ENABLED=false
369+
TREBLLE_QUEUE_CONNECTION=redis
370+
TREBLLE_QUEUE_NAME=default
328371
```
329372

330373
## Usage

config/treblle.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,33 @@
5656
* Enable Debug mode, will throw errors on apis.
5757
*/
5858
'debug' => env('TREBLLE_DEBUG_MODE', false),
59+
60+
/*
61+
* Queue Configuration
62+
*
63+
* Enable asynchronous data transmission using Laravel queues.
64+
* When enabled, Treblle data will be sent via jobs instead of synchronously.
65+
*
66+
* Supported connections: redis, sqs, beanstalkd, database (with proper indexes)
67+
* Not recommended: sync, file (slow and unreliable)
68+
*/
69+
'queue' => [
70+
/*
71+
* Enable queue-based data transmission
72+
*/
73+
'enabled' => env('TREBLLE_QUEUE_ENABLED', false),
74+
75+
/*
76+
* Queue connection to use (must be configured in config/queue.php)
77+
* If null, uses the default queue connection
78+
* Recommended: redis, sqs, beanstalkd
79+
*/
80+
'connection' => env('TREBLLE_QUEUE_CONNECTION', 'redis'),
81+
82+
/*
83+
* Queue name to dispatch jobs to
84+
* If null, uses the default queue for the connection
85+
*/
86+
'queue' => env('TREBLLE_QUEUE_NAME', 'default'),
87+
],
5988
];

src/DataProviders/LaravelRequestDataProvider.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Treblle\Laravel\DataProviders;
66

7+
use Throwable;
78
use Carbon\Carbon;
89
use Treblle\Php\Helpers\HeaderFilter;
910
use Treblle\Php\DataTransferObject\Request;
@@ -77,6 +78,13 @@ private function getRequestBody(): array
7778
return $this->request->attributes->get('treblle_original_payload');
7879
}
7980

80-
return $this->request->toArray();
81+
// Try toArray() first (supports JSON requests), fallback to all() for GET/multipart
82+
try {
83+
return $this->request->toArray();
84+
} catch (Throwable $e) {
85+
// toArray() throws BadMethodCallException for GET requests and ValidationException
86+
// for malformed JSON. Fall back to all() which safely returns all input.
87+
return $this->request->all();
88+
}
8189
}
8290
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Treblle\Laravel\DataTransferObject;
6+
7+
use Treblle\Php\DataTransferObject\Data;
8+
9+
/**
10+
* Data Transfer Object for Treblle payload.
11+
*
12+
* This DTO holds the complete extracted payload data that can be safely
13+
* serialized and passed to queue jobs. By extracting data from Request/Response
14+
* objects before queuing, we avoid serialization issues with Closures and
15+
* other non-serializable properties.
16+
*
17+
* @package Treblle\Laravel\DataTransferObject
18+
*/
19+
final readonly class TrebllePayloadData
20+
{
21+
/**
22+
* Create a new TrebllePayloadData instance.
23+
*
24+
* @param string $apiKey The Treblle API key
25+
* @param string $sdkToken The Treblle SDK token
26+
* @param string $sdkName The SDK name
27+
* @param float $sdkVersion The SDK version
28+
* @param Data $data The core Treblle data object containing request/response/errors
29+
* @param string|null $url Optional custom Treblle endpoint URL
30+
* @param bool $debug Whether debug mode is enabled
31+
*/
32+
public function __construct(
33+
public string $apiKey,
34+
public string $sdkToken,
35+
public string $sdkName,
36+
public float $sdkVersion,
37+
public Data $data,
38+
public string|null $url = null,
39+
public bool $debug = false
40+
) {
41+
}
42+
43+
/**
44+
* Convert to array format for JSON encoding.
45+
*
46+
* @return array<string, mixed>
47+
*/
48+
public function toArray(): array
49+
{
50+
return [
51+
'api_key' => $this->apiKey,
52+
'sdk_token' => $this->sdkToken,
53+
'sdk' => $this->sdkName,
54+
'version' => $this->sdkVersion,
55+
'data' => $this->data,
56+
];
57+
}
58+
}

src/Jobs/SendTreblleData.php

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Treblle\Laravel\Jobs;
6+
7+
use Throwable;
8+
use Illuminate\Bus\Queueable;
9+
use Illuminate\Support\Facades\Log;
10+
use Illuminate\Support\Facades\Http;
11+
use Illuminate\Queue\SerializesModels;
12+
use Illuminate\Queue\InteractsWithQueue;
13+
use Illuminate\Contracts\Queue\ShouldQueue;
14+
use Illuminate\Foundation\Bus\Dispatchable;
15+
use Treblle\Laravel\DataTransferObject\TrebllePayloadData;
16+
17+
/**
18+
* Queue job for sending Treblle monitoring data asynchronously.
19+
*
20+
* @package Treblle\Laravel\Jobs
21+
*/
22+
final class SendTreblleData implements ShouldQueue
23+
{
24+
use Dispatchable;
25+
use InteractsWithQueue;
26+
use Queueable;
27+
use SerializesModels;
28+
29+
/**
30+
* The number of times the job may be attempted.
31+
*
32+
* @var int
33+
*/
34+
public int $tries = 3;
35+
36+
/**
37+
* The number of seconds to wait before retrying the job.
38+
*
39+
* @var int
40+
*/
41+
public int $backoff = 5;
42+
43+
/**
44+
* The number of seconds the job can run before timing out.
45+
*
46+
* @var int
47+
*/
48+
public int $timeout = 10;
49+
50+
/**
51+
* Create a new job instance.
52+
*
53+
* @param TrebllePayloadData $payloadData The pre-extracted Treblle payload data
54+
*/
55+
public function __construct(
56+
private readonly TrebllePayloadData $payloadData
57+
) {
58+
}
59+
60+
/**
61+
* Execute the job.
62+
* @return void
63+
*/
64+
public function handle(): void
65+
{
66+
try {
67+
// JSON encode and compress in one pass for better memory efficiency
68+
$jsonPayload = json_encode($this->payloadData->toArray());
69+
$compressedPayload = gzencode($jsonPayload, 6);
70+
71+
$url = $this->getBaseUrl();
72+
73+
// Log the payload being sent (only in debug mode)
74+
if ($this->payloadData->debug) {
75+
Log::info('Treblle: Sending payload', [
76+
'url' => $url,
77+
'api_key' => $this->payloadData->apiKey,
78+
'sdk_token' => mb_substr($this->payloadData->sdkToken, 0, 10) . '...',
79+
'payload_size' => mb_strlen($jsonPayload),
80+
'compressed_size' => mb_strlen($compressedPayload),
81+
'payload' => json_decode($jsonPayload, true), // Log as array for better readability
82+
]);
83+
}
84+
85+
// Use Laravel's HTTP client for better integration and testing
86+
$response = Http::timeout(3)
87+
->connectTimeout(3)
88+
->withoutVerifying()
89+
->withHeaders([
90+
'Content-Type' => 'application/json',
91+
'Content-Encoding' => 'gzip',
92+
'x-api-key' => $this->payloadData->sdkToken,
93+
'Accept-Encoding' => 'gzip',
94+
])
95+
->withBody($compressedPayload, 'application/json')
96+
->post($url);
97+
98+
// Log the response (only in debug mode)
99+
if ($this->payloadData->debug) {
100+
Log::info('Treblle: Response received', [
101+
'status_code' => $response->status(),
102+
'headers' => $response->headers(),
103+
'body' => $response->body(),
104+
]);
105+
}
106+
} catch (Throwable $throwable) {
107+
// Always log errors (important for troubleshooting)
108+
Log::error('Treblle: Failed to send data', [
109+
'error' => $throwable->getMessage(),
110+
'trace' => $this->payloadData->debug ? $throwable->getTraceAsString() : null,
111+
]);
112+
113+
if ($this->payloadData->debug) {
114+
throw $throwable;
115+
}
116+
117+
// Let Laravel's queue system handle retry logic
118+
$this->fail($throwable);
119+
}
120+
}
121+
122+
/**
123+
* Get the base URL for Treblle API.
124+
*
125+
* If a custom URL is provided, it will be used. Otherwise, a random
126+
* endpoint from the available Treblle servers is selected for load
127+
* balancing.
128+
*
129+
* @return string The Treblle API endpoint URL
130+
*/
131+
private function getBaseUrl(): string
132+
{
133+
$urls = [
134+
'https://rocknrolla.treblle.com',
135+
'https://punisher.treblle.com',
136+
'https://sicario.treblle.com',
137+
];
138+
139+
return $this->payloadData->url ?? $urls[array_rand($urls)];
140+
}
141+
}

0 commit comments

Comments
 (0)