Skip to content

Commit c45a558

Browse files
committed
v1.1.0
1 parent cecb0db commit c45a558

15 files changed

Lines changed: 267 additions & 23 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
include:
18-
- php: '8.1'
18+
- php: '7.4'
1919
cake: '^4.4'
2020
- php: '8.1'
2121
cake: '^5.0'
22-
- php: '8.3'
23-
cake: '^5.0'
2422

2523
steps:
2624
- name: Checkout code

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ return [
9797
'enabled' => true,
9898
'api_key' => env('PERFBASE_API_KEY'),
9999
'sample_rate' => 0.1,
100+
'profile_http_status_codes' => [...range(200, 299), ...range(500, 599)],
100101
'flags' => FeatureFlags::DefaultFlags,
101102
'environment' => env('APP_ENV', 'production'),
102103
'app_version' => env('APP_VERSION', ''),
@@ -135,6 +136,7 @@ return [
135136
'api_key' => null,
136137
'api_url' => 'https://ingress.perfbase.cloud',
137138
'sample_rate' => 0.1,
139+
'profile_http_status_codes' => [...range(200, 299), ...range(500, 599)],
138140
'timeout' => 10,
139141
'proxy' => null,
140142
'flags' => \Perfbase\SDK\FeatureFlags::DefaultFlags,
@@ -162,12 +164,15 @@ return [
162164
| `api_key` | `null` | Perfbase API key |
163165
| `api_url` | `https://ingress.perfbase.cloud` | Receiver base URL |
164166
| `sample_rate` | `0.1` | Sampling rate from `0.0` to `1.0` |
167+
| `profile_http_status_codes` | `[...range(200, 299), ...range(500, 599)]` | HTTP response codes that should be submitted |
165168
| `timeout` | `10` | Submission timeout in seconds |
166169
| `proxy` | `null` | Optional outbound proxy |
167170
| `flags` | `FeatureFlags::DefaultFlags` | Perfbase extension feature flags |
168171
| `environment` | `production` | Trace environment tag |
169172
| `app_version` | `''` | Application version tag |
170173

174+
`profile_http_status_codes` is configured in CakePHP config rather than an environment variable. The default `[...range(200, 299), ...range(500, 599)]` submits successful responses and server errors, while dropping common noisy client responses such as `404`. Add codes like `404` if you want to keep them.
175+
171176
### Runtime config
172177

173178
If you want to override settings programmatically, do it before the plugin uses the config:
@@ -246,6 +251,8 @@ The HTTP lifecycle matches against normalized identifiers, not just the raw URL.
246251

247252
HTTP profiling is provided by [`PerfbaseMiddleware`](src/PerfbaseMiddleware.php). The middleware guarantees lifecycle cleanup with `try`/`finally` and attaches response or exception data before submission.
248253

254+
By default, only responses with a status code in `profile_http_status_codes` are submitted. The shipped default is `[...range(200, 299), ...range(500, 599)]`.
255+
249256
Recorded attributes include:
250257

251258
- `source=http`

config/perfbase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
'api_key' => null,
1111
'api_url' => 'https://ingress.perfbase.cloud',
1212
'sample_rate' => 0.1,
13+
'profile_http_status_codes' => [...range(200, 299), ...range(500, 599)],
1314
'timeout' => 10,
1415
'proxy' => null,
1516
'flags' => FeatureFlags::DefaultFlags,

src/Lifecycle/AbstractCakeProfiler.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ public function stopProfiling(): void
7777
return;
7878
}
7979

80+
if (!$this->shouldSubmitTrace()) {
81+
$this->perfbase->reset();
82+
return;
83+
}
84+
8085
$result = $this->perfbase->submitTrace();
8186
if (!$result->isSuccess()) {
8287
$this->handleSubmitFailure($result);
@@ -169,5 +174,10 @@ protected function handleSubmitFailure(SubmitResult $result): void
169174
);
170175
}
171176

177+
protected function shouldSubmitTrace(): bool
178+
{
179+
return true;
180+
}
181+
172182
abstract protected function shouldProfile(): bool;
173183
}

