Browse Source

Add support for SslBundles to OTLP logging export

Closes gh-49583
dependabot/npm_and_yarn/antora/springio/asciidoctor-extensions-1.0.0-alpha.18
Moritz Halbritter 6 days ago
parent
commit
166b88b9e1
  1. 11
      module/spring-boot-opentelemetry/src/dockerTest/java/org/springframework/boot/opentelemetry/docker/compose/GrafanaOtlpLoggingDockerComposeConnectionDetailsFactoryTests.java
  2. 11
      module/spring-boot-opentelemetry/src/dockerTest/java/org/springframework/boot/opentelemetry/docker/compose/OtelCollectorOtlpLoggingDockerComposeConnectionDetailsFactoryTests.java
  3. 1
      module/spring-boot-opentelemetry/src/dockerTest/java/org/springframework/boot/opentelemetry/testcontainers/GrafanaOtlpLoggingContainerConnectionDetailsFactoryTests.java
  4. 1
      module/spring-boot-opentelemetry/src/dockerTest/java/org/springframework/boot/opentelemetry/testcontainers/OtelCollectorOltpLoggingContainerConnectionDetailsFactoryTests.java
  5. 32
      module/spring-boot-opentelemetry/src/dockerTest/resources/org/springframework/boot/opentelemetry/docker/compose/ca.crt
  6. 8
      module/spring-boot-opentelemetry/src/dockerTest/resources/org/springframework/boot/opentelemetry/docker/compose/otlp-ssl-compose.yaml
  7. 55
      module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java
  8. 12
      module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConnectionDetails.java
  9. 23
      module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingProperties.java
  10. 14
      module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/docker/compose/OtlpLoggingDockerComposeConnectionDetailsFactory.java
  11. 10
      module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/testcontainers/GrafanaOtlpLoggingContainerConnectionDetailsFactory.java
  12. 10
      module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/testcontainers/OtelCollectorOltpLoggingContainerConnectionDetailsFactory.java
  13. 55
      module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationTests.java
  14. BIN
      module/spring-boot-opentelemetry/src/test/resources/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/test.jks

11
module/spring-boot-opentelemetry/src/dockerTest/java/org/springframework/boot/opentelemetry/docker/compose/GrafanaOtlpLoggingDockerComposeConnectionDetailsFactoryTests.java

@ -19,6 +19,7 @@ package org.springframework.boot.opentelemetry.docker.compose; @@ -19,6 +19,7 @@ package org.springframework.boot.opentelemetry.docker.compose;
import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.Transport;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.testsupport.container.TestImage;
import static org.assertj.core.api.Assertions.assertThat;
@ -35,6 +36,16 @@ class GrafanaOtlpLoggingDockerComposeConnectionDetailsFactoryTests { @@ -35,6 +36,16 @@ class GrafanaOtlpLoggingDockerComposeConnectionDetailsFactoryTests {
void runCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) {
assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("http://").endsWith("/v1/logs");
assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("http://").endsWith("/v1/logs");
assertThat(connectionDetails.getSslBundle()).isNull();
}
@DockerComposeTest(composeFile = "otlp-ssl-compose.yaml", image = TestImage.GRAFANA_OTEL_LGTM,
additionalResources = "ca.crt")
void runWithSslCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) {
assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("https://").endsWith("/v1/logs");
assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("https://").endsWith("/v1/logs");
SslBundle sslBundle = connectionDetails.getSslBundle();
assertThat(sslBundle).isNotNull();
}
}

11
module/spring-boot-opentelemetry/src/dockerTest/java/org/springframework/boot/opentelemetry/docker/compose/OtelCollectorOtlpLoggingDockerComposeConnectionDetailsFactoryTests.java

