Skip to content

Commit

Permalink
reuse contrib resource providers + add existing ones (#4)
Browse files Browse the repository at this point in the history
* add aws resource provider

* add gcp resource provider

* add app server resource providers

* re-add aws resource providers + server ones

* add gcp resource providers

* restore otel version + small refactor

* first step in shadowing resource providers

* use shadowed variant as dependency in agent

* keep the 'all' for shaded jar for clarity

* use intermediate module for repacking

* fix minor things and make it compile in IDE

* make spotless happy

* add test for ec2 resource provider

* slightly refactor ec2 tests

* bootstrap eks, but fails due to certificates

* mark the eks test as disabled for now

* add few extra aws providers

* add failing test for lambda & ecs

* add lambda + ecs tests

* working test with tomcat service name

* reformat

* add a few comments for later improvements

* reformat
  • Loading branch information
SylvainJuge authored Oct 27, 2023
1 parent fa913fc commit aafac43
Show file tree
Hide file tree
Showing 17 changed files with 1,096 additions and 228 deletions.
3 changes: 2 additions & 1 deletion agent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ dependencies {
bootstrapLibs(project(":bootstrap"))

javaagentLibs(project(":custom"))
javaagentLibs(project(":resources"))

// This is where we should add custom instrumentations
// javaagentLibs(project(":instrumentation:servlet-3"))

upstreamAgent("io.opentelemetry.javaagent:opentelemetry-javaagent:"+libraries.versions.opentelemetryJavaagent.get().toString())
upstreamAgent("io.opentelemetry.javaagent:opentelemetry-javaagent:" + libraries.versions.opentelemetryJavaagent.get().toString())
}

CopySpec isolateClasses(Iterable<File> jars) {
Expand Down
1 change: 0 additions & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,3 @@ dependencies {
// that makes the transitive one from jib fail see https://github.com/elastic/elastic-otel-java/issues/9 for details
// implementation("io.opentelemetry.instrumentation:gradle-plugins:1.30.0-alpha-SNAPSHOT")
}

11 changes: 11 additions & 0 deletions custom/src/main/java/co/elastic/otel/ElasticSpanExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.data.DelegatingSpanData;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
Expand Down Expand Up @@ -62,6 +63,16 @@ public Attributes getAttributes() {
.put(ElasticAttributes.SELF_TIME_ATTRIBUTE, data.getSelfTime())
.build();
}

@Override
public Resource getResource() {
Resource original = span.getResource();
// TODO once we implement asynchronous cloud resource loading, this is the place
// where we can merge them to make it as if they were available through the otel SDK
// if the original resource is immutable, we can probably keep a map and cache to
// prevent too much allocation
return Resource.create(original.getAttributes(), original.getSchemaUrl());
}
});
}
}
Expand Down
28 changes: 28 additions & 0 deletions resources/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
plugins {
java
alias(gradlePlugins.plugins.shadow)
alias(gradlePlugins.plugins.taskinfo)
}

dependencies {

implementation(project("repackaged")) {
attributes {
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.SHADOWED))
}
dependencies {
// AWS cloud resource providers transitive dependencies
// TODO remove the static version dependencies, either by automatically getting the
// transitive dependencies of the shaded artifacts, or by reusing the common versions that
// are very likely provided through transitive dependencies
implementation("com.fasterxml.jackson.core:jackson-core:2.15.2")
implementation("com.squareup.okhttp3:okhttp:4.11.0")

// required to make the IDE compile our own resource provider, won't be included as dependency
compileOnly("io.opentelemetry.contrib:opentelemetry-aws-resources:" + libraries.versions.opentelemetryContribAlpha.get())
compileOnly("io.opentelemetry.contrib:opentelemetry-resource-providers:" + libraries.versions.opentelemetryContribAlpha.get())
}
}

compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
}
38 changes: 38 additions & 0 deletions resources/repackaged/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
java
alias(gradlePlugins.plugins.shadow)
alias(gradlePlugins.plugins.taskinfo)
}

val shadowedImplementation by configurations.creating {
// do not shadow transitive dependencies
isTransitive = false
}

dependencies {

// AWS cloud resource providers
shadowedImplementation("io.opentelemetry.contrib:opentelemetry-aws-resources:" + libraries.versions.opentelemetryContribAlpha.get())

// TODO : GCP resource providers
// "com.google.cloud.opentelemetry:detector-resources:0.25.2-alpha"

// application servers resource providers
shadowedImplementation("io.opentelemetry.contrib:opentelemetry-resource-providers:" + libraries.versions.opentelemetryContribAlpha.get())
}