src/Lifecycle/HttpRequestLifecycle.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Perfbase\CakePHP\Lifecycle;
44

55
use Perfbase\CakePHP\Support\FilterMatcher;
6+
use Perfbase\CakePHP\Support\PerfbaseConfig;
67
use Perfbase\CakePHP\Support\SpanNaming;
78
use Perfbase\SDK\Utils\EnvironmentUtils;
89
use Psr\Http\Message\ResponseInterface;
@@ -11,6 +12,7 @@
1112
class HttpRequestLifecycle extends AbstractCakeProfiler
1213
{
1314
private ServerRequestInterface $request;
15+
private ?int $responseStatusCode = null;
1416

1517
public function __construct(ServerRequestInterface $request)
1618
{
@@ -47,7 +49,17 @@ protected function setDefaultAttributes(): void
4749

4850
public function setResponse(ResponseInterface $response): void
4951
{
50-
$this->setAttribute('http_status_code', (string)$response->getStatusCode());
52+
$this->responseStatusCode = $response->getStatusCode();
53+
$this->setAttribute('http_status_code', (string)$this->responseStatusCode);
54+
}
55+
56+
protected function shouldSubmitTrace(): bool
57+
{
58+
if ($this->responseStatusCode === null) {
59+
return true;
60+
}
61+
62+
return in_array($this->responseStatusCode, PerfbaseConfig::profileHttpStatusCodes(), true);
5163
}
5264

5365
/**

src/Lifecycle/QueueJobLifecycle.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class QueueJobLifecycle extends AbstractCakeProfiler
1515
{
1616
public function __construct()
1717
{
18-
parent::__construct('queue.future');
18+
parent::__construct('queue');
1919
}
2020

2121
protected function shouldProfile(): bool

src/PerfbasePlugin.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private function loadDefaultConfig(): void
6464
$configured = [];
6565
}
6666

67-
Configure::write('Perfbase', array_replace_recursive($defaults['Perfbase'], $appConfig, $configured));
67+
Configure::write('Perfbase', PerfbaseConfig::merge($defaults['Perfbase'], $appConfig, $configured));
6868
PerfbaseConfig::clearCache();
6969
}
7070

src/Support/PerfbaseConfig.php

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public static function defaults(): array
2424
'api_key' => null,
2525
'api_url' => 'https://ingress.perfbase.cloud',
2626
'sample_rate' => 0.1,
27+
'profile_http_status_codes' => [...range(200, 299), ...range(500, 599)],
2728
'timeout' => 10,
2829
'proxy' => null,
2930
'flags' => FeatureFlags::DefaultFlags,
@@ -54,7 +55,7 @@ public static function get(): array
5455
$configured = [];
5556
}
5657

57-
self::$cache = array_replace_recursive(self::defaults(), $configured);
58+
self::$cache = self::merge(self::defaults(), $configured);
5859

5960
return self::$cache;
6061
}
@@ -64,7 +65,7 @@ public static function get(): array
6465
*/
6566
public static function mergeAndWrite(array $config): void
6667
{
67-
Configure::write('Perfbase', array_replace_recursive(self::defaults(), $config));
68+
Configure::write('Perfbase', self::merge(self::defaults(), $config));
6869
self::clearCache();
6970
}
7071

@@ -113,6 +114,60 @@ public static function excludePatterns(string $context): array
113114
return self::readPatternList('exclude', $context);
114115
}
115116

117+
/**
118+
* @return array<int>
119+
*/
120+
public static function profileHttpStatusCodes(): array
121+
{
122+
$config = self::get();
123+
$statusCodes = $config['profile_http_status_codes'] ?? [...range(200, 299), ...range(500, 599)];
124+
125+
if (!is_array($statusCodes)) {
126+
return [...range(200, 299), ...range(500, 599)];
127+
}
128+
129+
$normalized = [];
130+
131+
foreach ($statusCodes as $statusCode) {
132+
if (is_int($statusCode)) {
133+
$normalized[] = $statusCode;
134+
continue;
135+
}
136+
137+
if (is_string($statusCode) && ctype_digit($statusCode)) {
138+
$normalized[] = (int)$statusCode;
139+
}
140+
}
141+
142+
return array_values(array_unique($normalized));
143+
}
144+
145+
/**
146+
* @param array<string, mixed> ...$layers
147+
* @return array<string, mixed>
148+
*/
149+
public static function merge(array ...$layers): array
150+
{
151+
$merged = [];
152+
$hasStatusCodeOverride = false;
153+
$statusCodeOverride = null;
154+
155+
foreach ($layers as $layer) {
156+
$merged = array_replace_recursive($merged, $layer);
157+
158+
if (array_key_exists('profile_http_status_codes', $layer)) {
159+
$statusCodeOverride = $layer['profile_http_status_codes'];
160+
$hasStatusCodeOverride = true;
161+
}
162+
}
163+
164+
if ($hasStatusCodeOverride) {
165+
$merged['profile_http_status_codes'] = $statusCodeOverride;
166+
}
167+
168+
return $merged;
169+
}
170+
116171
/**
117172
* @return array<string>
118173
*/

src/Support/SpanNaming.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ class SpanNaming
88
{
99
public static function generate(string $type, string $identifier): string
1010
{
11-
return $type . '.' . $identifier;
11+
return self::normalizeSpanName($type . '_' . $identifier);
1212
}
1313

1414
public static function forHttp(ServerRequestInterface $request): string
1515
{
16-
return self::generate('http', $request->getMethod() . '.' . self::httpIdentifier($request));
16+
return 'http';
1717
}
1818

1919
public static function httpAction(ServerRequestInterface $request): string
@@ -26,7 +26,7 @@ public static function httpAction(ServerRequestInterface $request): string
2626
*/
2727
public static function forConsole($command): string
2828
{
29-
return self::generate('console', self::consoleIdentifier($command));
29+
return 'artisan';
3030
}
3131

3232
/**
@@ -139,4 +139,14 @@ private static function classBasename(string $className): string
139139

140140
return $position === false ? $className : substr($className, $position + 1);
141141
}
142+
143+
private static function normalizeSpanName(string $spanName): string
144+
{
145+
$spanName = strtolower($spanName);
146+
$spanName = preg_replace('/[^a-z0-9_-]+/', '_', $spanName) ?? 'unknown';
147+
$spanName = preg_replace('/_+/', '_', $spanName) ?? 'unknown';
148+
$spanName = trim($spanName, '_');
149+
150+
return substr($spanName !== '' ? $spanName : 'unknown', 0, 64);
151+
}
142152
}

tests/Integration/PerfbasePluginTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ public function testBootstrapLoadsAppPerfbaseConfigAndPreservesExplicitOverrides
188188
'enabled' => true,
189189
'api_key' => 'file-key',
190190
'environment' => 'staging',
191+
'profile_http_status_codes' => [200, 404],
191192
],
192193
];
193194
PHP
@@ -197,6 +198,7 @@ public function testBootstrapLoadsAppPerfbaseConfigAndPreservesExplicitOverrides
197198
Configure::write('Perfbase', [
198199
'api_key' => 'runtime-key',
199200
'app_version' => '1.2.3',
201+
'profile_http_status_codes' => [200, 422],
200202
]);
201203
PerfbaseConfig::clearCache();
202204

@@ -209,6 +211,7 @@ public function testBootstrapLoadsAppPerfbaseConfigAndPreservesExplicitOverrides
209211
$this->assertSame('runtime-key', $config['api_key']);
210212
$this->assertSame('staging', $config['environment']);
211213
$this->assertSame('1.2.3', $config['app_version']);
214+
$this->assertSame([200, 422], $config['profile_http_status_codes']);
212215
}
213216

214217
public function testBootstrapHandlesMissingAppConfigFile(): void

0 commit comments

Comments
 (0)