@ -19,6 +19,7 @@ package org.springframework.boot.opentelemetry.docker.compose; @@ -19,6 +19,7 @@ package org.springframework.boot.opentelemetry.docker.compose;
import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.Transport;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.testsupport.container.TestImage;
import static org.assertj.core.api.Assertions.assertThat;
@ -34,6 +35,16 @@ class OtelCollectorOtlpLoggingDockerComposeConnectionDetailsFactoryTests { @@ -34,6 +35,16 @@ class OtelCollectorOtlpLoggingDockerComposeConnectionDetailsFactoryTests {
void runCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) {
assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("http://").endsWith("/v1/logs");
assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("http://").endsWith("/v1/logs");
assertThat(connectionDetails.getSslBundle()).isNull();
}
@DockerComposeTest(composeFile = "otlp-ssl-compose.yaml", image = TestImage.OTEL_COLLECTOR,
additionalResources = "ca.crt")
void runWithSslCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) {
assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("https://").endsWith("/v1/logs");
assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("https://").endsWith("/v1/logs");
SslBundle sslBundle = connectionDetails.getSslBundle();
assertThat(sslBundle).isNotNull();
}
}

1
module/spring-boot-opentelemetry/src/dockerTest/java/org/springframework/boot/opentelemetry/testcontainers/GrafanaOtlpLoggingContainerConnectionDetailsFactoryTests.java

@ -55,6 +55,7 @@ class GrafanaOtlpLoggingContainerConnectionDetailsFactoryTests { @@ -55,6 +55,7 @@ class GrafanaOtlpLoggingContainerConnectionDetailsFactoryTests {
.isEqualTo("%s/v1/logs".formatted(container.getOtlpGrpcUrl()));
assertThat(this.connectionDetails.getUrl(Transport.HTTP))
.isEqualTo("%s/v1/logs".formatted(container.getOtlpHttpUrl()));
assertThat(this.connectionDetails.getSslBundle()).isNull();
}
@Configuration(proxyBeanMethods = false)

1
module/spring-boot-opentelemetry/src/dockerTest/java/org/springframework/boot/opentelemetry/testcontainers/OtelCollectorOltpLoggingContainerConnectionDetailsFactoryTests.java

@ -56,6 +56,7 @@ class OtelCollectorOltpLoggingContainerConnectionDetailsFactoryTests { @@ -56,6 +56,7 @@ class OtelCollectorOltpLoggingContainerConnectionDetailsFactoryTests {
.isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4318) + "/v1/logs");
assertThat(this.connectionDetails.getUrl(Transport.GRPC))
.isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4317) + "/v1/logs");
assertThat(this.connectionDetails.getSslBundle()).isNull();
}
@Configuration(proxyBeanMethods = false)

