Skip to content

Commit fe6704a

Browse files
committed
Support authorization header for java OTel SDK
1 parent 42a5d6b commit fe6704a

File tree

5 files changed

+87
-14
lines changed

5 files changed

+87
-14
lines changed

config/logstash.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,11 @@
303303
#
304304
# otel.metrics.protocol: "grpc"
305305
#
306+
# Authorization header for authenticated OTLP endpoints.
307+
# Examples: "ApiKey xxx" or "Bearer xxx"
308+
#
309+
# otel.metrics.authorization_header:
310+
#
306311
# Additional resource attributes as comma-separated key=value pairs.
307312
# These are attached to all exported metrics to identify this Logstash instance.
308313
# Example: "environment=production,cluster=us-west,team=platform"

logstash-core/lib/logstash/environment.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ def self.as_java_range(r)
115115
Setting::StringSetting.new("otel.metrics.endpoint", "http://localhost:4317"),
116116
Setting::NumericSetting.new("otel.metrics.interval", 10), # seconds
117117
Setting::StringSetting.new("otel.metrics.protocol", "grpc", true, ["grpc", "http"]),
118+
Setting::NullableStringSetting.new("otel.metrics.authorization_header", nil, false), # e.g., "ApiKey xxx" or "Bearer xxx"
118119
Setting::NullableStringSetting.new("otel.resource.attributes", nil, false) # key=value,key2=value2 format
119120
# post_process
120121
].each {|setting| SETTINGS.register(setting) }

logstash-core/lib/logstash/instrument/periodic_poller/otel.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module LogStash module Instrument module PeriodicPoller
3535
# otel.metrics.endpoint: "http://localhost:4317"
3636
# otel.metrics.interval: 10
3737
# otel.metrics.protocol: "grpc"
38+
# otel.metrics.authorization_header: "ApiKey xxx" # or "Bearer xxx"
3839
# otel.resource.attributes: "environment=production,cluster=us-west"
3940
#
4041
class OTel < Base
@@ -54,7 +55,8 @@ def initialize(metric, agent, settings)
5455
agent.name,
5556
settings.get("otel.metrics.interval"),
5657
settings.get("otel.metrics.protocol"),
57-
settings.get("otel.resource.attributes")
58+
settings.get("otel.resource.attributes"),
59+
settings.get("otel.metrics.authorization_header")
5860
)
5961

6062
# Take initial snapshot

logstash-core/spec/logstash/instrument/periodic_poller/otel_spec.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
allow(s).to receive(:get).with("otel.metrics.interval").and_return(10)
5656
allow(s).to receive(:get).with("otel.metrics.protocol").and_return("grpc")
5757
allow(s).to receive(:get).with("otel.resource.attributes").and_return(nil)
58+
allow(s).to receive(:get).with("otel.metrics.authorization_header").and_return(nil)
5859
end
5960
end
6061

@@ -85,12 +86,65 @@
8586
"test-node-name",
8687
10,
8788
"grpc",
89+
nil,
8890
nil
8991
).and_return(otel_service)
9092

9193
otel_poller
9294
end
9395

