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:
parent
ebf3d78274
commit
239eb53a44
25
README.md
25
README.md
|
@ -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!
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -36,8 +36,9 @@ dependencies {
|
|||
implementation deps.autoservice
|
||||
|
||||
testCompile project(':testing')
|
||||
testCompile project(':dummy-exporter')
|
||||
|
||||
instrumentationMuzzle sourceSets.main.output
|
||||
instrumentationMuzzle configurations.compile
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package io.opentelemetry.auto.tooling.exporter;
|
||||
|
||||
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
||||
|
||||
public interface SpanExporterFactory {
|
||||
SpanExporter newExporter() throws ExporterConfigException;
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
io.opentelemetry.auto.dummyexporter.DummySpanExporterFactory
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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'
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
io.opentelemetry.auto.exporters.jaeger.JaegerExporterFactory
|
|
@ -10,5 +10,3 @@ dependencies {
|
|||
compile project(":exporter-support")
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
io.opentelemetry.auto.exporters.loggingexporter.LoggingExporterFactory
|
|
@ -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'
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue