Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import io.opentelemetry.android.Incubating
import io.opentelemetry.android.OpenTelemetryRum
import io.opentelemetry.android.RumBuilder
import io.opentelemetry.android.agent.connectivity.Compression
import io.opentelemetry.android.agent.connectivity.EndpointConnectivity
import io.opentelemetry.android.agent.connectivity.ExportProtocol
import io.opentelemetry.android.agent.dsl.OpenTelemetryConfiguration
import io.opentelemetry.android.agent.session.SessionConfig
import io.opentelemetry.android.agent.session.SessionIdTimeoutHandler
Expand All @@ -21,19 +23,15 @@ import io.opentelemetry.android.session.SessionProvider
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 io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
import io.opentelemetry.sdk.logs.export.LogRecordExporter
import io.opentelemetry.sdk.metrics.export.MetricExporter
import io.opentelemetry.sdk.trace.export.SpanExporter

@OptIn(Incubating::class)
object OpenTelemetryRumInitializer {
/**
* Opinionated [io.opentelemetry.android.OpenTelemetryRum] initialization.
*
* @param context Your android app's application context. This should be from your Application
* subclass or an appropriate context that allows retrieving the application context. If you
* supply an inappropriate context (e.g. from attachBaseContext) then instrumentation relying
* on activity lifecycle callbacks will not function correctly.
* @param configuration Type-safe config DSL that controls how OpenTelemetry
* should behave.
*/
@JvmStatic
fun initialize(
context: Context,
Expand All @@ -42,18 +40,13 @@ object OpenTelemetryRumInitializer {
val cfg = OpenTelemetryConfiguration()
configuration(cfg)

// ensure we're using the Application Context to prevent potential leaks.
// if context.applicationContext is null (e.g. called from within attachBaseContext),
// fallback to the supplied context.
val ctx =
when (context) {
is Application -> context
else -> context.applicationContext ?: context
}

val spansEndpoint = cfg.exportConfig.spansEndpoint()
val logsEndpoints = cfg.exportConfig.logsEndpoint()
val metricsEndpoint = cfg.exportConfig.metricsEndpoint()
val exportEndpoints = resolveExportEndpoints(cfg)

val resourceBuilder = AndroidResource.createDefault(ctx).toBuilder()
cfg.resourceAction(resourceBuilder)
Expand All @@ -65,28 +58,119 @@ object OpenTelemetryRumInitializer {
.setResource(resource)
.setClock(cfg.clock)
.addSpanExporterCustomizer {
createSpanExporter(exportEndpoints.spans, exportEndpoints.protocol)
}.addLogRecordExporterCustomizer {
createLogRecordExporter(exportEndpoints.logs, exportEndpoints.protocol)
}.addMetricExporterCustomizer {
createMetricExporter(exportEndpoints.metrics, exportEndpoints.protocol)
}.build()
}

private data class ExportEndpoints(
val spans: EndpointConnectivity,
val logs: EndpointConnectivity,
val metrics: EndpointConnectivity,
val protocol: ExportProtocol,
)

private fun resolveExportEndpoints(cfg: OpenTelemetryConfiguration): ExportEndpoints {
cfg.unifiedExportConfig?.let { unified ->
return ExportEndpoints(
spans = unified.spansEndpoint(),
logs = unified.logsEndpoint(),
metrics = unified.metricsEndpoint(),
protocol = unified.protocol,
)
}

cfg.grpcExportConfig?.let { grpc ->
return ExportEndpoints(
spans = grpc.spansEndpoint(),
logs = grpc.logsEndpoint(),
metrics = grpc.metricsEndpoint(),
protocol = ExportProtocol.GRPC,
)
}

return ExportEndpoints(
spans = cfg.exportConfig.spansEndpoint(),
logs = cfg.exportConfig.logsEndpoint(),
metrics = cfg.exportConfig.metricsEndpoint(),
protocol = ExportProtocol.HTTP,
)
}

private fun createSpanExporter(
endpoint: EndpointConnectivity,
protocol: ExportProtocol,
): SpanExporter =
when (protocol) {
ExportProtocol.HTTP -> {
OtlpHttpSpanExporter
.builder()
.setEndpoint(spansEndpoint.getUrl())
.setHeaders(spansEndpoint::getHeaders)
.setCompression(spansEndpoint.getCompression().getUpstreamName())
.setEndpoint(endpoint.getUrl())
.setHeaders(endpoint::getHeaders)
.setCompression(endpoint.getCompression().getUpstreamName())
.build()
}.addLogRecordExporterCustomizer {
}

ExportProtocol.GRPC -> {
OtlpGrpcSpanExporter
.builder()
.setEndpoint(endpoint.getUrl())
.setHeaders(endpoint::getHeaders)
.setCompression(endpoint.getCompression().getUpstreamName())
.build()
}
}

private fun createLogRecordExporter(
endpoint: EndpointConnectivity,
protocol: ExportProtocol,
): LogRecordExporter =
when (protocol) {
ExportProtocol.HTTP -> {
OtlpHttpLogRecordExporter
.builder()
.setEndpoint(logsEndpoints.getUrl())
.setHeaders(logsEndpoints::getHeaders)
.setCompression(logsEndpoints.getCompression().getUpstreamName())
.setEndpoint(endpoint.getUrl())
.setHeaders(endpoint::getHeaders)
.setCompression(endpoint.getCompression().getUpstreamName())
.build()
}.addMetricExporterCustomizer {
}

ExportProtocol.GRPC -> {
OtlpGrpcLogRecordExporter
.builder()
.setEndpoint(endpoint.getUrl())
.setHeaders(endpoint::getHeaders)
.setCompression(endpoint.getCompression().getUpstreamName())
.build()
}
}

private fun createMetricExporter(
endpoint: EndpointConnectivity,
protocol: ExportProtocol,
): MetricExporter =
when (protocol) {
ExportProtocol.HTTP -> {
OtlpHttpMetricExporter
.builder()
.setEndpoint(metricsEndpoint.getUrl())
.setHeaders(metricsEndpoint::getHeaders)
.setCompression(metricsEndpoint.getCompression().getUpstreamName())
.setEndpoint(endpoint.getUrl())
.setHeaders(endpoint::getHeaders)
.setCompression(endpoint.getCompression().getUpstreamName())
.build()
}.build()
}
}

ExportProtocol.GRPC -> {
OtlpGrpcMetricExporter
.builder()
.setEndpoint(endpoint.getUrl())
.setHeaders(endpoint::getHeaders)
.setCompression(endpoint.getCompression().getUpstreamName())
.build()
}
}

private fun Compression.getUpstreamName(): String =
when (this) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.android.agent.connectivity

enum class ExportProtocol {
HTTP,
GRPC,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.android.agent.connectivity

internal class GrpcEndpointConnectivity private constructor(
private val endpoint: String,
private val headers: Map<String, String>,
private val compression: Compression,
) : EndpointConnectivity {
companion object {
fun create(
endpoint: String,
headers: Map<String, String>,
compression: Compression,
): GrpcEndpointConnectivity = GrpcEndpointConnectivity(endpoint, headers, compression)
}

override fun getUrl(): String = endpoint

override fun getHeaders(): Map<String, String> = headers

override fun getCompression(): Compression = compression
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.android.agent.dsl

import io.opentelemetry.android.agent.connectivity.Compression
import io.opentelemetry.android.agent.connectivity.EndpointConnectivity
import io.opentelemetry.android.agent.connectivity.ExportProtocol
import io.opentelemetry.android.agent.connectivity.GrpcEndpointConnectivity
import io.opentelemetry.android.agent.connectivity.HttpEndpointConnectivity

Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing class-level documentation for ExportConfiguration. This public API class should have KDoc documentation explaining its purpose, usage, and how it differs from the protocol-specific export configurations (HttpExportConfiguration and GrpcExportConfiguration).

Suggested change
/**
* Configures how telemetry is exported from the Android agent.
*
* This class is the protocol-agnostic entry point of the OpenTelemetry Android
* export configuration DSL. It lets you choose the export [protocol], a default
* collector [endpoint], common request [headers], and [compression] settings,
* as well as optional per-signal overrides for [spans], [logs], and [metrics].
*
* Typical usage from a configuration script might look like:
*
* ```kotlin
* export {
* protocol = ExportProtocol.HTTP
* endpoint = "https://collector.example.com"
* compression = Compression.GZIP
*
* spans {
* // optional per-signal overrides, e.g. custom endpoint or headers
* }
*
* logs {
* // log-specific overrides
* }
*
* metrics {
* // metric-specific overrides
* }
* }
* ```
*
* Unlike protocol-specific configuration types such as `HttpExportConfiguration`
* and `GrpcExportConfiguration`, which represent concrete connectivity details
* for a single protocol, `ExportConfiguration` is a higher-level description
* that remains independent of the underlying transport. The agent derives the
* appropriate protocol-specific configuration from this class during setup.
*/

Copilot uses AI. Check for mistakes.
@OpenTelemetryDslMarker
class ExportConfiguration internal constructor() {
var protocol: ExportProtocol = ExportProtocol.HTTP

var endpoint: String = ""

var headers: Map<String, String> = emptyMap()

Comment on lines +23 to +46
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing property documentation for protocol, endpoint, headers, and compression. These public API properties should have KDoc documentation explaining their purpose and behavior, especially the protocol property which is key to this unified configuration approach.

Suggested change
class ExportConfiguration internal constructor() {
var protocol: ExportProtocol = ExportProtocol.HTTP
var endpoint: String = ""
var headers: Map<String, String> = emptyMap()
class ExportConfiguration internal constructor() {
/**
* Export protocol to use for all telemetry signals.
*
* This value controls whether HTTP or gRPC exporters are created in [spansEndpoint],
* [logsEndpoint], and [metricsEndpoint]. Per-signal configuration currently inherits
* this global protocol selection.
*/
var protocol: ExportProtocol = ExportProtocol.HTTP
/**
* Default endpoint URL used for all telemetry signals.
*
* Each signal (`spans`, `logs`, `metrics`) can define its own endpoint in
* [EndpointConfiguration]. If a signal-specific endpoint is blank, this global
* [endpoint] is used instead via [chooseEndpoint].
*/
var endpoint: String = ""
/**
* Global HTTP headers applied to all telemetry exports.
*
* These headers are merged with signal-specific headers configured via
* [EndpointConfiguration.headers]. When merging, entries from this global [headers]
* map override signal-specific headers that use the same key.
*/
var headers: Map<String, String> = emptyMap()
/**
* Default compression algorithm for all telemetry signals.
*
* A signal can override this by setting a non-null compression in its
* [EndpointConfiguration]. If no per-signal compression is provided, this global
* [compression] value is used by [chooseCompression].
*/

Copilot uses AI. Check for mistakes.
var compression: Compression = Compression.GZIP

private val spansConfig: EndpointConfiguration = EndpointConfiguration("")
private val logsConfig: EndpointConfiguration = EndpointConfiguration("")
private val metricsConfig: EndpointConfiguration = EndpointConfiguration("")

internal fun spansEndpoint(): EndpointConnectivity =
when (protocol) {
ExportProtocol.HTTP -> {
HttpEndpointConnectivity.forTraces(
chooseEndpoint(spansConfig),
spansConfig.headers + headers,
chooseCompression(spansConfig.compression),
)
Comment on lines +62 to +66
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In spansEndpoint(), headers are merged as spansConfig.headers + headers. With Kotlin map +, keys from the right-hand map win, so global headers override per-signal headers on key conflicts. This contradicts the KDoc that says signal-specific values override top-level settings; consider reversing the merge order (global first, then signal) or updating the documentation/expectations accordingly.

Copilot uses AI. Check for mistakes.
}

ExportProtocol.GRPC -> {
GrpcEndpointConnectivity.create(
chooseEndpoint(spansConfig),
spansConfig.headers + headers,
chooseCompression(spansConfig.compression),
)
}
}

internal fun logsEndpoint(): EndpointConnectivity =
when (protocol) {
ExportProtocol.HTTP -> {
HttpEndpointConnectivity.forLogs(
chooseEndpoint(logsConfig),
logsConfig.headers + headers,
chooseCompression(logsConfig.compression),
)
Comment on lines +81 to +85
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In logsEndpoint(), the merge order logsConfig.headers + headers causes global headers to override per-signal headers when keys collide. If per-signal configuration is intended to override, reverse the merge order or adjust the docs to match the implemented precedence.

Copilot uses AI. Check for mistakes.
}

ExportProtocol.GRPC -> {
GrpcEndpointConnectivity.create(
chooseEndpoint(logsConfig),
logsConfig.headers + headers,
chooseCompression(logsConfig.compression),
)
}
}

internal fun metricsEndpoint(): EndpointConnectivity =
when (protocol) {
ExportProtocol.HTTP -> {
HttpEndpointConnectivity.forMetrics(
chooseEndpoint(metricsConfig),
metricsConfig.headers + headers,
chooseCompression(metricsConfig.compression),
)
Comment on lines +100 to +104
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In metricsEndpoint(), metricsConfig.headers + headers makes global headers win on duplicate keys, which is the opposite of the stated override semantics in the method KDoc. Consider swapping the operands (or updating docs/tests) so per-signal headers can override global ones.

Copilot uses AI. Check for mistakes.
}

ExportProtocol.GRPC -> {
GrpcEndpointConnectivity.create(
chooseEndpoint(metricsConfig),
metricsConfig.headers + headers,
chooseCompression(metricsConfig.compression),
)
}
}

private fun chooseEndpoint(cfg: EndpointConfiguration): String = cfg.url.ifBlank { endpoint }

private fun chooseCompression(signalConfigCompression: Compression?): Compression = signalConfigCompression ?: this.compression

fun spans(action: EndpointConfiguration.() -> Unit) {
spansConfig.action()
}

fun logs(action: EndpointConfiguration.() -> Unit) {
logsConfig.action()
}

Comment on lines +119 to +139
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing documentation for the spans, logs, and metrics functions. These public API functions should have KDoc documentation explaining their purpose and usage in the context of the unified export configuration.

Suggested change
fun spans(action: EndpointConfiguration.() -> Unit) {
spansConfig.action()
}
fun logs(action: EndpointConfiguration.() -> Unit) {
logsConfig.action()
}
/**
* Configures span (trace) export settings for this unified export configuration.
*
* Use this to customize the endpoint URL, headers, and compression for span exports
* independently of the values defined by [endpoint], [headers], and [compression].
* Any values set inside [action] override the corresponding top-level settings
* when exporting spans.
*
* @param action DSL block applied to the span [EndpointConfiguration].
*/
fun spans(action: EndpointConfiguration.() -> Unit) {
spansConfig.action()
}
/**
* Configures log export settings for this unified export configuration.
*
* Use this to customize the endpoint URL, headers, and compression for log exports
* independently of the values defined by [endpoint], [headers], and [compression].
* Any values set inside [action] override the corresponding top-level settings
* when exporting logs.
*
* @param action DSL block applied to the log [EndpointConfiguration].
*/
fun logs(action: EndpointConfiguration.() -> Unit) {
logsConfig.action()
}
/**
* Configures metric export settings for this unified export configuration.
*
* Use this to customize the endpoint URL, headers, and compression for metric exports
* independently of the values defined by [endpoint], [headers], and [compression].
* Any values set inside [action] override the corresponding top-level settings
* when exporting metrics.
*
* @param action DSL block applied to the metric [EndpointConfiguration].
*/

Copilot uses AI. Check for mistakes.
fun metrics(action: EndpointConfiguration.() -> Unit) {
metricsConfig.action()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.android.agent.dsl

import io.opentelemetry.android.agent.connectivity.Compression
import io.opentelemetry.android.agent.connectivity.GrpcEndpointConnectivity

Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing class-level documentation for GrpcExportConfiguration. This public API class should have KDoc documentation explaining its purpose, usage, and relationship to other export configuration classes, following the pattern established in HttpExportConfiguration (see HttpExportConfiguration.kt:11-13).

Suggested change
/**
* Configuration for exporting telemetry over gRPC from the Android agent DSL.
*
* This mirrors [HttpExportConfiguration] but targets gRPC exporters and is used from
* the `export { grpc { ... } }` section of the agent configuration DSL.
*
* Use [endpoint], [headers], and [compression] for common settings, and
* [spans], [logs], and [metrics] to override per-signal endpoints, headers,
* or compression as needed.
*/

Copilot uses AI. Check for mistakes.
@OpenTelemetryDslMarker
class GrpcExportConfiguration internal constructor() {
var endpoint: String = ""
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property name endpoint is inconsistent with the naming convention used in HttpExportConfiguration, which uses baseUrl for the same purpose. For API consistency and clarity, consider renaming this property to baseEndpoint to match the pattern established by baseUrl and baseHeaders in HttpExportConfiguration (see HttpExportConfiguration.kt:19).

Copilot uses AI. Check for mistakes.

var headers: Map<String, String> = emptyMap()
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property name headers is inconsistent with the naming convention used in HttpExportConfiguration, which uses baseHeaders for the same purpose. For API consistency, consider renaming this property to baseHeaders to match the pattern established in HttpExportConfiguration (see HttpExportConfiguration.kt:24).

Copilot uses AI. Check for mistakes.

Comment on lines +18 to +34
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing property documentation for endpoint, headers, and compression. These public API properties should have KDoc documentation explaining their purpose and behavior, following the pattern established in HttpExportConfiguration (see HttpExportConfiguration.kt:16-28).

Suggested change
class GrpcExportConfiguration internal constructor() {
var endpoint: String = ""
var headers: Map<String, String> = emptyMap()
class GrpcExportConfiguration internal constructor() {
/**
* The base gRPC endpoint to which telemetry will be exported.
*
* This value is used for all signals (spans, logs, metrics) unless a
* signal-specific endpoint is configured via [spans], [logs], or [metrics].
*/
var endpoint: String = ""
/**
* Headers that will be sent with every gRPC export request.
*
* These headers are combined with any headers configured on the per-signal
* [EndpointConfiguration] instances used by [spans], [logs], and [metrics].
*/
var headers: Map<String, String> = emptyMap()
/**
* The compression algorithm to use for gRPC export requests.
*
* This acts as the default compression for all signals and may be overridden
* in the per-signal [EndpointConfiguration], if supported.
*/

Copilot uses AI. Check for mistakes.
var compression: Compression = Compression.GZIP

private val spansConfig: EndpointConfiguration = EndpointConfiguration("")
private val logsConfig: EndpointConfiguration = EndpointConfiguration("")
private val metricsConfig: EndpointConfiguration = EndpointConfiguration("")

internal fun spansEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(spansConfig),
spansConfig.headers + headers,
chooseCompression(spansConfig.compression),
)

internal fun logsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(logsConfig),
logsConfig.headers + headers,
chooseCompression(logsConfig.compression),
)

internal fun metricsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(metricsConfig),
metricsConfig.headers + headers,
Comment on lines +50 to +64
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In spansEndpoint(), headers are merged as spansConfig.headers + headers, which means the top-level headers override per-signal headers on key conflicts (right-hand side wins for Kotlin map +). This conflicts with the KDoc stating per-signal values override; consider reversing the merge order or updating the KDoc to reflect actual behavior.

Suggested change
spansConfig.headers + headers,
chooseCompression(spansConfig.compression),
)
internal fun logsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(logsConfig),
logsConfig.headers + headers,
chooseCompression(logsConfig.compression),
)
internal fun metricsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(metricsConfig),
metricsConfig.headers + headers,
headers + spansConfig.headers,
chooseCompression(spansConfig.compression),
)
internal fun logsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(logsConfig),
headers + logsConfig.headers,
chooseCompression(logsConfig.compression),
)
internal fun metricsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(metricsConfig),
headers + metricsConfig.headers,

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +64
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In logsEndpoint(), the current merge order logsConfig.headers + headers makes global headers take precedence over per-signal headers for duplicate keys. If per-signal overrides are intended, swap the merge order (or adjust documentation accordingly).

Suggested change
spansConfig.headers + headers,
chooseCompression(spansConfig.compression),
)
internal fun logsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(logsConfig),
logsConfig.headers + headers,
chooseCompression(logsConfig.compression),
)
internal fun metricsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(metricsConfig),
metricsConfig.headers + headers,
headers + spansConfig.headers,
chooseCompression(spansConfig.compression),
)
internal fun logsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(logsConfig),
headers + logsConfig.headers,
chooseCompression(logsConfig.compression),
)
internal fun metricsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(metricsConfig),
headers + metricsConfig.headers,

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +64
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In metricsEndpoint(), metricsConfig.headers + headers causes global headers to override per-signal headers on key collision, which is inconsistent with the documented override behavior. Consider reversing the merge order or updating docs/tests to match precedence.

