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 Spring Boot service version finder / ResourceProvider #9480

Merged
merged 6 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ InputStream openClasspathResource(String filename) {
return classLoader.getResourceAsStream(path);
}

InputStream openClasspathResource(String filename, String location) {
String path = location + "/" + filename;
return classLoader.getResourceAsStream(path);
}
Copy link
Member

Choose a reason for hiding this comment

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

Now that the SystemHelper is used in two classes, WDYT about making it a top-level class?


InputStream openFile(String filename) throws Exception {
return Files.newInputStream(Paths.get(filename));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.resources;

import static java.util.logging.Level.FINE;

import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.spring.resources.SpringBootServiceNameDetector.SystemHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.ResourceAttributes;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.Properties;
import java.util.logging.Logger;

@AutoService(ResourceProvider.class)
public class SpringBootServiceVersionDetector implements ResourceProvider {

private static final Logger logger =
Logger.getLogger(SpringBootServiceVersionDetector.class.getName());

private final SystemHelper system;

public SpringBootServiceVersionDetector() {
this.system = new SystemHelper();
}

// Exists for testing
public SpringBootServiceVersionDetector(SystemHelper system) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public SpringBootServiceVersionDetector(SystemHelper system) {
SpringBootServiceVersionDetector(SystemHelper system) {

this.system = system;
}

@Override
public Resource createResource(ConfigProperties config) {
return getServiceVersionFromBuildInfo()
.map(
version -> {
logger.log(FINE, "Auto-detected Spring Boot service version: {0}", version);
return Resource.builder().put(ResourceAttributes.SERVICE_VERSION, version).build();
})
.orElseGet(Resource::empty);
}

private Optional<String> getServiceVersionFromBuildInfo() {
try (InputStream in = system.openClasspathResource("build-info.properties", "META-INF")) {
Copy link
Member

Choose a reason for hiding this comment

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

I think putting "META-INF" first makes more sense, since it's a directory that contains the build info file

Suggested change
try (InputStream in = system.openClasspathResource("build-info.properties", "META-INF")) {
try (InputStream in = system.openClasspathResource("META-INF", "build-info.properties")) {

return getServiceVersionPropertyFromStream(in);
} catch (Exception e) {
return Optional.empty();
}
}

private static Optional<String> getServiceVersionPropertyFromStream(InputStream in) {
Properties properties = new Properties();
try {
// Note: load() uses ISO 8859-1 encoding, same as spring uses by default for property files
properties.load(in);
laurit marked this conversation as resolved.
Show resolved Hide resolved
return Optional.ofNullable(properties.getProperty("build.version"));
} catch (IOException e) {
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.resources;

import static io.opentelemetry.semconv.ResourceAttributes.SERVICE_VERSION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.resources.Resource;
import java.io.InputStream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class SpringBootServiceVersionDetectorTest {

static final String BUILD_PROPS = "build-info.properties";
static final String META_INFO = "META-INF";

@Mock ConfigProperties config;
@Mock SpringBootServiceNameDetector.SystemHelper system;

@Test
void givenBuildVersionIsPresentInBuildInfProperties_thenReturnBuildVersion() {
when(system.openClasspathResource(BUILD_PROPS, META_INFO))
.thenReturn(openClasspathResource(META_INFO + "/" + BUILD_PROPS));

SpringBootServiceVersionDetector guesser = new SpringBootServiceVersionDetector(system);
Resource result = guesser.createResource(config);
assertThat(result.getAttribute(SERVICE_VERSION)).isEqualTo("0.0.2");
}

private InputStream openClasspathResource(String resource) {
return getClass().getClassLoader().getResourceAsStream(resource);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build.artifact=something
build.name=some-name
build.version=0.0.2
Loading