Skip to content
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ All in all, you should do the following:
4. Adjust your logging configuration accordingly.

Let's say you want to make use of the *servlet filter* feature, then you need to add the following dependency to your
POM with property `cf-logging-version` referring to the latest nexus version (currently `4.0.0`):
POM with property `cf-logging-version` referring to the latest nexus version (currently `4.0.1`):

```xml

<properties>
<cf-logging-version>4.0.0</cf-logging-version>
<cf-logging-version>4.0.1</cf-logging-version>
</properties>
```

Expand Down
2 changes: 1 addition & 1 deletion cf-java-logging-support-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.sap.hcp.cf.logging</groupId>
<artifactId>cf-java-logging-support-parent</artifactId>
<version>4.0.0</version>
<version>4.0.1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<build>
Expand Down
2 changes: 1 addition & 1 deletion cf-java-logging-support-log4j2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<relativePath>../pom.xml</relativePath>
<groupId>com.sap.hcp.cf.logging</groupId>
<artifactId>cf-java-logging-support-parent</artifactId>
<version>4.0.0</version>
<version>4.0.1</version>
</parent>

<dependencies>
Expand Down
2 changes: 1 addition & 1 deletion cf-java-logging-support-logback/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<relativePath>../pom.xml</relativePath>
<groupId>com.sap.hcp.cf.logging</groupId>
<artifactId>cf-java-logging-support-parent</artifactId>
<version>4.0.0</version>
<version>4.0.1</version>
</parent>

<dependencies>
Expand Down
48 changes: 46 additions & 2 deletions cf-java-logging-support-opentelemetry-agent-extension/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,57 @@
<packaging>jar</packaging>

<name>cf-java-logging-support-opentelemetry-agent-extension</name>

<description>OpenTelemetry Java Agent Extension for auto-configuration of SAP BTP services.</description>
<parent>
<artifactId>cf-java-logging-support-parent</artifactId>
<groupId>com.sap.hcp.cf.logging</groupId>
<version>4.0.0</version>
<version>4.0.1</version>
<relativePath>../pom.xml</relativePath>
</parent>

<url>https://github.com/SAP/cf-java-logging-support</url>
<organization>
<name>SAP SE</name>
<url>https://www.sap.com</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>

<developers>
<developer>
<id>KarstenSchnitter</id>
<name>Karsten Schnitter</name>
<email>k.schnitter@sap.com</email>
<organization>SAP SE</organization>
<organizationUrl>https://github.com/SAP</organizationUrl>
</developer>
<developer>
<id>WolfgangTheilmann</id>
<name>Wolfgang Theilmann</name>
<email>wolfgang.theilmann@sap.com</email>
<organization>SAP SE</organization>
<organizationUrl>https://github.com/SAP</organizationUrl>
</developer>
<developer>
<id>HariG</id>
<name>Hariharan Gandhi</name>
<email>hariharan.gandhi@sap.com</email>
<organization>SAP SE</organization>
<organizationUrl>https://github.com/SAP</organizationUrl>
</developer>
</developers>

<scm>
<connection>scm:git:git@github.com:SAP/cf-java-logging-support.git</connection>
<developerConnection>scm:git:git@github.com:SAP/cf-java-logging-support.git</developerConnection>
<url>git@github.com:SAP/cf-java-logging-support.git</url>
</scm>

<properties>
<opentelemetry.sdk.version>1.55.0</opentelemetry.sdk.version>
</properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.sap.hcf.cf.logging.opentelemetry.agent.ext;

import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudLoggingBindingPropertiesSupplier;
import com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter.SanitizeSpanExporterCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;