Suggested change
spansConfig.headers + headers,
chooseCompression(spansConfig.compression),
)
internal fun logsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(logsConfig),
logsConfig.headers + headers,
chooseCompression(logsConfig.compression),
)
internal fun metricsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(metricsConfig),
metricsConfig.headers + headers,
headers + spansConfig.headers,
chooseCompression(spansConfig.compression),
)
internal fun logsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(logsConfig),
headers + logsConfig.headers,
chooseCompression(logsConfig.compression),
)
internal fun metricsEndpoint(): GrpcEndpointConnectivity =
GrpcEndpointConnectivity.create(
chooseEndpoint(metricsConfig),
headers + metricsConfig.headers,

Copilot uses AI. Check for mistakes.
chooseCompression(metricsConfig.compression),
)

private fun chooseEndpoint(cfg: EndpointConfiguration): String = cfg.url.ifBlank { endpoint }

private fun chooseCompression(signalConfigCompression: Compression?): Compression = signalConfigCompression ?: this.compression

fun spans(action: EndpointConfiguration.() -> Unit) {
spansConfig.action()
}

fun logs(action: EndpointConfiguration.() -> Unit) {
logsConfig.action()
}

Comment on lines +71 to +91
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing documentation for the spans, logs, and metrics functions. These public API functions should have KDoc documentation explaining their purpose, following the pattern established in HttpExportConfiguration (see HttpExportConfiguration.kt:60-79).

Suggested change
fun spans(action: EndpointConfiguration.() -> Unit) {
spansConfig.action()
}
fun logs(action: EndpointConfiguration.() -> Unit) {
logsConfig.action()
}
/**
* Configures export options specific to span data for this gRPC exporter.
*
* Values set in this configuration override the top-level [endpoint],
* [headers], and [compression] for span exports.
*
* @param action configuration block applied to the spans [EndpointConfiguration].
*/
fun spans(action: EndpointConfiguration.() -> Unit) {
spansConfig.action()
}
/**
* Configures export options specific to log data for this gRPC exporter.
*
* Values set in this configuration override the top-level [endpoint],
* [headers], and [compression] for log exports.
*
* @param action configuration block applied to the logs [EndpointConfiguration].
*/
fun logs(action: EndpointConfiguration.() -> Unit) {
logsConfig.action()
}
/**
* Configures export options specific to metric data for this gRPC exporter.
*
* Values set in this configuration override the top-level [endpoint],
* [headers], and [compression] for metric exports.
*
* @param action configuration block applied to the metrics [EndpointConfiguration].
*/

Copilot uses AI. Check for mistakes.
fun metrics(action: EndpointConfiguration.() -> Unit) {
metricsConfig.action()
}
}
Loading
Loading