96+
context "with authorization header" do
97+
let(:settings) do
98+
double("settings").tap do |s|
99+
allow(s).to receive(:get).with("otel.metrics.endpoint").and_return("https://apm.example.com")
100+
allow(s).to receive(:get).with("otel.metrics.interval").and_return(10)
101+
allow(s).to receive(:get).with("otel.metrics.protocol").and_return("http")
102+
allow(s).to receive(:get).with("otel.resource.attributes").and_return(nil)
103+
allow(s).to receive(:get).with("otel.metrics.authorization_header").and_return("ApiKey my-secret-key")
104+
end
105+
end
106+
107+
it "passes authorization_header to OtelMetricsService" do
108+
expect(OtelMetricsService).to receive(:new).with(
109+
"https://apm.example.com",
110+
"test-node-id",
111+
"test-node-name",
112+
10,
113+
"http",
114+
nil,
115+
"ApiKey my-secret-key"
116+
).and_return(otel_service)
117+
118+
otel_poller
119+
end
120+
end
121+
122+
context "with Bearer token authorization" do
123+
let(:settings) do
124+
double("settings").tap do |s|
125+
allow(s).to receive(:get).with("otel.metrics.endpoint").and_return("https://apm.example.com")
126+
allow(s).to receive(:get).with("otel.metrics.interval").and_return(10)
127+
allow(s).to receive(:get).with("otel.metrics.protocol").and_return("http")
128+
allow(s).to receive(:get).with("otel.resource.attributes").and_return(nil)
129+
allow(s).to receive(:get).with("otel.metrics.authorization_header").and_return("Bearer my-bearer-token")
130+
end
131+
end
132+
133+
it "passes Bearer token to OtelMetricsService" do
134+
expect(OtelMetricsService).to receive(:new).with(
135+
"https://apm.example.com",
136+
"test-node-id",
137+
"test-node-name",
138+
10,
139+
"http",
140+
nil,
141+
"Bearer my-bearer-token"
142+
).and_return(otel_service)
143+
144+
otel_poller
145+
end
146+
end
147+
94148
it "registers global metrics" do
95149
expect(otel_service).to receive(:registerObservableCounter).with(
96150
"logstash.events.in", anything, anything, anything, anything

logstash-core/src/main/java/org/logstash/instrument/metrics/otel/OtelMetricsService.java

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,25 @@ public class OtelMetricsService {
6363
private final Map<String, ObservableLongGauge> gauges = new ConcurrentHashMap<>();
6464
private final Map<String, ObservableLongCounter> observableCounters = new ConcurrentHashMap<>();
6565

66+
private final String authorizationHeader;
67+
6668
/**
6769
* Creates a new OTel metrics service.
6870
*
69-
* @param endpoint OTLP endpoint (e.g., "http://localhost:4317")
70-
* @param nodeId Logstash node ID
71-
* @param nodeName Logstash node name
72-
* @param intervalSeconds Export interval in seconds
73-
* @param protocol "grpc" or "http"
74-
* @param resourceAttributes Additional resource attributes (comma-separated key=value pairs)
71+
* @param endpoint OTLP endpoint (e.g., "http://localhost:4317")
72+
* @param nodeId Logstash node ID
73+
* @param nodeName Logstash node name
74+
* @param intervalSeconds Export interval in seconds
75+
* @param protocol "grpc" or "http"
76+
* @param resourceAttributes Additional resource attributes (comma-separated key=value pairs)
77+
* @param authorizationHeader Authorization header value (e.g., "ApiKey xxx" or "Bearer xxx"), or null
7578
*/
7679
public OtelMetricsService(String endpoint, String nodeId, String nodeName,
77-
int intervalSeconds, String protocol, String resourceAttributes) {
80+
int intervalSeconds, String protocol, String resourceAttributes,
81+
String authorizationHeader) {
7882
LOGGER.info("Initializing OpenTelemetry metrics export to {} (protocol: {}, interval: {}s)",
7983
endpoint, protocol, intervalSeconds);
84+
this.authorizationHeader = authorizationHeader;
8085

8186
// Build resource attributes
8287
AttributesBuilder resourceAttrsBuilder = Attributes.builder()
@@ -115,16 +120,22 @@ public OtelMetricsService(String endpoint, String nodeId, String nodeName,
115120

116121
private MetricExporter createExporter(String endpoint, String protocol) {
117122
if ("http".equalsIgnoreCase(protocol)) {
118-
return OtlpHttpMetricExporter.builder()
123+
var builder = OtlpHttpMetricExporter.builder()
119124
.setEndpoint(endpoint + "/v1/metrics")
120-
.setTimeout(Duration.ofSeconds(10))
121-
.build();
125+
.setTimeout(Duration.ofSeconds(10));
126+
if (authorizationHeader != null && !authorizationHeader.isEmpty()) {
127+
builder.addHeader("Authorization", authorizationHeader);
128+
}
129+
return builder.build();
122130
} else {
123131
// Default to gRPC
124-
return OtlpGrpcMetricExporter.builder()
132+
var builder = OtlpGrpcMetricExporter.builder()
125133
.setEndpoint(endpoint)
126-
.setTimeout(Duration.ofSeconds(10))
127-
.build();
134+
.setTimeout(Duration.ofSeconds(10));
135+
if (authorizationHeader != null && !authorizationHeader.isEmpty()) {
136+
builder.addHeader("Authorization", authorizationHeader);
137+
}
138+
return builder.build();
128139
}
129140
}
130141

0 commit comments

Comments
 (0)