Plack-Middleware-OpenTelemetry
view release on metacpan or search on metacpan
- OpenTelemetry (>= 0.018) - OpenTelemetry Perl SDK
- OpenTelemetry::SDK (>= 0.020) - OpenTelemetry SDK implementation
- Feature::Compat::Try - Modern try/catch syntax
- Syntax::Keyword::Dynamically - Dynamic scope management
### Middleware Functionality
The middleware automatically:
1. Creates OpenTelemetry spans for HTTP requests
2. Extracts tracing context from incoming requests
3. Sets standard HTTP semantic attributes following OpenTelemetry conventions
4. Handles both synchronous and streaming responses
5. Records exceptions and sets appropriate span status
### Configuration Options
- `include_client_errors`: Boolean flag to mark 4xx HTTP status codes as errors (default: false)
- `resource_attributes`: Hash reference for custom resource attributes
### Testing Environment
Tests use the console exporter (`OTEL_TRACES_EXPORTER=console`) for development and debugging.
## Development Workflow
lib/Plack/Middleware/OpenTelemetry.pm view on Meta::CPAN
The middleware automatically:
=over
=item * Creates OpenTelemetry spans for HTTP requests
=item * Extracts W3C trace context from incoming requests
=item * Sets standard HTTP semantic attributes following OpenTelemetry conventions
=item * Handles both synchronous and streaming responses
=item * Records exceptions and sets appropriate span status
=back
The following HTTP attributes are set on spans:
=over
=item * C<http.request.method> - HTTP method
t/otel_integration.t view on Meta::CPAN
my $path = $env->{PATH_INFO} || '/';
if ($path eq '/success') {
return [200, ['Content-Type' => 'text/plain'], ['OK']];
} elsif ($path eq '/not_found') {
return [404, ['Content-Type' => 'text/plain'], ['Not Found']];
} elsif ($path eq '/server_error') {
return [500, ['Content-Type' => 'text/plain'], ['Server Error']];
} elsif ($path eq '/exception') {
die "Test exception message";
} elsif ($path eq '/streaming') {
return sub {
my $respond = shift;
my $writer = $respond->([200, ['Content-Type' => 'text/plain']]);
$writer->write("chunk1");
$writer->write("chunk2");
$writer->close;
};
} else {
return [200, ['Content-Type' => 'text/plain'], ['Default']];
}
t/otel_integration.t view on Meta::CPAN
};
my $span = parse_console_span($span_output);
is $span->{status}{code}, SPAN_STATUS_ERROR, 'Exception marked span as error';
is $span->{attributes}->{'http.response.status_code'}, 500, 'Exception sets 500 status';
# Check if exception was recorded
ok $span->{events} && @{$span->{events}}, 'Exception events recorded';
};
# Test streaming responses
subtest 'Streaming Response' => sub {
my $app = build_app();
my $span_output = capture_stderr {
test_psgi $app, sub {
my $cb = shift;
my $res = $cb->(GET 'http://example.com/streaming');
is $res->code, 200;
is $res->content, 'chunk1chunk2', 'Streaming content received';
};
};
# For streaming responses, multiple spans might be output or format might differ
# Just verify we captured some span output
ok $span_output, 'Span output captured for streaming response';
like $span_output, qr/streaming|GET request/, 'Span output contains expected content';
};
# Test resource attributes
subtest 'Resource Attributes' => sub {
my $app = build_app(
resource_attributes => {
'service.name' => 'test-service',
'service.version' => '1.0.0'
}
);
( run in 0.550 second using v1.01-cache-2.11-cpan-4face438c0f )