tasks {
assemble {
dependsOn(shadowJar)
}
jar {
// jar not needed as the shadowed jar replaces it
enabled = false
}
shadowJar {
configurations = listOf(shadowedImplementation)

// skip service provider definitions as providers should not be initialized by SDK.
exclude("META-INF/services/**")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package co.elastic.otel.resources;

import io.opentelemetry.contrib.aws.resource.BeanstalkResource;
import io.opentelemetry.contrib.aws.resource.Ec2Resource;
import io.opentelemetry.contrib.aws.resource.EcsResource;
import io.opentelemetry.contrib.aws.resource.EksResource;
import io.opentelemetry.contrib.aws.resource.LambdaResource;
import io.opentelemetry.contrib.resourceproviders.AppServerServiceNameProvider;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;

public class ElasticResourceProvider implements ResourceProvider {

@Override
public Resource createResource(ConfigProperties config) {
Resource resource = Resource.empty();

// TODO : find a way to make the resource providers async, and then retrieve it
// maybe using a "magic value" for the config properties could be relevant here

resource =
resource
// ec2 relies on http calls without pre-checks
.merge(Ec2Resource.get())
// beanstalk relies on json config file parsing
.merge(BeanstalkResource.get())
// relies on https call without pre-checks + TLS setup (thus quite expensive)
.merge(EksResource.get())
// relies on http call with url provided through env var used as pre-check
.merge(EcsResource.get())
// relies on env variables only
.merge(LambdaResource.get());

// application server providers
resource = resource.merge(new AppServerServiceNameProvider().createResource(config));

return resource;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
co.elastic.otel.resources.ElasticResourceProvider
10 changes: 7 additions & 3 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ include("agent")
include("bootstrap")
include("custom")
include("instrumentation")
include("resources")
include("resources:repackaged")
include("smoke-tests")
include("smoke-tests:test-app")
include("smoke-tests:test-app-war")
include("testing:agent-for-testing")

dependencyResolutionManagement {
Expand All @@ -35,11 +38,12 @@ dependencyResolutionManagement {
version("junit", "5.10.0")

version("opentelemetrySdk", "1.29.0")
version("opentelemetryJavaagent", "1.30.0-SNAPSHOT")
version("opentelemetryJavaagentAlpha", "1.30.0-alpha-SNAPSHOT")
version("opentelemetryJavaagent", "1.30.0")
version("opentelemetryJavaagentAlpha", "1.30.0-alpha")
version("opentelemetryContribAlpha", "1.30.0-alpha")

version("autoservice", "1.1.1")
}

}
}
}
29 changes: 28 additions & 1 deletion smoke-tests/build.gradle
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import jdk.internal.joptsimple.internal.Strings

// TODO : convert to kotlin for consistency
plugins {
id "java"
alias gradlePlugins.plugins.taskinfo
}

configurations {
warOnly {
transitive = false
canBeResolved = true
canBeConsumed = false
}
}

dependencies {
testImplementation("org.testcontainers:testcontainers:1.19.1")
testImplementation("com.fasterxml.jackson.core:jackson-databind:2.15.2")
Expand All @@ -15,6 +25,16 @@ dependencies {
testImplementation("ch.qos.logback:logback-classic:1.4.11")

testImplementation("org.assertj:assertj-core:3.24.2")

testImplementation("io.opentelemetry.semconv:opentelemetry-semconv")

testImplementation("org.mock-server:mockserver-client-java:5.15.0")

warOnly(project("test-app-war")){
attributes {
attribute(Attribute.of("elastic.packaging", String.class), "war")
}
}
}

tasks.test {
Expand All @@ -30,7 +50,14 @@ tasks.test {
dependsOn(agentShadowTask)
inputs.files(layout.files(agentShadowTask))

// depends on the packaged test app war file
dependsOn(project(":smoke-tests:test-app-war").tasks.assemble)
def warFile = project.configurations.warOnly.files.stream().findFirst().get()
inputs.files(layout.files(warFile))

doFirst {
jvmArgs("-Dio.opentelemetry.smoketest.agent.shadowJar.path=${agentShadowTask.archiveFile.get()}")
jvmArgs(
"-Dio.opentelemetry.smoketest.agent.shadowJar.path=${agentShadowTask.archiveFile.get()}",
"-Dio.opentelemetry.smoketest.agent.testAppWar.path=${warFile}")
}
}
Loading

0 comments on commit aafac43

Please sign in to comment.