Removed statically loaded exporters and added more tests (#171)

* Classloader done. Still needs shading

* First working version

* Made the dummy exporter slightly less stupid

* Use SPI pattern for loading. Added runtime shader

* Changed to do shading on binary stream instead of loading the class first

* Protected the runtime shader from having its internals shaded

* Cleaned up code and naming

* Cleaned up

* Cleaned up and fixed test issues

* Minor fixes from code review

* Added exporter smoke tests

* Finalized exporter smoke tests

* Reenabled springboot test

* Fixed some copy-paste issues

* Initial work on exporter bridges

* Implemented dynamically loaded exporter adapters

* Added some more files

* Added exporter-adapters for well-known exporters

Moved dummy-exporter to exporter adapters

Removed -Dota.exporter option in favor of ota.exporter.jar

Added basic exporter tests to smoketest

Increased timeout for Finatra server start to help with parallel test execution

* Cleaned up tests, removed dead code and updated README

* Update README.md

* Update README.md

* Removed references to gRPC dependencies needed by Jaeger

* Fixed incorrect use of ServiceLoader in test

* Fixed test dependencies

* Adjusted exporter tests

* Fixed codeNarc issues

* Fixed CircleCI issues

* Added tests to check what CircleCI is doing

* Added missing logging directory

* Removed deadline() from Jaeger factory

* Another attempt to make it pass CircleCI

* Fixed some minor codeNarc issues

* Wrapped test config in doFirst

* Change forEach -> each

* Removed README section about commercial exporters

* Reduced number of changes to Config

* Fixed field reordering issue

* Removed references to dummy exporter

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
Pontus Rydin 2020-02-21 17:12:48 -06:00 committed by GitHub
parent ebf3d78274
commit 239eb53a44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 290 additions and 179 deletions

View File

@ -21,7 +21,7 @@ as Java system properties (-D flags) or as environment variables. This is an exa
```
java -javaagent:/path/to/opentelemetry-auto-<version>.jar \
-Dota.exporter=jaeger \
-Dota.exporter.jar=exporter-adapters/logging-exporter-adapter/build/libs/logging-exporter-adapter-0.1.2-SNAPSHOT.jar \
-Dota.jaeger.host=localhost \
-Dota.jaeger.port=14250 \
-Dota.jaeger.service=shopping \
@ -31,10 +31,27 @@ java -javaagent:/path/to/opentelemetry-auto-<version>.jar \
### Configuration parameters (subject to change!)
System property | Environment variable | Purpose
--- | --- | ---
ota.exporter | OTA_EXPORTER | The name of the exporter. Currently only supports 'jaeger' for Jager over gRPC
ota.exporter.jar | OTA_EXPORTER_JAR | The path to an exporter JAR
ota.service | OTA_SERVICE | The service name of this JVM instance. This is used as a label in Jaeger to distinguish between JVM instances in a multi-service environment.
ota.jaeger.host | OTA_JAEGER_HOST | The Jaeger host to connect to. Currently only gRPC is supported.
ota.jaeger.port | OTA_JAEGER_PORT | The port to connect to on the Jaeger host. Currently only gRPC is supported
### Available exporters
Currently two exporters are available and bundled with this project. They area available under the ```exporter-adapters``` directory.
#### Logging Exporter
The logging exporter simply prints the name of the span along with its attributes to stdout. It is used manly
for testing and debugging. It takes a single configuration parameter.
System property | Environment variable | Purpose
--- | --- | ---
ota.exporter.logging.prefix | OTA_EXPORTER_LOGGING_PREFIX | A string that is printed in front of the span name and attributes.
#### Jaeger exporter
A simple wrapper for the Jaeger exporter of opentelemetry-java. It currently only supports gRPC as its communications protocol.
System property | Environment variable | Purpose
--- | --- | ---
ota.exporter.jaeger.host | OTA_EXPORTER_JAEGER_HOST | The Jaeger host to connect to. Currently only gRPC is supported.
ota.exporter.jaeger.port | OTA_EXPORTER_JAEGER_PORT | The port to connect to on the Jaeger host. Currently only gRPC is supported
These parameter names are very likely to change over time, so please check back here when trying out a new version!

View File

@ -36,7 +36,6 @@ public class Config {
private static final Pattern ENV_REPLACEMENT = Pattern.compile("[^a-zA-Z0-9_]");
public static final String EXPORTER = "exporter";
public static final String EXPORTER_JAR = "exporter.jar";
public static final String SERVICE = "service";
public static final String CONFIGURATION_FILE = "trace.config";
@ -84,7 +83,6 @@ public class Config {
private static final String DEFAULT_TRACE_EXECUTORS = "";
private static final String DEFAULT_TRACE_METHODS = null;
@Getter private final String exporter;
@Getter private final String exporterJar;
@Getter private final String serviceName;
@Getter private final boolean traceEnabled;
@ -132,7 +130,6 @@ public class Config {
Config() {
propertiesFromConfigFile = loadConfigurationFile();
exporter = getSettingFromEnvironment(EXPORTER, null);
exporterJar = getSettingFromEnvironment(EXPORTER_JAR, null);
serviceName = getSettingFromEnvironment(SERVICE, "(unknown)");
traceEnabled = getBooleanSettingFromEnvironment(TRACE_ENABLED, DEFAULT_TRACE_ENABLED);
@ -191,7 +188,6 @@ public class Config {
// Read order: Properties -> Parent
private Config(final Properties properties, final Config parent) {
exporter = properties.getProperty(EXPORTER, parent.exporter);
exporterJar = properties.getProperty(EXPORTER_JAR, parent.exporterJar);
serviceName = properties.getProperty(SERVICE, parent.serviceName);

View File

@ -36,8 +36,9 @@ dependencies {
implementation deps.autoservice
testCompile project(':testing')
testCompile project(':dummy-exporter')
instrumentationMuzzle sourceSets.main.output
instrumentationMuzzle configurations.compile
}

View File

@ -6,6 +6,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import net.bytebuddy.jar.asm.ClassReader;
import net.bytebuddy.jar.asm.ClassWriter;
import net.bytebuddy.jar.asm.commons.ClassRemapper;
@ -37,6 +38,17 @@ public class ExporterClassLoader extends URLClassLoader {
super(urls, parent);
}
@Override
public Enumeration<URL> getResources(final String name) throws IOException {
// A small hack to prevent other exporters from being loaded by this classloader if they
// should happen to appear on the classpath.
if (name.equals(
"META-INF/services/io.opentelemetry.auto.exportersupport.SpanExporterFactory")) {
return findResources(name);
}
return super.getResources(name);
}
@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {

View File

@ -3,8 +3,6 @@ package io.opentelemetry.auto.tooling;
import com.google.common.annotations.VisibleForTesting;
import io.opentelemetry.auto.config.Config;
import io.opentelemetry.auto.exportersupport.SpanExporterFactory;
import io.opentelemetry.auto.tooling.exporter.ExporterConfigException;
import io.opentelemetry.auto.tooling.exporter.ExporterRegistry;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.export.SimpleSpansProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
@ -22,28 +20,16 @@ public class TracerInstaller {
if (Config.get().isTraceEnabled()) {
// Try to create an exporter
SpanExporter exporter = null;
final String expName = Config.get().getExporter();
if (expName != null) {
try {
final io.opentelemetry.auto.tooling.exporter.SpanExporterFactory f =
ExporterRegistry.getInstance().getFactory(expName);
exporter = f.newExporter();
log.info("Loaded span exporter: " + expName);
} catch (final ExporterConfigException e) {
log.warn("Error loading exporter. Spans will be dropped", e);
final String exporterJar = Config.get().getExporterJar();
if (exporterJar != null) {
final SpanExporter exporter = loadFromJar(exporterJar);
if (exporter != null) {
OpenTelemetrySdk.getTracerFactory()
.addSpanProcessor(SimpleSpansProcessor.newBuilder(exporter).build());
log.info("Installed span exporter: " + exporter.getClass().getCanonicalName());
} else {
log.warn("No valid exporter found. Tracing will run but spans are dropped");
}
} else {
final String exporterJar = Config.get().getExporterJar();
if (exporterJar != null) {
exporter = loadFromJar(exporterJar);
}
}
if (exporter != null) {
OpenTelemetrySdk.getTracerFactory()
.addSpanProcessor(SimpleSpansProcessor.newBuilder(exporter).build());
log.info("Installed span exporter: " + exporter.getClass().getCanonicalName());
} else {
log.warn("No exporter is specified. Tracing will run but spans are dropped");
}
} else {

View File

@ -1,25 +0,0 @@
package io.opentelemetry.auto.tooling.exporter;
public class ExporterConfigException extends Exception {
public ExporterConfigException() {}
public ExporterConfigException(final String message) {
super(message);
}
public ExporterConfigException(final String message, final Throwable cause) {
super(message, cause);
}
public ExporterConfigException(final Throwable cause) {
super(cause);
}
public ExporterConfigException(
final String message,
final Throwable cause,
final boolean enableSuppression,
final boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@ -1,34 +0,0 @@
package io.opentelemetry.auto.tooling.exporter;
import java.util.HashMap;
import java.util.Map;
public class ExporterRegistry {
private static final ExporterRegistry instance = new ExporterRegistry();
private final Map<String, SpanExporterFactory> factories = new HashMap<>();
public static ExporterRegistry getInstance() {
return instance;
}
public ExporterRegistry() {
registerDefaultFactories();
}
public void registerFactory(final String name, final SpanExporterFactory factory) {
factories.put(name, factory);
}
public SpanExporterFactory getFactory(final String name) throws ExporterConfigException {
final SpanExporterFactory f = factories.get(name);
if (f == null) {
throw new ExporterConfigException("Exporter type " + name + " is not registered");
}
return f;
}
private void registerDefaultFactories() {
registerFactory("jaeger", new JaegerExporterFactory());
}
}

View File

@ -1,40 +0,0 @@
package io.opentelemetry.auto.tooling.exporter;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.opentelemetry.auto.config.Config;
import io.opentelemetry.exporters.jaeger.JaegerGrpcSpanExporter;
import io.opentelemetry.sdk.trace.export.SpanExporter;
public class JaegerExporterFactory implements SpanExporterFactory {
private static final String HOST_CONFIG = "jaeger.host";
private static final String PORT_CONFIG = "jaeger.port";
@Override
public SpanExporter newExporter() throws ExporterConfigException {
final String host = Config.getSettingFromEnvironment(HOST_CONFIG, null);
if (host == null) {
throw new ExporterConfigException(HOST_CONFIG + " must be specified");
}
final String ipStr = Config.getSettingFromEnvironment(PORT_CONFIG, null);
if (ipStr == null) {
throw new ExporterConfigException(PORT_CONFIG + " must be specified");
}
final String service = Config.get().getServiceName();
final int port;
try {
port = Integer.parseInt(ipStr);
} catch (final NumberFormatException e) {
throw new ExporterConfigException("Error parsing " + PORT_CONFIG, e);
}
final ManagedChannel jaegerChannel =
ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
return JaegerGrpcSpanExporter.newBuilder()
.setServiceName(service)
.setChannel(jaegerChannel)
.setDeadline(30000)
.build();
}
}

View File

@ -1,7 +0,0 @@
package io.opentelemetry.auto.tooling.exporter;
import io.opentelemetry.sdk.trace.export.SpanExporter;
public interface SpanExporterFactory {
SpanExporter newExporter() throws ExporterConfigException;
}

View File

@ -1,18 +0,0 @@
package io.opentelemetry.auto.tooling
import io.opentelemetry.auto.util.test.AgentSpecification
import io.opentelemetry.sdk.OpenTelemetrySdk
class ExporterLoaderTest extends AgentSpecification {
def jarName = "../dummy-exporter/build/libs/dummy-exporter-0.1.0-all.jar"
def tracer = OpenTelemetrySdk.getTracerFactory().get("test")
def "test load exporter"() {
when:
def exporter = TracerInstaller.loadFromJar(jarName)
then:
exporter.getClass().getName() == "io.opentelemetry.auto.dummyexporter.DummyExporter"
}
}

View File

@ -1 +0,0 @@
io.opentelemetry.auto.dummyexporter.DummySpanExporterFactory

View File

@ -0,0 +1,21 @@
apply from: "${rootDir}/gradle/java.gradle"
dependencies {
testCompile project(':auto-tooling')
testCompile project(':exporter-adapters:jaeger-adapter')
testCompile project(':exporter-adapters:logging-exporter-adapter')
}
tasks.withType(Test).configureEach() {
dependsOn ':exporter-adapters:logging-exporter-adapter:shadowJar'
dependsOn ':exporter-adapters:jaeger-adapter:shadowJar'
doFirst {
systemProperty 'projectVersion', allprojects.version[0]
systemProperty 'adapterRoot', "${rootDir}/exporter-adapters"
systemProperty 'loggingExporterJar', project(':exporter-adapters:logging-exporter-adapter').tasks.shadowJar.archivePath
systemProperty 'jaegerExporterJar', project(':exporter-adapters:jaeger-adapter').tasks.shadowJar.archivePath
}
}

View File

@ -0,0 +1,11 @@
plugins {
id "com.github.johnrengelman.shadow"
}
apply from: "${rootDir}/gradle/java.gradle"
dependencies {
compile deps.opentelemetryJaeger
compile project(':exporter-support')
compile group: 'io.grpc', name: 'grpc-api', version: '1.24.0'
}

View File

@ -0,0 +1,36 @@
package io.opentelemetry.auto.exporters.jaeger;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.opentelemetry.auto.exportersupport.ConfigProvider;
import io.opentelemetry.auto.exportersupport.SpanExporterFactory;
import io.opentelemetry.exporters.jaeger.JaegerGrpcSpanExporter;
import io.opentelemetry.sdk.trace.export.SpanExporter;
public class JaegerExporterFactory implements SpanExporterFactory {
private static final String HOST_CONFIG = "jaeger.host";
private static final String PORT_CONFIG = "jaeger.port";
private static final String SERVICE_CONFIG = "service";
private static final int DEFAULT_PORT = 14250;
private static final String DEFAULT_SERVICE = "(unknown service)";
@Override
public SpanExporter fromConfig(final ConfigProvider config) {
final String host = config.getString(HOST_CONFIG, null);
if (host == null) {
throw new IllegalArgumentException(HOST_CONFIG + " must be specified");
}
final int port = config.getInt(PORT_CONFIG, DEFAULT_PORT);
final String service = config.getString(SERVICE_CONFIG, DEFAULT_SERVICE);
final ManagedChannel jaegerChannel =
ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
return JaegerGrpcSpanExporter.newBuilder()
.setServiceName(service)
.setChannel(jaegerChannel)
.build();
}
}

View File

@ -0,0 +1 @@
io.opentelemetry.auto.exporters.jaeger.JaegerExporterFactory

View File

@ -10,5 +10,3 @@ dependencies {
compile project(":exporter-support")
}

View File

@ -1,4 +1,4 @@
package io.opentelemetry.auto.dummyexporter;
package io.opentelemetry.auto.exporters.loggingexporter;
import io.opentelemetry.sdk.trace.SpanData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
@ -6,10 +6,10 @@ import io.opentelemetry.trace.AttributeValue;
import java.util.List;
import java.util.Map;
public class DummyExporter implements SpanExporter {
public class LoggingExporter implements SpanExporter {
private final String prefix;
public DummyExporter(final String prefix) {
public LoggingExporter(final String prefix) {
this.prefix = prefix;
}

View File

@ -1,12 +1,12 @@
package io.opentelemetry.auto.dummyexporter;
package io.opentelemetry.auto.exporters.loggingexporter;
import io.opentelemetry.auto.exportersupport.ConfigProvider;
import io.opentelemetry.auto.exportersupport.SpanExporterFactory;
import io.opentelemetry.sdk.trace.export.SpanExporter;
public class DummySpanExporterFactory implements SpanExporterFactory {
public class LoggingExporterFactory implements SpanExporterFactory {
@Override
public SpanExporter fromConfig(final ConfigProvider config) {
return new DummyExporter(config.getString("prefix", "no-prefix"));
return new LoggingExporter(config.getString("logging.prefix", "no-prefix"));
}
}

View File

@ -0,0 +1 @@
io.opentelemetry.auto.exporters.loggingexporter.LoggingExporterFactory

View File

@ -0,0 +1,56 @@
import io.opentelemetry.auto.exportersupport.SpanExporterFactory
import io.opentelemetry.auto.tooling.ExporterClassLoader
import spock.lang.Shared
import spock.lang.Specification
class ExporterAdaptersTest extends Specification {
@Shared
def projectVersion = System.getProperty("projectVersion")
@Shared
def adapterRoot = System.getProperty("adapterRoot")
@Shared
def loggingExporterJar = System.getProperty("loggingExporterJar")
@Shared
def jaegerExporterJar = System.getProperty("jaegerExporterJar")
@Shared
def jaegerDir = new File("${adapterRoot}/jaeger-adapter/build/libs")
def "test jars exist"() {
when:
def file = new File(exporter)
then:
file != null
where:
exporter << [loggingExporterJar, jaegerExporterJar]
}
def "test exporter load"() {
setup:
def file = new File(exporter)
println "Attempting to load ${file.toString()} for ${classname}"
assert file.exists(): "${file.toString()} does not exist"
URL[] urls = [file.toURI().toURL()]
def classLoader = new ExporterClassLoader(urls, this.getClass().getClassLoader())
def serviceLoader = ServiceLoader.load(SpanExporterFactory, classLoader)
when:
def f = serviceLoader.iterator().next()
println f.class.getName()
then:
f != null
f instanceof SpanExporterFactory
f.getClass().getName() == classname
where:
exporter | classname
jaegerExporterJar | 'io.opentelemetry.auto.exporters.jaeger.JaegerExporterFactory'
loggingExporterJar | 'io.opentelemetry.auto.exporters.loggingexporter.LoggingExporterFactory'
}
}

View File

@ -20,7 +20,7 @@ import static io.opentelemetry.trace.Span.Kind.SERVER
class FinatraServerTest extends HttpServerTest<HttpServer, FinatraDecorator> {
private static final Duration TIMEOUT = Duration.fromSeconds(5)
private static final long STARTUP_TIMEOUT = 20 * 1000
private static final long STARTUP_TIMEOUT = 40 * 1000
static closeAndWait(Closable closable) {
if (closable != null) {

View File

@ -80,11 +80,6 @@ dependencies {
compile(project(':auto-tooling')) {
exclude module: ':auto-bootstrap'
}
// TODO: including the jaeger exporter doubles the agent jar file size, would be better for it to be optional component
// (but this can wait until after 0.1.0)
compile group: 'io.opentelemetry', name: 'opentelemetry-exporters-jaeger', version: versions.opentelemetry
compile group: 'io.grpc', name: 'grpc-netty', version: '1.24.0'
}
configurations {

View File

@ -40,6 +40,7 @@ include ':smoke-tests:java9-modules'
include ':smoke-tests:play'
include ':smoke-tests:springboot'
include ':smoke-tests:wildfly'
include ':smoke-tests:springboot'
// instrumentation:
include ':instrumentation:akka-http-10.0'
@ -135,15 +136,17 @@ include ':instrumentation:trace-annotation'
include ':instrumentation:twilio-6.6'
include ':instrumentation:vertx-testing'
// exporter adapters
include ":exporter-adapters"
include ":exporter-adapters:logging-exporter-adapter"
include ":exporter-adapters:jaeger-adapter"
// benchmark
include ':benchmark'
include ':benchmark-integration'
include ':benchmark-integration:jetty-perftest'
include ':benchmark-integration:play-perftest'
//Dummy exporter TODO: Move it somewhere better
include ":dummy-exporter"
def setBuildFile(project) {
project.buildFileName = "${project.name}.gradle"
project.children.each {

View File

@ -5,6 +5,9 @@ import spock.lang.Shared
class PlaySmokeTest extends AbstractServerSmokeTest {
static final PLAY_SPAN = 'LOGGED_SPAN play.request'
static final AKKA_SPAN = 'LOGGED_SPAN akka-http.request'
@Shared
File playDirectory = new File("${buildDirectory}/stage/playBinary")
@ -13,8 +16,11 @@ class PlaySmokeTest extends AbstractServerSmokeTest {
ProcessBuilder processBuilder =
new ProcessBuilder("${playDirectory}/bin/playBinary")
processBuilder.directory(playDirectory)
processBuilder.environment().put("JAVA_OPTS",
defaultJavaProperties.join(" ")
+ " -Dota.exporter.jar=${exporterPath}"
+ " -Dota.exporter.logging.prefix=LOGGED_SPAN"
+ " -Dconfig.file=${workingDirectory}/conf/application.conf -Dhttp.port=${httpPort}"
+ " -Dhttp.address=127.0.0.1")
return processBuilder
@ -22,16 +28,23 @@ class PlaySmokeTest extends AbstractServerSmokeTest {
def "welcome endpoint #n th time"() {
setup:
def spanCounter = new SpanCounter(logfile, [
(PLAY_SPAN): 1,
(AKKA_SPAN): 1,
], 10000)
String url = "http://localhost:$httpPort/welcome?id=$n"
def request = new Request.Builder().url(url).get().build()
when:
def response = client.newCall(request).execute()
def spans = spanCounter.countSpans()
then:
def responseBodyStr = response.body().string()
responseBodyStr == "Welcome $n."
response.code() == 200
spans[PLAY_SPAN] == 1
spans[AKKA_SPAN] == 1
where:
n << (1..200)

View File

@ -5,6 +5,7 @@ description = 'smoke-tests'
dependencies {
compile deps.spock
compile project(':testing')
compile project(':exporter-adapters:logging-exporter-adapter')
}
subprojects { subProject ->
@ -15,6 +16,7 @@ subprojects { subProject ->
// Tests depend on this to know where to run things and what agent jar to use
jvmArgs "-Dio.opentelemetry.smoketest.builddir=${buildDir}"
jvmArgs "-Dio.opentelemetry.smoketest.agent.shadowJar.path=${project(':opentelemetry-auto').tasks.shadowJar.archivePath}"
jvmArgs "-Dota.exporter.jar=${project(':exporter-adapters:logging-exporter-adapter').tasks.shadowJar.archivePath}"
}
}
}

View File

@ -4,6 +4,9 @@ import okhttp3.Request
class SpringBootSmokeTest extends AbstractServerSmokeTest {
static final HANDLER_SPAN = "LOGGED_SPAN spring.handler"
static final SERVLET_SPAN = "LOGGED_SPAN servlet.request"
@Override
ProcessBuilder createProcessBuilder() {
String springBootShadowJar = System.getProperty("io.opentelemetry.smoketest.springboot.shadowJar.path")
@ -11,18 +14,23 @@ class SpringBootSmokeTest extends AbstractServerSmokeTest {
List<String> command = new ArrayList<>()
command.add(javaPath())
command.addAll(defaultJavaProperties)
command.addAll((String[]) ["-jar", springBootShadowJar, "--server.port=${httpPort}"])
command.addAll((String[]) ["-Dota.exporter.jar=${exporterPath}", "-Dota.exporter.logging.prefix=LOGGED_SPAN", "-jar", springBootShadowJar, "--server.port=${httpPort}"])
ProcessBuilder processBuilder = new ProcessBuilder(command)
processBuilder.directory(new File(buildDirectory))
}
def "default home page #n th time"() {
setup:
def spanCounter = new SpanCounter(logfile, [
(HANDLER_SPAN): 1,
(SERVLET_SPAN): 1,
], 10000)
String url = "http://localhost:${httpPort}/greeting"
def request = new Request.Builder().url(url).get().build()
when:
def response = client.newCall(request).execute()
def spans = spanCounter.countSpans()
then:
def responseBodyStr = response.body().string()
@ -30,6 +38,8 @@ class SpringBootSmokeTest extends AbstractServerSmokeTest {
responseBodyStr.contains("Sup Dawg")
response.body().contentType().toString().contains("text/plain")
response.code() == 200
spans[HANDLER_SPAN] == 1
spans[SERVLET_SPAN] == 1
where:
n << (1..200)

View File

@ -17,7 +17,12 @@ abstract class AbstractServerSmokeTest extends AbstractSmokeTest {
protected OkHttpClient client = OkHttpUtils.client()
def setupSpec() {
PortUtils.waitForPortToOpen(httpPort, 240, TimeUnit.SECONDS, serverProcess)
try {
PortUtils.waitForPortToOpen(httpPort, 240, TimeUnit.SECONDS, serverProcess)
} catch (e) {
System.err.println(logfile.text)
throw e
}
}
}

View File

@ -14,9 +14,19 @@ abstract class AbstractSmokeTest extends Specification {
protected String shadowJarPath = System.getProperty("io.opentelemetry.smoketest.agent.shadowJar.path")
@Shared
protected String[] defaultJavaProperties
@Shared
protected Process serverProcess
@Shared
protected String exporterPath = System.getProperty("ota.exporter.jar")
@Shared
protected File logfile
def countSpans(prefix) {
return logfile.text.tokenize('\n').count {
it.startsWith prefix
}
}
def setupSpec() {
if (buildDirectory == null || shadowJarPath == null) {
@ -34,10 +44,12 @@ abstract class AbstractSmokeTest extends Specification {
processBuilder.environment().put("JAVA_HOME", System.getProperty("java.home"))
processBuilder.redirectErrorStream(true)
File log = new File("${buildDirectory}/reports/testProcess.${this.getClass().getName()}.log")
processBuilder.redirectOutput(ProcessBuilder.Redirect.to(log))
logfile = new File("${buildDirectory}/reports/testProcess.${this.getClass().getName()}.log")
processBuilder.redirectOutput(ProcessBuilder.Redirect.to(logfile))
serverProcess = processBuilder.start()
}
String javaPath() {

View File

@ -0,0 +1,55 @@
package io.opentelemetry.smoketest
class SpanCounter {
long expiration
final Map<String, Integer> targets
int totalTargets = 0
final Reader reader
final Map<String, Integer> counters
SpanCounter(File file, Map<String, Integer> targets, long timeout) {
reader = file.newReader()
reader.skip(file.length())
expiration = System.currentTimeMillis() + timeout
this.targets = targets
counters = new HashMap<>(targets.size())
targets.keySet().each({
totalTargets += targets[it]
counters[it] = 0
})
}
Map<String, Integer> countSpans() {
try {
def line
while (System.currentTimeMillis() < expiration) {
line = reader.readLine()
if (line) {
for (def key : counters.keySet()) {
if (line.startsWith(key)) {
counters[key]++
if (--totalTargets == 0) {
// We hit our total target. We may or may not have gotten the right
// number for each tag, but we're letting the caller sort that out!
return counters
}
}
}
} else {
Thread.sleep(10)
}
}
}
finally {
reader?.close()
}
return counters
}
}

View File

@ -18,8 +18,13 @@ class WildflySmokeTest extends AbstractServerSmokeTest {
ProcessBuilder processBuilder =
new ProcessBuilder("${wildflyDirectory}/bin/standalone.sh")
processBuilder.directory(wildflyDirectory)
// We're installing a span exporter to make sure it doesn't blow anything up, but we're not
// checking the spans, since JBoss seems to redirect stdout to something we don't have (easy) access to.
processBuilder.environment().put("JAVA_OPTS",
defaultJavaProperties.join(" ")
+ " -Dota.exporter.jar=${exporterPath}"
+ " -Dota.exporter.logging.prefix=LOGGED_SPAN"
+ " -Djboss.http.port=${httpPort} -Djboss.https.port=${httpsPort}"
+ " -Djboss.management.http.port=${managementPort}")
return processBuilder