32
module/spring-boot-opentelemetry/src/dockerTest/resources/org/springframework/boot/opentelemetry/docker/compose/ca.crt

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFhjCCA26gAwIBAgIUfIkk29IT9OpbgfjL8oRIPSLjUcAwDQYJKoZIhvcNAQEL
BQAwOzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDEeMBwGA1UEAwwVQ2VydGlm
aWNhdGUgQXV0aG9yaXR5MB4XDTI0MDUwMTE2NTMyNVoXDTM0MDQyOTE2NTMyNVow
OzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDEeMBwGA1UEAwwVQ2VydGlmaWNh
dGUgQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAusN2
KzQQUUxZSiI3ZZuZohFwq2KXSUNPdJ6rgD3/YKNTDSZXKZPO53kYPP0DXf0sm3CH
cyWSWVabyimZYuPWena1MElSL4ZpJ9WwkZoOQ3bPFK1utz6kMOwrgAUcky8H/rIK
j2JEBhkSHUIGr57NjUEwG1ygaSerM8RzWw1PtMq+C8LOu3v94qzE3NDg1QRpyvV9
OmsLsjISd0ZmAJNi9vmiEH923KnPyiqnQmWKpYicdgQmX1GXylS22jZqAwaOkYGj
X8UdeyvrohkZkM0hn9uaSufQGEW4yKACn3PkjJtzi8drBIyjIi9YcAzBxZB9oVKq
XZMlltgO2fDMmIJi0Ngt0Ci7fCoEMqSocKyDKML6YLr9UWtx4bfsrk+rVO9Q/D/v
8RKgstv7dCf2KWRX3ZJEC0IBHS5gLNq0qqqVcGx3LcSyhdiKJOtSwAnNkHMh+jSQ
xLSlBjcSqTPiGTRK/Rddl+xnU/mBgk7ZBGNrUFaD5McMFjddS7Ih82aHnpQ1gekW
nUGv+Tm/G68h2BvZ5U2q+RfeOCgRW9i/AYW2jgT7IFnfjyUXgBQveauMAchomqFE
VLe95ZgViF6vmH34EKo3w9L5TQiwk/r53YlM7TSOTyDqx66t4zGYDsVMicpKmzi4
2Rp8EpErARRyREUIKSvWs9O9+uT3+7arNLgHe5ECAwEAAaOBgTB/MB0GA1UdDgQW
BBRVMLDVqPECWaH6GruL9E52VcTrPjAfBgNVHSMEGDAWgBRVMLDVqPECWaH6GruL
9E52VcTrPjAPBgNVHRMBAf8EBTADAQH/MCwGA1UdEQQlMCOCC2V4YW1wbGUuY29t
gglsb2NhbGhvc3SCCTEyNy4wLjAuMTANBgkqhkiG9w0BAQsFAAOCAgEAeSpjCL3j
2GIFBNKr/5amLOYa0kZ6r1dJs+K6xvMsUvsBJ/QQsV5nYDMIoV/NYUd8SyYV4lEj
7LHX5ZbmJrvPk30LGEBG/5Vy2MIATrQrQ14S4nXtEdSnBvTQwPOOaHc+2dTp3YpM
f4ffELKWyispTifx1eqdiUJhURKeQBh+3W7zpyaiN4vJaqEDKGgFQtHA/OyZL2hZ
BpxHB0zpb2iDHV8MeyfOT7HQWUk6p13vdYm6EnyJT8fzWvE+TqYNbqFmB+CLRSXy
R3p1yaeTd4LnVknJ0UBKqEyul3ziHZDhKhBpwdglYOQz4eWjSFhikX9XZ8NaI38Q
QqLZVn0DsH2ztkjrQrUVgK2xn4aUuqoLDk4Hu6h5baUn+f2GLuzx+EXc/i3ikYvw
Y3JyufOgw6nGGFG+/QXEj85XtLPhN7Wm42z2e/BGzi0MLl65sfpEDXvFTA72Yzws
OYaeg/HxeYwUHQgs2fKl/LgV4chntSCvTqfNl6OnQafD/ISJNpx3xWR3HwF+ypFG
UaLE+e1soqEJbzL31U/6pypHLsj8Y8r9hJbZXo2ibnhjFV6fypUAP0rbIzaoWcrJ
T0Sbliz+KQTMzCcubiAi4bI/kZ5FJ4kkaHqUpIWzlx1h2WVJ65ASFDjBWb8eVmB6
Dyno/RVFR/rUL5091gjGRXhLsi1oUHKdEzU=
-----END CERTIFICATE-----

8
module/spring-boot-opentelemetry/src/dockerTest/resources/org/springframework/boot/opentelemetry/docker/compose/otlp-ssl-compose.yaml

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
services:
otlp:
image: '{imageName}'
ports:
- '4317'
- '4318'
labels:
- 'org.springframework.boot.sslbundle.pem.truststore.certificate=ca.crt'

55
module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java