Expand All @@ -9,14 +10,13 @@
public class CloudLoggingConfigurationCustomizerProvider implements AutoConfigurationCustomizerProvider {

private static final Logger LOG = Logger.getLogger(CloudLoggingConfigurationCustomizerProvider.class.getName());
private static final String VERSION = "4.0.0";
private static final String VERSION = "4.0.1";

@Override
public void customize(AutoConfigurationCustomizer autoConfiguration) {
LOG.info("Initializing SAP BTP Observability extension " + VERSION);
autoConfiguration.addPropertiesSupplier(new CloudLoggingBindingPropertiesSupplier());

// ConfigurableLogRecordExporterProvider
autoConfiguration.addSpanExporterCustomizer(new SanitizeSpanExporterCustomizer());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.trace.data.DelegatingSpanData;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SpanExporter;

import java.util.Collection;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

import static io.opentelemetry.api.common.AttributeKey.stringKey;

public class SanitizeSpanExporterCustomizer implements BiFunction<SpanExporter, ConfigProperties, SpanExporter> {

private static final String PROPERTY_ENABLED_KEY = "sap.cf.integration.otel.extension.sanitizer.enabled";
private static final AttributeKey<String> DB_QUERY_TEXT = stringKey("db.query.text");
//@Deprecated
private static final AttributeKey<String> DB_STATEMENT = stringKey("db.statement");

@Override
public SpanExporter apply(SpanExporter delegate, ConfigProperties config) {
if (config != null && !config.getBoolean(PROPERTY_ENABLED_KEY, true)) {
return delegate;
}
return new SpanExporter() {
@Override
public CompletableResultCode export(Collection<SpanData> spans) {
return delegate.export(spans.stream().map(this::sanitizeSpanData).collect(Collectors.toList()));
}

private SpanData sanitizeSpanData(SpanData spanData) {
Attributes attributes = spanData.getAttributes();
if (attributes == null) {
return spanData;
}
String dbQueryText = attributes.get(DB_QUERY_TEXT);
String dbStatement = attributes.get(DB_STATEMENT);
if (isClean(dbQueryText) && isClean(dbStatement)) {
return spanData;
}
AttributesBuilder sanitized = attributes.toBuilder();
if (!isClean(dbQueryText)) {
sanitized.put(DB_QUERY_TEXT, dbQueryText.substring(0, 7) + " [REDACTED]");
}
if (!isClean(dbStatement)) {
sanitized.put(DB_STATEMENT, dbStatement.substring(0, 7) + " [REDACTED]");
}
return new SanitizedSpanData(spanData, sanitized.build());
}

private boolean isClean(String query) {
return query == null || !query.toLowerCase().startsWith("connect");
}

@Override
public CompletableResultCode flush() {
return delegate.flush();
}

@Override
public CompletableResultCode shutdown() {
return delegate.shutdown();
}
};
}

private static class SanitizedSpanData extends DelegatingSpanData {

private final Attributes filteredAttributes;

protected SanitizedSpanData(SpanData delegate, Attributes filteredAttributes) {
super(delegate);
this.filteredAttributes = filteredAttributes;
}

@Override
public Attributes getAttributes() {
return filteredAttributes;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class SanitizeSpanExporterCustomizerTest {

@Mock(strictness = Mock.Strictness.LENIENT)
private SpanData spanData;

@Mock
private SpanExporter delegateExporter;

@Captor
private ArgumentCaptor<List<SpanData>> spanDataCaptor;

private SpanExporter sanitizeExporter;

@BeforeEach
void setUp() {
when(spanData.getName()).thenReturn("test-span");
this.sanitizeExporter = new SanitizeSpanExporterCustomizer().apply(delegateExporter, null);
}

@Test
void forwardsSpanWithoutAttributes() {
List<SpanData> spans = List.of(spanData);
sanitizeExporter.export(spans);

verify(delegateExporter).export(spans);
}

@Test
void forwardsSpanWithEmptyAttributes() {
List<SpanData> spans = List.of(spanData);
when(spanData.getAttributes()).thenReturn(Attributes.empty());
sanitizeExporter.export(spans);

verify(delegateExporter).export(spans);
}

@Test
void forwardsSpanWithoutSensitiveAttributeKey() {
Attributes attributes = Attributes.builder().put("some.key", "some value").build();
when(spanData.getAttributes()).thenReturn(attributes);
List<SpanData> spans = List.of(spanData);
sanitizeExporter.export(spans);

verify(delegateExporter).export(spans);
}

@Test
void forwardsSpanWithSensitiveAttributeKeyButWithoutSensitiveValue() {
Attributes attributes = Attributes.builder().put("db.query.text", "some safe value").build();
when(spanData.getAttributes()).thenReturn(attributes);
List<SpanData> spans = List.of(spanData);
sanitizeExporter.export(spans);

verify(delegateExporter).export(spans);
}

@Test
void redactsSensitiveDbQueryTextValue() {
Attributes attributes = Attributes.builder().put("db.query.text", "Connect somewhere").build();
when(spanData.getAttributes()).thenReturn(attributes);
List<SpanData> spans = List.of(spanData);
sanitizeExporter.export(spans);

verify(delegateExporter).export(spanDataCaptor.capture());
SpanData sanitizedSpan = spanDataCaptor.getValue().get(0);
assertThat(sanitizedSpan).extracting(SpanData::getName).isEqualTo("test-span");
assertThat(sanitizedSpan).extracting(SpanData::getAttributes)
.extracting(attrs -> attrs.get(AttributeKey.stringKey("db.query.text")))
.isEqualTo("Connect [REDACTED]");
}

@Test
void redactsSensitiveDbStatementValue() {
Attributes attributes = Attributes.builder().put("db.statement", "CONNECT somewhere").build();
when(spanData.getAttributes()).thenReturn(attributes);
List<SpanData> spans = List.of(spanData);
sanitizeExporter.export(spans);

verify(delegateExporter).export(spanDataCaptor.capture());
SpanData sanitizedSpan = spanDataCaptor.getValue().get(0);
assertThat(sanitizedSpan).extracting(SpanData::getName).isEqualTo("test-span");
assertThat(sanitizedSpan).extracting(SpanData::getAttributes)
.extracting(attrs -> attrs.get(AttributeKey.stringKey("db.statement")))
.isEqualTo("CONNECT [REDACTED]");
}

@Test
void keepsOtherAttributesOnRedaction() {
Attributes attributes =
Attributes.builder().put("db.query.text", "connect somewhere").put("some.key", "some.value").build();
when(spanData.getAttributes()).thenReturn(attributes);
List<SpanData> spans = List.of(spanData);
sanitizeExporter.export(spans);

verify(delegateExporter).export(spanDataCaptor.capture());
SpanData sanitizedSpan = spanDataCaptor.getValue().get(0);
assertThat(sanitizedSpan).extracting(SpanData::getName).isEqualTo("test-span");
assertThat(sanitizedSpan).extracting(SpanData::getAttributes)
.extracting(attrs -> attrs.get(AttributeKey.stringKey("db.query.text")))
.isEqualTo("connect [REDACTED]");
assertThat(sanitizedSpan).extracting(SpanData::getAttributes)
.extracting(attrs -> attrs.get(AttributeKey.stringKey("some.key")))
.isEqualTo("some.value");
}

@Test
void canBeDisabledViaConfig() {
Map<String, String> configEntries = new HashMap<>();
configEntries.put("sap.cf.integration.otel.extension.sanitizer.enabled", "false");
DefaultConfigProperties configProperties = DefaultConfigProperties.createFromMap(configEntries);
SpanExporter spanExporter = new SanitizeSpanExporterCustomizer().apply(delegateExporter, configProperties);
assertThat(spanExporter).isSameAs(delegateExporter);
}

@Test
void delegatesFlush() {
sanitizeExporter.flush();
verify(delegateExporter).flush();
}

@Test
void delegatesShutdown() {
sanitizeExporter.shutdown();
verify(delegateExporter).shutdown();
}
}
2 changes: 1 addition & 1 deletion cf-java-logging-support-servlet-dynlog-jwt/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<parent>
<groupId>com.sap.hcp.cf.logging</groupId>
<artifactId>cf-java-logging-support-parent</artifactId>
<version>4.0.0</version>
<version>4.0.1</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion cf-java-logging-support-servlet/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>com.sap.hcp.cf.logging</groupId>
<artifactId>cf-java-logging-support-parent</artifactId>
<version>4.0.0</version>
<version>4.0.1</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.sap.hcp.cf.logging</groupId>
<artifactId>cf-java-logging-support-parent</artifactId>
<version>4.0.0</version>
<version>4.0.1</version>
<packaging>pom</packaging>

<name>Cloud Foundry Java logging support components</name>
Expand Down
Loading