Skip to content

Commit a50beca

Browse files
access_log: add OTLP/HTTP exporter to OpenTelemetry access logger
Adds HTTP transport support to the OpenTelemetry access logger, complementing #29207 (OTLP/HTTP tracing). This enables direct OTLP log export to backends that only accept HTTP (Dynatrace, Datadog, Logfire), without requiring an intermediate collector sidecar. Changes: - Add http_service field for OTLP/HTTP transport configuration - Add top-level grpc_service, log_name, buffer_flush_interval, buffer_size_bytes, filter_state_objects_to_log, and custom_tags fields to match tracer config pattern - Add field_migrate.oneof_promotion annotations for future oneof migration - Add helper functions getLogName() and getGrpcService() with fallback to common_config - Add transport validation (exactly one transport must be configured) - Deprecate common_config field (still functional for backward compatibility) - Extract shared utilities (otlp_log_utils) from duplicated HTTP/gRPC code Fixes #26352 (access logs portion) Signed-off-by: Adrian Cole <[email protected]>
1 parent b34c60c commit a50beca

File tree

22 files changed

+2003
-74
lines changed

22 files changed

+2003
-74
lines changed

TESTING.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Running OTLP Access Logger Tests
2+
3+
## macOS Setup
4+
5+
On macOS, do NOT use `--config=clang` in `user.bazelrc`. The default Xcode clang works without additional configuration.
6+
7+
## Running Tests
8+
9+
Config tests:
10+
11+
```bash
12+
bazel test //test/extensions/access_loggers/open_telemetry:config_test --test_output=errors
13+
```
14+
15+
HTTP access log exporter tests:
16+
17+
```bash
18+
bazel test //test/extensions/access_loggers/open_telemetry:http_access_log_impl_test --test_output=errors
19+
```
20+
21+
HTTP access log integration test:
22+
23+
```bash
24+
bazel test //test/extensions/access_loggers/open_telemetry:http_access_log_integration_test --test_output=errors
25+
```
26+
27+
All OTLP access logger tests:
28+
29+
```bash
30+
bazel test //test/extensions/access_loggers/open_telemetry/... --test_output=errors
31+
```
32+
33+
## Building Envoy
34+
35+
Build with crash recovery:
36+
37+
```bash
38+
cd ~/oss/envoy
39+
nohup bazel build envoy 2>&1 | tee build.log &
40+
```
41+
42+
Using `nohup` with `tee` ensures:
43+
- Build continues if your terminal/session crashes
44+
- Output is saved to `build.log` for review
45+
- You can resume monitoring with `tail -10 build.log`
46+
47+
**WARNING**: A full build takes a long time (~7,000 actions with cache, 16,000+ from scratch).
48+
Expect 45-90 minutes on a modern M-series Mac, longer on slower machines.
49+
50+
### Monitoring Progress
51+
52+
Poll periodically with last 10 lines only (saves context):
53+
54+
```bash
55+
tail -10 build.log
56+
```
57+
58+
**DO NOT use `tail -f`** - it wastes context by streaming everything.
59+
60+
Only read the full log if the build fails:
61+
62+
```bash
63+
# If build failed, then read full log
64+
cat build.log
65+
```
66+
67+
### If Session Crashes
68+
69+
```bash
70+
# Check if build is still running
71+
pgrep -f "bazel build envoy"
72+
73+
# Resume monitoring (poll, don't follow)
74+
tail -10 ~/oss/envoy/build.log
75+
```
76+
77+
### Verify Build
78+
79+
```bash
80+
bazel-bin/source/exe/envoy-static --version
81+
```
82+
83+
Expected output format:
84+
```
85+
bazel-bin/source/exe/envoy-static version: <commit>/<version>-dev/Modified/DEBUG/BoringSSL
86+
```
87+
88+
## Manual Testing with OTLP HTTP Collector
89+
90+
1. Start a local OTLP HTTP collector on port 4318 (e.g., otel-tui or OpenTelemetry Collector)
91+
92+
2. Run Envoy with the example config (note the `OTEL_RESOURCE_ATTRIBUTES` env var for service name):
93+
94+
```bash
95+
OTEL_RESOURCE_ATTRIBUTES=service.name=envoy-example bazel-bin/source/exe/envoy-static -c configs/envoy-otel-http.yaml
96+
```
97+
98+
3. Send a test request:
99+
100+
```bash
101+
curl localhost:10080/get
102+
```
103+
104+
4. Wait at least 2 seconds before killing Envoy for **logs** to be exported. The HTTP access logger buffers logs and flushes every 1 second. If you kill Envoy immediately, buffered logs may not be sent. **Traces are sent immediately** (no buffering).
105+
106+
```bash
107+
# Example: send request, wait for log flush, then stop
108+
curl localhost:10080/get
109+
sleep 2
110+
pkill -f envoy-static
111+
```
112+
113+
5. Verify logs and traces are received by your collector.
114+
115+
The example config (`configs/envoy-otel-http.yaml`) demonstrates:
116+
- HTTP transport for OTLP access logs
117+
- HTTP transport for OTLP traces with 100% sampling
118+
- Log body with request details
119+
- `resource_detectors` for consistent `service.name` across all signals via `OTEL_RESOURCE_ATTRIBUTES` env var
120+
121+
## Coding Style Points from PR Reviews
122+
123+
Key patterns learned from reviewing 25+ Envoy PRs with extensive review comments:
124+
125+
### Proto Design
126+
- **Never use `oneof` for existing fields** - Adding `oneof` is a breaking change per API_VERSIONING.md. Use separate optional fields with runtime validation and `field_migrate.oneof_promotion` annotation. (PR #29207, #29289)
127+
- **Document mutual exclusivity clearly** - State "Only one of X or Y may be set" in both field comments. (PR #29207)
128+
- **Document behavior when fields are unset** - Required vs optional fields need clear defaults. (PR #39628, #28490)
129+
130+
### Code Style
131+
- **Comments end with periods** - Envoy style requires punctuation on comments. (PR #37172)
132+
- **Use `constexpr` for constants** - Avoid runtime initialization with `const`. (PR #32490, #31507)
133+
- **Prefer `absl::string_view` over `const std::string&`** where appropriate. (PR #39628)
134+
- **Magic numbers should be named constants** - Use `constexpr` with descriptive names. (PR #36581)
135+
- **Log levels matter** - Use `warn` for recoverable failures, `error` for configuration issues, `debug` for routine events. (PR #29207, #37172)
136+
137+
### Testing
138+
- **Add descriptive comments to tests** - Explain what each test verifies. (PR #37172, #30479)
139+
- **Test config rejection** - Always test that invalid configurations are properly rejected. (PR #29289)
140+
- **Verify all headers in HTTP tests** - Check method, path, content-type, custom headers. (PR #29207)
141+
142+
### Async/HTTP Patterns
143+
- **Track in-flight requests** - Use `Http::AsyncClientRequestTracker` to cancel pending requests on destruction. (PR #29207)
144+
- **Parse headers at construction time** - Avoid copies per request. (PR #29207)
145+
- **Use `setReference()` for headers** - Not `setCopy()`, to avoid allocations. (PR #29207)
146+
147+
### Avoiding Common Mistakes
148+
- **Don't hardcode values that appear elsewhere** - Use existing constants. (PR #36581)
149+
- **Reject bad config rather than silently fail** - Throw exception at config time. (PR #37172, #39628)
150+
- **Consider performance impact** - Run benchmarks for shared code paths. (PR #29709)

api/envoy/extensions/access_loggers/open_telemetry/v3/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ api_proto_package(
88
deps = [
99
"//envoy/config/core/v3:pkg",
1010
"//envoy/extensions/access_loggers/grpc/v3:pkg",
11+
"//envoy/type/tracing/v3:pkg",
1112
"@com_github_cncf_xds//udpa/annotations:pkg",
1213
"@opentelemetry_proto//:common_proto",
1314
],

api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,61 @@ syntax = "proto3";
33
package envoy.extensions.access_loggers.open_telemetry.v3;
44

55
import "envoy/config/core/v3/extension.proto";
6+
import "envoy/config/core/v3/grpc_service.proto";
7+
import "envoy/config/core/v3/http_service.proto";
68
import "envoy/extensions/access_loggers/grpc/v3/als.proto";
9+
import "envoy/type/tracing/v3/custom_tag.proto";
10+
11+
import "google/protobuf/duration.proto";
12+
import "google/protobuf/wrappers.proto";
713

814
import "opentelemetry/proto/common/v1/common.proto";
915

16+
import "udpa/annotations/migrate.proto";
1017
import "udpa/annotations/status.proto";
11-
import "validate/validate.proto";
1218

1319
option java_package = "io.envoyproxy.envoy.extensions.access_loggers.open_telemetry.v3";
1420
option java_outer_classname = "LogsServiceProto";
1521
option java_multiple_files = true;
1622
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/open_telemetry/v3;open_telemetryv3";
1723
option (udpa.annotations.file_status).package_version_status = ACTIVE;
1824

19-
// [#protodoc-title: OpenTelemetry (gRPC) Access Log]
25+
// [#protodoc-title: OpenTelemetry Access Log]
2026

2127
// Configuration for the built-in ``envoy.access_loggers.open_telemetry``
2228
// :ref:`AccessLog <envoy_v3_api_msg_config.accesslog.v3.AccessLog>`. This configuration will
2329
// populate `opentelemetry.proto.collector.v1.logs.ExportLogsServiceRequest.resource_logs <https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/collector/logs/v1/logs_service.proto>`_.
2430
// In addition, the request start time is set in the dedicated field.
2531
// [#extension: envoy.access_loggers.open_telemetry]
26-
// [#next-free-field: 8]
32+
// [#next-free-field: 16]
2733
message OpenTelemetryAccessLogConfig {
2834
// [#comment:TODO(itamarkam): add 'filter_state_objects_to_log' to logs.]
29-
grpc.v3.CommonGrpcAccessLogConfig common_config = 1 [(validate.rules).message = {required: true}];
35+
// [#deprecated:]
36+
// DEPRECATED: Use ``grpc_service`` and other top-level fields instead.
37+
// This field is kept for backward compatibility with existing gRPC configurations.
38+
// Note: Only one of ``common_config``, ``grpc_service``, or ``http_service`` may be used.
39+
grpc.v3.CommonGrpcAccessLogConfig common_config = 1
40+
[(udpa.annotations.field_migrate).oneof_promotion = "otlp_exporter"];
41+
42+
// The upstream HTTP cluster that will receive OTLP logs via
43+
// `OTLP/HTTP <https://opentelemetry.io/docs/specs/otlp/#otlphttp>`_.
44+
// Note: Only one of ``common_config``, ``grpc_service``, or ``http_service`` may be used.
45+
//
46+
// .. note::
47+
//
48+
// The ``request_headers_to_add`` property in the OTLP HTTP exporter service
49+
// does not support the :ref:`format specifier <config_access_log_format>` as used for
50+
// :ref:`HTTP access logging <config_access_log>`.
51+
// The values configured are added as HTTP headers on the OTLP export request
52+
// without any formatting applied.
53+
config.core.v3.HttpService http_service = 8
54+
[(udpa.annotations.field_migrate).oneof_promotion = "otlp_exporter"];
55+
56+
// The upstream gRPC cluster that will receive OTLP logs.
57+
// Note: Only one of ``common_config``, ``grpc_service``, or ``http_service`` may be used.
58+
// This field is preferred over ``common_config.grpc_service``.
59+
config.core.v3.GrpcService grpc_service = 9
60+
[(udpa.annotations.field_migrate).oneof_promotion = "otlp_exporter"];
3061

3162
// If specified, Envoy will not generate built-in resource labels
3263
// like ``log_name``, ``zone_name``, ``cluster_name``, ``node_name``.
@@ -57,4 +88,33 @@ message OpenTelemetryAccessLogConfig {
5788
// See the formatters extensions documentation for details.
5889
// [#extension-category: envoy.formatter]
5990
repeated config.core.v3.TypedExtensionConfig formatters = 7;
91+
92+
// Friendly identifier for the access log, used as a resource attribute.
93+
// This field is preferred over ``common_config.log_name``.
94+
string log_name = 10;
95+
96+
// The interval for flushing access logs to the transport. Default: 1 second.
97+
// This field is preferred over ``common_config.buffer_flush_interval``.
98+
google.protobuf.Duration buffer_flush_interval = 11;
99+
100+
// Soft size limit in bytes for the access log buffer. When the buffer exceeds
101+
// this limit, logs will be flushed. Default: 16KB.
102+
// This field is preferred over ``common_config.buffer_size_bytes``.
103+
google.protobuf.UInt32Value buffer_size_bytes = 12;
104+
105+
// Additional filter state objects to log.
106+
// This field is preferred over ``common_config.filter_state_objects_to_log``.
107+
repeated string filter_state_objects_to_log = 13;
108+
109+
// Optional custom tags to include in the log record.
110+
// This field is preferred over ``common_config.custom_tags``.
111+
repeated type.tracing.v3.CustomTag custom_tags = 14;
112+
113+
// An ordered list of resource detectors for populating resource attributes.
114+
// This enables using the ``OTEL_RESOURCE_ATTRIBUTES`` environment variable for
115+
// ``service.name`` and other resource attributes, matching the tracer and
116+
// metrics sink patterns. When configured, the detected resources are merged
117+
// with any static ``resource_attributes``, with detector values taking precedence.
118+
// [#extension-category: envoy.tracers.opentelemetry.resource_detectors]
119+
repeated config.core.v3.TypedExtensionConfig resource_detectors = 15;
60120
}

changelogs/current.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,5 +458,26 @@ new_features:
458458
- area: attributes
459459
change: |
460460
added :ref:`attributes <arch_overview_attributes>` for looking up request or response headers bytes.
461+
- area: access_log
462+
change: |
463+
Added support for exporting OpenTelemetry access logs via HTTP and refactored the configuration
464+
to match the tracer config pattern. New top-level fields ``http_service``, ``grpc_service``,
465+
``log_name``, ``buffer_flush_interval``, ``buffer_size_bytes``, ``filter_state_objects_to_log``,
466+
and ``custom_tags`` provide a cleaner, transport-agnostic configuration.
467+
The ``common_config`` field is deprecated but remains functional for backward compatibility.
468+
See :ref:`http_service <envoy_v3_api_field_extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig.http_service>`
469+
and :ref:`grpc_service <envoy_v3_api_field_extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig.grpc_service>`.
470+
- area: access_log
471+
change: |
472+
Added ``resource_detectors`` to the OpenTelemetry access logger configuration. This enables
473+
consistent resource attribute configuration across all signals (tracing, metrics, access logs)
474+
using the ``OTEL_RESOURCE_ATTRIBUTES`` environment variable via the environment resource detector.
475+
See :ref:`resource_detectors <envoy_v3_api_field_extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig.resource_detectors>`.
461476
462477
deprecated:
478+
- area: access_log
479+
change: |
480+
The ``common_config`` field in
481+
:ref:`OpenTelemetryAccessLogConfig <envoy_v3_api_msg_extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig>`
482+
is deprecated. Use the new top-level fields ``grpc_service``, ``log_name``, ``buffer_flush_interval``,
483+
``buffer_size_bytes``, ``filter_state_objects_to_log``, and ``custom_tags`` instead.

0 commit comments

Comments
 (0)