@ -18,11 +18,16 @@ package org.springframework.boot.opentelemetry.autoconfigure.logging.otlp; @@ -18,11 +18,16 @@ package org.springframework.boot.opentelemetry.autoconfigure.logging.otlp;
import java.util.Locale;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@ -30,9 +35,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -30,9 +35,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.opentelemetry.autoconfigure.logging.ConditionalOnEnabledLoggingExport;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Configurations imported by {@link OtlpLoggingAutoConfiguration}.
@ -48,8 +56,9 @@ final class OtlpLoggingConfigurations { @@ -48,8 +56,9 @@ final class OtlpLoggingConfigurations {
@Bean
@ConditionalOnMissingBean(OtlpLoggingConnectionDetails.class)
@ConditionalOnProperty("management.opentelemetry.logging.export.otlp.endpoint")
PropertiesOtlpLoggingConnectionDetails openTelemetryLoggingConnectionDetails(OtlpLoggingProperties properties) {
return new PropertiesOtlpLoggingConnectionDetails(properties);
PropertiesOtlpLoggingConnectionDetails openTelemetryLoggingConnectionDetails(OtlpLoggingProperties properties,
ObjectProvider<SslBundles> sslBundles) {
return new PropertiesOtlpLoggingConnectionDetails(properties, sslBundles.getIfAvailable());
}
/**
@ -59,8 +68,11 @@ final class OtlpLoggingConfigurations { @@ -59,8 +68,11 @@ final class OtlpLoggingConfigurations {
private final OtlpLoggingProperties properties;
PropertiesOtlpLoggingConnectionDetails(OtlpLoggingProperties properties) {
private final @Nullable SslBundles sslBundles;
PropertiesOtlpLoggingConnectionDetails(OtlpLoggingProperties properties, @Nullable SslBundles sslBundles) {
this.properties = properties;
this.sslBundles = sslBundles;
}
@Override
@ -73,6 +85,16 @@ final class OtlpLoggingConfigurations { @@ -73,6 +85,16 @@ final class OtlpLoggingConfigurations {
return endpoint;
}
@Override
public @Nullable SslBundle getSslBundle() {
String bundleName = this.properties.getSsl().getBundle();
if (StringUtils.hasLength(bundleName)) {
Assert.notNull(this.sslBundles, "SSL bundle name has been set but no SSL bundles found in context");
return this.sslBundles.getBundle(bundleName);
}
return null;
}
}
}
@ -97,6 +119,7 @@ final class OtlpLoggingConfigurations { @@ -97,6 +119,7 @@ final class OtlpLoggingConfigurations {
.setCompression(properties.getCompression().name().toLowerCase(Locale.US));
properties.getHeaders().forEach(builder::addHeader);
meterProvider.ifAvailable(builder::setMeterProvider);
configureSsl(connectionDetails, builder::setSslContext);
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build();
}
@ -113,10 +136,36 @@ final class OtlpLoggingConfigurations { @@ -113,10 +136,36 @@ final class OtlpLoggingConfigurations {
.setCompression(properties.getCompression().name().toLowerCase(Locale.US));
properties.getHeaders().forEach(builder::addHeader);
meterProvider.ifAvailable(builder::setMeterProvider);
configureSsl(connectionDetails, builder::setSslContext);
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build();
}
private void configureSsl(OtlpLoggingConnectionDetails connectionDetails,
SslContextConfigurer sslContextConfigurer) {
SslBundle sslBundle = connectionDetails.getSslBundle();
if (sslBundle != null) {
SSLContext sslContext = sslBundle.createSslContext();
X509TrustManager trustManager = extractTrustManager(sslBundle);
sslContextConfigurer.configure(sslContext, trustManager);
}
}
private X509TrustManager extractTrustManager(SslBundle sslBundle) {
for (TrustManager trustManager : sslBundle.getManagers().getTrustManagers()) {
if (trustManager instanceof X509TrustManager x509TrustManager) {
return x509TrustManager;
}
}
throw new IllegalStateException("No X509TrustManager found in the SSL bundle trust managers");
}
private interface SslContextConfigurer {
void configure(SSLContext sslContext, X509TrustManager trustManager);
}
}
}

12
module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConnectionDetails.java

@ -16,7 +16,10 @@ @@ -16,7 +16,10 @@
package org.springframework.boot.opentelemetry.autoconfigure.logging.otlp;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.ssl.SslBundle;
/**
* Details required to establish a connection to an OpenTelemetry logging service.
@ -33,4 +36,13 @@ public interface OtlpLoggingConnectionDetails extends ConnectionDetails { @@ -33,4 +36,13 @@ public interface OtlpLoggingConnectionDetails extends ConnectionDetails {
*/
String getUrl(Transport transport);
/**
* SSL bundle to use.
* @return the SSL bundle to use or {@code null}
* @since 4.1.0
*/
default @Nullable SslBundle getSslBundle() {
return null;
}
}

23
module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingProperties.java

@ -66,6 +66,8 @@ public class OtlpLoggingProperties { @@ -66,6 +66,8 @@ public class OtlpLoggingProperties {
*/
private final Map<String, String> headers = new HashMap<>();
private final Ssl ssl = new Ssl();
public @Nullable String getEndpoint() {
return this.endpoint;
}
@ -110,6 +112,27 @@ public class OtlpLoggingProperties { @@ -110,6 +112,27 @@ public class OtlpLoggingProperties {
return this.headers;
}
public Ssl getSsl() {
return this.ssl;
}
public static class Ssl {
/**
* SSL bundle name.
*/
private @Nullable String bundle;
public @Nullable String getBundle() {
return this.bundle;
}
public void setBundle(@Nullable String bundle) {
this.bundle = bundle;
}
}
public enum Compression {
/**

14
module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/docker/compose/OtlpLoggingDockerComposeConnectionDetailsFactory.java

@ -16,11 +16,14 @@ @@ -16,11 +16,14 @@
package org.springframework.boot.opentelemetry.docker.compose;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.docker.compose.core.RunningService;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.Transport;
import org.springframework.boot.ssl.SslBundle;
/**
* {@link DockerComposeConnectionDetailsFactory} to create
@ -57,11 +60,14 @@ class OtlpLoggingDockerComposeConnectionDetailsFactory @@ -57,11 +60,14 @@ class OtlpLoggingDockerComposeConnectionDetailsFactory
private final int httpPort;
private final @Nullable SslBundle sslBundle;
private OtlpLoggingDockerComposeConnectionDetails(RunningService source) {
super(source);
this.host = source.host();
this.grpcPort = source.ports().get(OTLP_GRPC_PORT);
this.httpPort = source.ports().get(OTLP_HTTP_PORT);
this.sslBundle = getSslBundle(source);
}
@Override
@ -70,7 +76,13 @@ class OtlpLoggingDockerComposeConnectionDetailsFactory @@ -70,7 +76,13 @@ class OtlpLoggingDockerComposeConnectionDetailsFactory
case HTTP -> this.httpPort;
case GRPC -> this.grpcPort;
};
return "http://%s:%d/v1/logs".formatted(this.host, port);
String scheme = (this.sslBundle != null) ? "https" : "http";
return "%s://%s:%d/v1/logs".formatted(scheme, this.host, port);
}
@Override
public @Nullable SslBundle getSslBundle() {
return this.sslBundle;
}
}

10
module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/testcontainers/GrafanaOtlpLoggingContainerConnectionDetailsFactory.java

@ -16,10 +16,12 @@ @@ -16,10 +16,12 @@
package org.springframework.boot.opentelemetry.testcontainers;
import org.jspecify.annotations.Nullable;
import org.testcontainers.grafana.LgtmStackContainer;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.Transport;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
@ -58,9 +60,17 @@ class GrafanaOtlpLoggingContainerConnectionDetailsFactory @@ -58,9 +60,17 @@ class GrafanaOtlpLoggingContainerConnectionDetailsFactory
case HTTP -> getContainer().getOtlpHttpUrl();
case GRPC -> getContainer().getOtlpGrpcUrl();
};
if (getSslBundle() != null) {
url = url.replaceFirst("^http://", "https://");
}
return "%s/v1/logs".formatted(url);
}
@Override
public @Nullable SslBundle getSslBundle() {
return super.getSslBundle();
}
}
}

10
module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/testcontainers/OtelCollectorOltpLoggingContainerConnectionDetailsFactory.java

@ -16,11 +16,13 @@ @@ -16,11 +16,13 @@
package org.springframework.boot.opentelemetry.testcontainers;
import org.jspecify.annotations.Nullable;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.GenericContainer;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.Transport;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
@ -64,7 +66,13 @@ class OtelCollectorOltpLoggingContainerConnectionDetailsFactory @@ -64,7 +66,13 @@ class OtelCollectorOltpLoggingContainerConnectionDetailsFactory
case HTTP -> OTLP_HTTP_PORT;
case GRPC -> OTLP_GRPC_PORT;
};
return "http://%s:%d/v1/logs".formatted(getContainer().getHost(), getContainer().getMappedPort(port));
String scheme = (getSslBundle() != null) ? "https" : "http";
return "%s://%s:%d/v1/logs".formatted(scheme, getContainer().getHost(), getContainer().getMappedPort(port));
}
@Override
public @Nullable SslBundle getSslBundle() {
return super.getSslBundle();
}
}

55
module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationTests.java

@ -20,6 +20,8 @@ import java.time.Duration; @@ -20,6 +20,8 @@ import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import javax.net.ssl.X509TrustManager;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
@ -33,13 +35,16 @@ import org.junit.jupiter.params.provider.ValueSource; @@ -33,13 +35,16 @@ import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
import org.springframework.boot.context.annotation.ImportCandidates;
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration;
import org.springframework.boot.opentelemetry.autoconfigure.logging.OpenTelemetryLoggingAutoConfiguration;
import org.springframework.boot.opentelemetry.autoconfigure.logging.SdkLoggerProviderBuilderCustomizer;
import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConfigurations.ConnectionDetails.PropertiesOtlpLoggingConnectionDetails;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.testsupport.classpath.resources.WithPackageResources;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -266,6 +271,56 @@ class OtlpLoggingAutoConfigurationTests { @@ -266,6 +271,56 @@ class OtlpLoggingAutoConfigurationTests {
});
}
@Test
@WithPackageResources("test.jks")
void whenHasSslBundleConfiguresHttpExporter() {
this.contextRunner.withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class))
.withPropertyValues("management.opentelemetry.logging.export.otlp.endpoint=https://localhost:4318/v1/logs",
"management.opentelemetry.logging.export.otlp.ssl.bundle=mybundle",
"spring.ssl.bundle.jks.mybundle.truststore.location=classpath:test.jks")
.run((context) -> {
assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class);
OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class);
assertThat(exporter).extracting("delegate.httpSender.client.sslSocketFactoryOrNull").isNotNull();
assertThat(exporter).extracting("delegate.httpSender.client.x509TrustManager")
.isInstanceOf(X509TrustManager.class);
});
}
@Test
@WithPackageResources("test.jks")
void whenHasSslBundleConfiguresGrpcExporter() {
this.contextRunner.withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class))
.withPropertyValues("management.opentelemetry.logging.export.otlp.endpoint=https://localhost:4318/v1/logs",
"management.opentelemetry.logging.export.otlp.transport=grpc",
"management.opentelemetry.logging.export.otlp.ssl.bundle=mybundle",
"spring.ssl.bundle.jks.mybundle.truststore.location=classpath:test.jks")
.run((context) -> {
assertThat(context).hasSingleBean(OtlpGrpcLogRecordExporter.class);
OtlpGrpcLogRecordExporter exporter = context.getBean(OtlpGrpcLogRecordExporter.class);
assertThat(exporter).extracting("delegate.grpcSender.client.sslSocketFactoryOrNull").isNotNull();
assertThat(exporter).extracting("delegate.grpcSender.client.x509TrustManager")
.isInstanceOf(X509TrustManager.class);
});
}
@Test
void whenCustomConnectionDetailsProvidesSslBundleConfiguresHttpExporter() {
SslBundle sslBundle = SslBundle.systemDefault();
OtlpLoggingConnectionDetails connectionDetails = mock(OtlpLoggingConnectionDetails.class);
given(connectionDetails.getUrl(Transport.HTTP)).willReturn("https://localhost:4318/v1/logs");
given(connectionDetails.getSslBundle()).willReturn(sslBundle);
this.contextRunner
.withBean("customOtlpLoggingConnectionDetails", OtlpLoggingConnectionDetails.class, () -> connectionDetails)
.run((context) -> {
assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class);
OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class);
assertThat(exporter).extracting("delegate.httpSender.client.sslSocketFactoryOrNull").isNotNull();
assertThat(exporter).extracting("delegate.httpSender.client.x509TrustManager")
.isInstanceOf(X509TrustManager.class);
});
}
@Configuration(proxyBeanMethods = false)
public static class MultipleSdkLoggerProviderBuilderCustomizersConfig {

BIN
module/spring-boot-opentelemetry/src/test/resources/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/test.jks

Binary file not shown.
Loading…
Cancel
Save