### Install CakePHP OpenTelemetry Plugin Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/README.md Use Composer to install the plugin. Load it in your application's bootstrap process. ```bash composer require kaz29/cakephp-otel-plugin ``` ```php $this->addPlugin('OtelInstrumentation'); ``` -------------------------------- ### Verify Controller Span with PHPUnit Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Example PHPUnit test demonstrating how to verify that a controller action generates an OpenTelemetry span with correct attributes and status. Requires OtelTestTrait for setup and teardown. ```php // No application code changes required — instrumentation fires automatically. // Span produced when GET /articles/index is handled: // // Span { // name: "App\Controller\ArticlesController::index", // kind: KIND_SERVER, // attributes: { // "http.method": "GET", // "http.url": "http://example.com/articles/index", // "http.route": "index", // "cake.controller": "App\\Controller\\ArticlesController", // "cake.action": "index", // }, // status: STATUS_OK, // } // PHPUnit verification example: use OtelInstrumentation\Test\TestCase\OtelTestTrait; use PHPUnit\Framework\TestCase; class MyControllerTest extends TestCase { use OtelTestTrait; protected function setUp(): void { $this->setUpOtel(); } protected function tearDown(): void { $this->resetOtel(); } public function testControllerSpan(): void { $request = new \Cake\Http\ServerRequest([ 'url' => '/articles/index', 'environment' => ['REQUEST_METHOD' => 'GET', 'REQUEST_URI' => '/articles/index'], ]); $request = $request->withParam('action', 'index'); $controller = new \App\Controller\ArticlesController($request); $controller->invokeAction(fn () => $controller->getResponse(), []); $span = $this->getFirstSpan(); $this->assertSame(\OpenTelemetry\API\Trace\SpanKind::KIND_SERVER, $span->getKind()); $this->assertSame('GET', $this->getSpanAttribute($span, 'http.method')); $this->assertSame(\OpenTelemetry\API\Trace\StatusCode::STATUS_OK, $span->getStatus()->getCode()); } } ``` -------------------------------- ### PHPUnit Helpers for OpenTelemetry Span and Log Assertions Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Use `OtelTestTrait` in your PHPUnit tests to configure an in-memory TracerProvider and LoggerProvider. Remember to call `setUpOtel()` in `setUp()` and `resetOtel()` in `tearDown()`. The trait provides methods like `getSpansByName()`, `getFirstSpan()`, and `getLogRecords()` for assertions. ```php use OtelInstrumentation\Test\TestCase\OtelTestTrait; use PHPUnit\Framework\TestCase; class PaymentServiceTest extends TestCase { use OtelTestTrait; protected function setUp(): void { if (!extension_loaded('opentelemetry')) { $this->markTestSkipped('ext-opentelemetry is not installed.'); } $this->setUpOtel(); } protected function tearDown(): void { $this->resetOtel(); } public function testChargeProducesSpan(): void { $service = new \App\Service\PaymentService(); $service->charge(1000, 'usd'); // Assert span by name $spans = $this->getSpansByName('payment.charge'); $this->assertCount(1, $spans); $this->assertSame('stripe', $this->getSpanAttribute($spans[0], 'payment.provider')); $this->assertSame(1000, $this->getSpanAttribute($spans[0], 'payment.amount')); // Assert first span $first = $this->getFirstSpan(); $this->assertNotNull($first); // Assert all spans $allSpans = $this->getSpans(); $this->assertGreaterThan(0, count($allSpans)); // Assert log records $logs = $this->getLogRecords(); $this->assertCount(0, $logs); // no errors expected } } ``` -------------------------------- ### Resolve OpenTelemetry db.system from CakePHP Table Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Use `DbSystemResolver::resolveFromTable()` to get the OpenTelemetry `db.system` string from a CakePHP Table instance. For testing or direct driver class resolution, use `DbSystemResolver::resolveFromDriverClass()` with the fully qualified driver class name. ```php use OtelInstrumentation\Util\DbSystemResolver; // From a Table instance (used internally by TableInstrumentation): $dbSystem = DbSystemResolver::resolveFromTable($this->Users); // Returns: 'mysql' | 'postgresql' | 'sqlite' | 'mssql' | 'other_sql' // From a driver class name string (useful for testing): DbSystemResolver::resolveFromDriverClass(\Cake\Database\Driver\Mysql::class); // 'mysql' DbSystemResolver::resolveFromDriverClass(\Cake\Database\Driver\Postgres::class); // 'postgresql' DbSystemResolver::resolveFromDriverClass(\Cake\Database\Driver\Sqlite::class); // 'sqlite' DbSystemResolver::resolveFromDriverClass(\Cake\Database\Driver\Sqlserver::class);// 'mssql' DbSystemResolver::resolveFromDriverClass('Vendor\Driver\Oracle'); // 'other_sql' ``` -------------------------------- ### Embed Trace ID into CakePHP Logs Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/specs/001-exception-logger/SPEC.md This example shows how to extract and potentially log the trace ID and span ID from the current OpenTelemetry span. It's useful for correlating logs with traces when the span is valid. ```php use OpenTelemetry\API\Trace\Span; $context = Span::getCurrent()->getContext(); $traceId = $context->getTraceId(); $spanId = $context->getSpanId(); // ゼロ値は出力しない if ($context->isValid()) { // ログに trace_id / span_id を付与 } ``` -------------------------------- ### Configure OpenTelemetry Environment Variables Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/README.md Set these environment variables to configure the OpenTelemetry exporter and service name. ```bash OTEL_PHP_AUTOLOAD_ENABLED=true OTEL_SERVICE_NAME=my-cakephp-app OTEL_TRACES_EXPORTER=otlp OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 ``` -------------------------------- ### Configure OpenTelemetry Environment Variables Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Set required environment variables for OpenTelemetry. These variables configure service name, exporter type, protocol, and endpoint, and can disable the SDK if needed. ```bash # Required environment variables OTEL_PHP_AUTOLOAD_ENABLED=true OTEL_SERVICE_NAME=my-cakephp-app OTEL_TRACES_EXPORTER=otlp OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 # Disable SDK entirely (e.g., local dev with no collector) OTEL_SDK_DISABLED=true ``` -------------------------------- ### Register Custom Instrumentation Statically Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/README.md Use CustomInstrumentation::register() for dynamic attributes via a callback. This method allows for more advanced instrumentation scenarios. ```php // In Application::bootstrap(), before $this->addPlugin('OtelInstrumentation') use OtelInstrumentation\Instrumentation\CustomInstrumentation; use OpenTelemetry\API\Trace\SpanKind; CustomInstrumentation::register( \App\Service\PaymentService::class, 'charge', spanName: 'payment.charge', kind: SpanKind::KIND_CLIENT, attributes: ['payment.provider' => 'stripe'], attributeCallback: fn($instance, $params, $class, $function) => [ 'payment.amount' => $params[0] ?? null, ], ); ``` -------------------------------- ### Add OtelErrorLoggingMiddleware to Application Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/README.md Integrate OtelErrorLoggingMiddleware into your application's middleware stack after ErrorHandlerMiddleware to log 500-level exceptions as OpenTelemetry log records associated with the current span. ```php use OtelInstrumentation\Middleware\OtelErrorLoggingMiddleware; public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { $middlewareQueue ->add(new ErrorHandlerMiddleware()) ->add(new OtelErrorLoggingMiddleware()) // ... ; } ``` -------------------------------- ### Register OtelErrorLoggingMiddleware in Application.php Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/specs/001-exception-logger/SPEC.md Register the OtelErrorLoggingMiddleware within the ErrorHandlerMiddleware in the `middleware()` method of `src/Application.php`. Placing it inside ensures it can catch exceptions. ```php use OtelInstrumentation\Middleware\OtelErrorLoggingMiddleware; public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { $middlewareQueue ->add(new ErrorHandlerMiddleware()) ->add(new OtelErrorLoggingMiddleware()) // ← ErrorHandler の内側 // ... ; } ``` -------------------------------- ### Implement OtelErrorLoggingMiddleware for Exception Logging Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Add `OtelErrorLoggingMiddleware` after `ErrorHandlerMiddleware` to log exceptions as OpenTelemetry `ERROR`-severity log records. It handles `Throwable` instances, treating non-HTTP exceptions as 500-level errors. ```php // src/Application.php use OtelInstrumentation\Middleware\OtelErrorLoggingMiddleware; use Cake\Error\Middleware\ErrorHandlerMiddleware; public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { $middlewareQueue ->add(new ErrorHandlerMiddleware(Configure::read('Error'))) ->add(new OtelErrorLoggingMiddleware()) // ← inside ErrorHandlerMiddleware // ... other middleware ; return $middlewareQueue; } ``` ```php // PHPUnit verification: public function testMiddlewareEmitsLogForServerError(): void { $this->setUpOtel(); $middleware = new OtelErrorLoggingMiddleware(); $request = new \Cake\Http\ServerRequest(); $handler = new class implements \Psr\Http\Server\RequestHandlerInterface { public function handle(\Psr\Http\Message\ServerRequestInterface $r): \Psr\Http\Message\ResponseInterface { throw new \RuntimeException('boom'); } }; try { $middleware->process($request, $handler); } catch (\RuntimeException) {} $logs = $this->getLogRecords(); $this->assertCount(1, $logs); $this->assertSame('boom', (string) $logs[0]->getBody()); } ``` -------------------------------- ### Add Development Dependencies to composer.json Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/specs/001-exception-logger/SPEC.md Include the specified PHPUnit version and configure autoload-dev for the test namespace in your `composer.json` file to manage development dependencies. ```json "require-dev": { "phpunit/phpunit": "^10.0 || ^11.0" }, "autoload-dev": { "psr-4": { "OtelInstrumentation\\Test\\": "tests/" } } ``` -------------------------------- ### PHPUnit Test Suite Configuration Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/specs/001-exception-logger/SPEC.md Configure `phpunit.xml` to define two test suites: `unit` for tests not requiring the OpenTelemetry extension and `integration` for tests that do. This allows for flexible testing environments. ```xml ``` -------------------------------- ### Register Custom Hook Programmatically Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Register custom OpenTelemetry hooks for any class method before adding the OtelInstrumentation plugin. Supports static attributes and a dynamic attribute callback. Duplicate registrations are ignored. ```php // src/Application.php — bootstrap() method use OtelInstrumentation\Instrumentation\CustomInstrumentation; use OpenTelemetry\API\Trace\SpanKind; public function bootstrap(): void { // Register BEFORE addPlugin so hooks are queued before apply() CustomInstrumentation::register( class: \App\Service\PaymentService::class, method: 'charge', spanName: 'payment.charge', kind: SpanKind::KIND_CLIENT, attributes: ['payment.provider' => 'stripe'], attributeCallback: fn ($instance, $params, $class, $function) => [ 'payment.amount' => $params[0] ?? null, 'payment.currency' => $params[1] ?? 'usd', ], ); // Minimal registration — span name auto-generated as FQCN::method CustomInstrumentation::register( class: \App\Service\InventoryService::class, method: 'reserve', ); parent::bootstrap(); $this->addPlugin('OtelInstrumentation'); } // Resulting span for PaymentService::charge(1500, 'usd'): // Span { // name: "payment.charge", // kind: KIND_CLIENT, // attributes: { // "payment.provider": "stripe", // "payment.amount": 1500, // "payment.currency": "usd", // }, // status: STATUS_OK | STATUS_ERROR (with recordedException), // } ``` -------------------------------- ### Disable OTel SDK via .env Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/specs/001-exception-logger/SPEC.md To disable the OpenTelemetry SDK entirely in local development, add `OTEL_SDK_DISABLED=true` to your `.env` file. No plugin modifications are necessary. ```bash OTEL_SDK_DISABLED=true ``` -------------------------------- ### Configure Custom Instrumentation via Configure Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/README.md Register custom hooks for class methods to generate spans. The plugin automatically generates span names if not explicitly provided. Avoid re-registering already instrumented methods like Controller::invokeAction or Table::find. ```php // config/bootstrap.php or config/app_local.php use Cake\Core\Configure; use OpenTelemetry\API\Trace\SpanKind; Configure::write('OtelInstrumentation.hooks', [ // Minimal — span name auto-generated as "App\Service\PaymentService::charge" ['class' => \App\Service\PaymentService::class, 'method' => 'charge'], // With options [ 'class' => \App\Service\PaymentService::class, 'method' => 'refund', 'spanName' => 'payment.refund', 'kind' => SpanKind::KIND_CLIENT, 'attributes' => ['payment.provider' => 'stripe'], ], ]); ``` -------------------------------- ### Register Plugin in Application Bootstrap Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Register the 'OtelInstrumentation' plugin in your CakePHP application's bootstrap process. This ensures the plugin's instrumentation is loaded and active. ```php // config/bootstrap.php or src/Application.php $this->addPlugin('OtelInstrumentation'); ``` -------------------------------- ### Define Custom Method Hooks with HookDefinition Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Use `HookDefinition` to configure custom method hooks for instrumentation. Ensure the `kind` property is a valid `SpanKind` constant to avoid exceptions. ```php use OtelInstrumentation\Instrumentation\HookDefinition; use OtelInstrumentation\Instrumentation\CustomInstrumentation; use OpenTelemetry\API\Trace\SpanKind; $definition = new HookDefinition( class: \App\Queue\JobRunner::class, method: 'execute', spanName: 'queue.job.execute', kind: SpanKind::KIND_CONSUMER, attributes: ['messaging.system' => 'redis'], attributeCallback: fn ($instance, $params) => [ 'messaging.destination' => $params[0]?->getQueue() ?? 'default', ], ); CustomInstrumentation::add($definition); // Invalid kind — throws \InvalidArgumentException: // new HookDefinition(class: Foo::class, method: 'bar', kind: 999); ``` -------------------------------- ### Decorate PSR-3 Logger with TraceAwareLogger Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Wrap an existing PSR-3 logger with `TraceAwareLogger` to automatically inject current trace and span IDs into log contexts. If no active span is present, the context remains unchanged. ```php use OtelInstrumentation\Log\TraceAwareLogger; // Wrap your existing CakePHP / Monolog logger $innerLogger = \Cake\Log\Log::engine('default'); // any PSR-3 LoggerInterface $logger = new TraceAwareLogger($innerLogger); // Usage — standard PSR-3 interface $logger->info('User logged in', ['user_id' => 42]); // Resulting context passed to inner logger (when inside an active span): // ['user_id' => 42, 'trace_id' => 'a3d5e7...', 'span_id' => 'b1c2d3...'] // Outside an active span — context unchanged: // ['user_id' => 42] // Integrate with CakePHP dependency injection or service layer: // src/Application.php public function services(ContainerInterface $container): void { $container->add(\Psr\Log\LoggerInterface::class, function () { $inner = new \Monolog\Logger('app'); $inner->pushHandler(new \Monolog\Handler\StreamHandler('php://stdout')); return new TraceAwareLogger($inner); }); } ``` -------------------------------- ### Decorate PSR-3 Logger with TraceAwareLogger Source: https://github.com/kaz29/cakephp-otel-plugin/blob/main/README.md Wrap an existing PSR-3 LoggerInterface with TraceAwareLogger to automatically inject trace and span IDs into log message contexts. ```php $logger = new \OtelInstrumentation\Log\TraceAwareLogger($existingPsr3Logger); ``` -------------------------------- ### Register Hooks via CakePHP Configure Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Load hook definitions from a CakePHP Configure array for static hook definitions. The plugin reads this configuration automatically during Plugin::bootstrap(). Throws InvalidArgumentException if 'class' or 'method' are missing. ```php // config/app_local.php or config/bootstrap.php use Cake\Core\Configure; use OpenTelemetry\API\Trace\SpanKind; Configure::write('OtelInstrumentation.hooks', [ // Minimal — span name: "App\Service\NotificationService::send" [ 'class' => \App\Service\NotificationService::class, 'method' => 'send', ], // Full options (no attributeCallback — use register() for that) [ 'class' => \App\Service\PaymentService::class, 'method' => 'refund', 'spanName' => 'payment.refund', 'kind' => SpanKind::KIND_CLIENT, 'attributes' => ['payment.provider' => 'stripe'], ], ]); // Invalid config — throws \InvalidArgumentException: // Configure::write('OtelInstrumentation.hooks', [ // ['class' => \App\Service\Foo::class], // missing 'method' // ]); ``` -------------------------------- ### Automatic Table Instrumentation Source: https://context7.com/kaz29/cakephp-otel-plugin/llms.txt Automatically hooks into CakePHP Table methods like find, save, and delete. No application code changes are required. Spans include database system, table name, and entity new status for saves. Validation failures on save are marked as errors. ```php // No application code changes required. // // Span produced by $this->Users->find('all'): // Span { name: "Users.find(all)", kind: KIND_CLIENT, // attributes: { "db.system": "mysql", "cake.table": "Users", "cake.find_type": "all" } } // // Span produced by $this->Users->save($entity) for a new entity: // Span { name: "Users.save", kind: KIND_CLIENT, // attributes: { "db.system": "mysql", "cake.table": "Users", "cake.entity.new": true } } // // Span produced by $this->Users->delete($entity): // Span { name: "Users.delete", kind: KIND_CLIENT, // attributes: { "db.system": "mysql", "cake.table": "Users" } } // PHPUnit verification example: public function testSaveSpanMarksErrorOnFalse(): void { // Simulate save() returning false (e.g., validation failure) $span = $this->getSpansByName('Users.save')[0]; $this->assertSame("\OpenTelemetry\API\Trace\StatusCode::STATUS_ERROR", $span->getStatus()->getCode()); $this->assertStringContainsString('save() returned false', $span->getStatus()->getDescription()); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.