Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OTLP authentication section to ./java/sdk.md #5866

Merged
merged 7 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
[submodule "content-modules/opentelemetry-java-examples"]
path = content-modules/opentelemetry-java-examples
url = https://github.com/open-telemetry/opentelemetry-java-examples.git
javaexamples-pin = 63cc9b4
javaexamples-pin = cce0f6a
146 changes: 132 additions & 14 deletions content/en/docs/languages/java/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,7 @@ Span exporters built-in to the SDK and maintained by the community in
| `InterceptableSpanExporter` | `io.opentelemetry.contrib:opentelemetry-processors:{{% param vers.contrib %}}-alpha` | Passes spans to a flexible interceptor before exporting. |
| `KafkaSpanExporter` | `io.opentelemetry.contrib:opentelemetry-kafka-exporter:{{% param vers.contrib %}}-alpha` | Exports spans by writing to a Kafka topic. |

**[1]**: See [OTLP exporter sender](#otlp-exporter-senders) for implementation
details.
**[1]**: See [OTLP exporters](#otlp-exporters) for implementation details.

The following code snippet demonstrates `SpanExporter` programmatic
configuration:
Expand Down Expand Up @@ -767,8 +766,7 @@ Metric exporters built-in to the SDK and maintained by the community in
| `OtlpStdoutMetricExporter` | `io.opentelemetry:opentelemetry-exporter-logging-otlp:{{% param vers.otel %}}` | Logs metrics to `System.out` in the OTLP [JSON file encoding][] (experimental). |
| `InterceptableMetricExporter` | `io.opentelemetry.contrib:opentelemetry-processors:{{% param vers.contrib %}}-alpha` | Passes metrics to a flexible interceptor before exporting. |

**[1]**: See [OTLP exporter sender](#otlp-exporter-senders) for implementation
details.
**[1]**: See [OTLP exporters](#otlp-exporters) for implementation details.

The following code snippet demonstrates `MetricExporter` programmatic
configuration:
Expand Down Expand Up @@ -1095,8 +1093,7 @@ Span exporters built-in to the SDK and maintained by the community in
| `OtlpStdoutLogRecordExporter` | `io.opentelemetry:opentelemetry-exporter-logging-otlp:{{% param vers.otel %}}` | Logs log records to `System.out` in the OTLP [JSON file encoding][] (experimental). |
| `InterceptableLogRecordExporter` | `io.opentelemetry.contrib:opentelemetry-processors:{{% param vers.contrib %}}-alpha` | Passes log records to a flexible interceptor before exporting. |

**[1]**: See [OTLP exporter sender](#otlp-exporter-senders) for implementation
details.
**[1]**: See [OTLP exporters](#otlp-exporters) for implementation details.

**[2]**: `OtlpJsonLoggingLogRecordExporter` logs to JUL, and may cause infinite
loops (i.e. JUL -> SLF4J -> Logback -> OpenTelemetry Appender -> OpenTelemetry
Expand Down Expand Up @@ -1362,20 +1359,29 @@ public class IgnoreExportErrorsFilter implements java.util.logging.Filter {
io.opentelemetry.sdk.trace.export.BatchSpanProcessor = io.opentelemetry.extension.logging.IgnoreExportErrorsFilter
```

### OTLP exporter senders
### OTLP exporters

The [span exporter](#spanexporter), [metric exporter](#metricexporter), and
[log exporter](#logrecordexporter) discuss OTLP exporters of the form:
[log exporter](#logrecordexporter) sections describe OTLP exporters of the form:

- `OtlpHttp{Signal}Exporter`s export data via OTLP `http/protobuf`.
- `OtlpGrpc{Signal}Exporter`s export data via OTLP `grpc`.
- `OtlpHttp{Signal}Exporter`, which exports data via OTLP `http/protobuf`
- `OtlpGrpc{Signal}Exporter`, which exports data via OTLP `grpc`

The exporters for all signals are available via
`io.opentelemetry:opentelemetry-exporter-otlp:{{% param vers.otel %}}`.
`io.opentelemetry:opentelemetry-exporter-otlp:{{% param vers.otel %}}`, and have
significant overlap across `grpc` and `http/protobuf` versions of the OTLP
protocol, and between signals. The following sections elaborate on these key
concepts:

Internally, these exporters depend on various client libraries to execute HTTP
and gRPC requests. There is no single HTTP / gRPC client library which satisfies
all use cases in the Java ecosystem:
- [Senders](#senders): an abstraction for a different HTTP / gRPC client
libraries.
- [Authentication](#authentication) options for OTLP exporters.

#### Senders

The OTLP exporters depend on various client libraries to execute HTTP and gRPC
requests. There is no single HTTP / gRPC client library which satisfies all use
cases in the Java ecosystem:

- Java 11+ brings the built-in `java.net.http.HttpClient`, but
`opentelemetry-java` needs to support Java 8+ users, and this can't be used to
Expand Down Expand Up @@ -1403,6 +1409,118 @@ add a dependency on the alternative.
you must also add a dependency on a
[gRPC transport implementations](https://github.com/grpc/grpc-java#transport).

#### Authentication

The OTLP exporters provide mechanisms for static and dynamic header-based
authentication, and for mTLS.

If using
[zero-code SDK autoconfigure](../configuration/#zero-code-sdk-autoconfigure)
with environment variables and system properties, see
[relevant system properties](../configuration/#properties-exporters):

- `otel.exporter.otlp.headers` for static header-based authentication.
- `otel.exporter.otlp.client.key`, `otel.exporter.otlp.client.certificate` for
mTLS authentication.

The following code snippet demonstrates programmatic configuration of static and
dynamic header-based authentication:

<!-- prettier-ignore-start -->
<?code-excerpt "src/main/java/otel/OtlpAuthenticationConfig.java"?>
```java
package otel;

import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.Map;
import java.util.function.Supplier;

public class OtlpAuthenticationConfig {
public static void staticAuthenticationHeader(String endpoint) {
// If the OTLP destination accepts a static, long-lived authentication header like an API key,
// set it as a header.
// This reads the API key from the OTLP_API_KEY env var to avoid hard coding the secret in
// source code.
String apiKeyHeaderName = "api-key";
String apiKeyHeaderValue = System.getenv("OTLP_API_KEY");

// Initialize OTLP Span, Metric, and LogRecord exporters using a similar pattern
OtlpHttpSpanExporter spanExporter =
OtlpHttpSpanExporter.builder()
.setEndpoint(endpoint)
.addHeader(apiKeyHeaderName, apiKeyHeaderValue)
.build();
OtlpHttpMetricExporter metricExporter =
OtlpHttpMetricExporter.builder()
.setEndpoint(endpoint)
.addHeader(apiKeyHeaderName, apiKeyHeaderValue)
.build();
OtlpHttpLogRecordExporter logRecordExporter =
OtlpHttpLogRecordExporter.builder()
.setEndpoint(endpoint)
.addHeader(apiKeyHeaderName, apiKeyHeaderValue)
.build();
}

public static void dynamicAuthenticationHeader(String endpoint) {
// If the OTLP destination requires a dynamic authentication header, such as a JWT which needs
// to be periodically refreshed, use a header supplier.
// Here we implement a simple supplier which adds a header of the form "Authorization: Bearer
// <token>", where <token> is fetched from refreshBearerToken every 10 minutes.
String username = System.getenv("OTLP_USERNAME");
String password = System.getenv("OTLP_PASSWORD");
Supplier<Map<String, String>> supplier =
new AuthHeaderSupplier(() -> refreshToken(username, password), Duration.ofMinutes(10));

// Initialize OTLP Span, Metric, and LogRecord exporters using a similar pattern
OtlpHttpSpanExporter spanExporter =
OtlpHttpSpanExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build();
OtlpHttpMetricExporter metricExporter =
OtlpHttpMetricExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build();
OtlpHttpLogRecordExporter logRecordExporter =
OtlpHttpLogRecordExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build();
}

private static class AuthHeaderSupplier implements Supplier<Map<String, String>> {
private final Supplier<String> tokenRefresher;
private final Duration tokenRefreshInterval;
private Instant refreshedAt = Instant.ofEpochMilli(0);
private String currentTokenValue;

private AuthHeaderSupplier(Supplier<String> tokenRefresher, Duration tokenRefreshInterval) {
this.tokenRefresher = tokenRefresher;
this.tokenRefreshInterval = tokenRefreshInterval;
}

@Override
public Map<String, String> get() {
return Collections.singletonMap("Authorization", "Bearer " + getToken());
}

private synchronized String getToken() {
Instant now = Instant.now();
if (currentTokenValue == null || now.isAfter(refreshedAt.plus(tokenRefreshInterval))) {
currentTokenValue = tokenRefresher.get();
refreshedAt = now;
}
return currentTokenValue;
}
}

private static String refreshToken(String username, String password) {
// For a production scenario, this would be replaced with out-of-band request to exchange
// username / password for bearer token.
return "abc123";
}
}
```
<!-- prettier-ignore-end -->

### Testing

TODO: document tools available for testing the SDK
Expand Down
Loading