diff options
Diffstat (limited to 'exporters/trace/ocagent')
14 files changed, 1884 insertions, 0 deletions
diff --git a/exporters/trace/ocagent/README.md b/exporters/trace/ocagent/README.md new file mode 100644 index 00000000..4f25bd6e --- /dev/null +++ b/exporters/trace/ocagent/README.md @@ -0,0 +1,48 @@ +# OpenCensus Java OC-Agent Trace Exporter + +The *OpenCensus Java OC-Agent Trace Exporter* is the Java implementation of the OpenCensus Agent +(OC-Agent) Trace Exporter. + +## Quickstart + +### Add the dependencies to your project + +For Maven add to your `pom.xml`: +```xml +<dependencies> + <dependency> + <groupId>io.opencensus</groupId> + <artifactId>opencensus-api</artifactId> + <version>0.17.0</version> + </dependency> + <dependency> + <groupId>io.opencensus</groupId> + <artifactId>opencensus-exporter-trace-ocagent</artifactId> + <version>0.17.0</version> + </dependency> + <dependency> + <groupId>io.opencensus</groupId> + <artifactId>opencensus-impl</artifactId> + <version>0.17.0</version> + <scope>runtime</scope> + </dependency> +</dependencies> +``` + +For Gradle add to your dependencies: +```gradle +compile 'io.opencensus:opencensus-api:0.17.0' +compile 'io.opencensus:opencensus-exporter-trace-ocagent:0.17.0' +runtime 'io.opencensus:opencensus-impl:0.17.0' +``` + +### Register the exporter + +```java +public class MyMainClass { + public static void main(String[] args) throws Exception { + OcAgentTraceExporter.createAndRegister(); + // ... + } +} +``` diff --git a/exporters/trace/ocagent/build.gradle b/exporters/trace/ocagent/build.gradle new file mode 100644 index 00000000..777c08d0 --- /dev/null +++ b/exporters/trace/ocagent/build.gradle @@ -0,0 +1,21 @@ +description = 'OpenCensus Java OC-Agent Trace Exporter' + +[compileJava, compileTestJava].each() { + it.sourceCompatibility = 1.7 + it.targetCompatibility = 1.7 +} + +dependencies { + compileOnly libraries.auto_value + + compile project(':opencensus-api'), + project(':opencensus-contrib-monitored-resource-util'), + libraries.grpc_core, + libraries.grpc_netty, + libraries.grpc_stub, + libraries.opencensus_proto + + testCompile project(':opencensus-api') + + signature "org.codehaus.mojo.signature:java17:1.0@signature" +} diff --git a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtils.java b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtils.java new file mode 100644 index 00000000..65729803 --- /dev/null +++ b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtils.java @@ -0,0 +1,184 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import com.google.common.annotations.VisibleForTesting; +import io.opencensus.common.OpenCensusLibraryInformation; +import io.opencensus.common.Timestamp; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.AwsEc2InstanceMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGceInstanceMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGkeContainerMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResourceUtils; +import io.opencensus.proto.agent.common.v1.LibraryInfo; +import io.opencensus.proto.agent.common.v1.LibraryInfo.Language; +import io.opencensus.proto.agent.common.v1.Node; +import io.opencensus.proto.agent.common.v1.ProcessIdentifier; +import io.opencensus.proto.agent.common.v1.ServiceInfo; +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.SecureRandom; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +/** Utilities for detecting and creating {@link Node}. */ +final class OcAgentNodeUtils { + + // The current version of the OpenCensus OC-Agent Exporter. + @VisibleForTesting + static final String OC_AGENT_EXPORTER_VERSION = "0.17.0-SNAPSHOT"; // CURRENT_OPENCENSUS_VERSION + + @VisibleForTesting static final String RESOURCE_TYPE_ATTRIBUTE_KEY = "OPENCENSUS_SOURCE_TYPE"; + @VisibleForTesting static final String RESOURCE_LABEL_ATTRIBUTE_KEY = "OPENCENSUS_SOURCE_LABELS"; + + @Nullable + private static final MonitoredResource RESOURCE = MonitoredResourceUtils.getDefaultResource(); + + // Creates a Node with information from the OpenCensus library and environment variables. + static Node getNodeInfo(String serviceName) { + String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + Timestamp censusTimestamp = Timestamp.fromMillis(System.currentTimeMillis()); + return Node.newBuilder() + .setIdentifier(getProcessIdentifier(jvmName, censusTimestamp)) + .setLibraryInfo(getLibraryInfo(OpenCensusLibraryInformation.VERSION)) + .setServiceInfo(getServiceInfo(serviceName)) + .putAllAttributes(getAttributeMap(RESOURCE)) + .build(); + } + + // Creates process identifier with the given JVM name and start time. + @VisibleForTesting + static ProcessIdentifier getProcessIdentifier(String jvmName, Timestamp censusTimestamp) { + String hostname; + int pid; + // jvmName should be something like '<pid>@<hostname>', at least in Oracle and OpenJdk JVMs + int delimiterIndex = jvmName.indexOf('@'); + if (delimiterIndex < 1) { + // Not the expected format, generate a random number. + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + hostname = "localhost"; + } + // Generate a random number as the PID. + pid = new SecureRandom().nextInt(); + } else { + hostname = jvmName.substring(delimiterIndex + 1, jvmName.length()); + try { + pid = Integer.parseInt(jvmName.substring(0, delimiterIndex)); + } catch (NumberFormatException e) { + // Generate a random number as the PID if format is unexpected. + pid = new SecureRandom().nextInt(); + } + } + + return ProcessIdentifier.newBuilder() + .setHostName(hostname) + .setPid(pid) + .setStartTimestamp(TraceProtoUtils.toTimestampProto(censusTimestamp)) + .build(); + } + + // Creates library info with the given OpenCensus Java version. + @VisibleForTesting + static LibraryInfo getLibraryInfo(String currentOcJavaVersion) { + return LibraryInfo.newBuilder() + .setLanguage(Language.JAVA) + .setCoreLibraryVersion(currentOcJavaVersion) + .setExporterVersion(OC_AGENT_EXPORTER_VERSION) + .build(); + } + + // Creates service info with the given service name. + @VisibleForTesting + static ServiceInfo getServiceInfo(String serviceName) { + return ServiceInfo.newBuilder().setName(serviceName).build(); + } + + /* + * Creates an attribute map with the given MonitoredResource. + * If the given resource is not null, the attribute map contains exactly two entries: + * + * OPENCENSUS_SOURCE_TYPE: + * A string that describes the type of the resource prefixed by a domain namespace, + * e.g. “kubernetes.io/container”. + * OPENCENSUS_SOURCE_LABELS: + * A comma-separated list of labels describing the source in more detail, + * e.g. “key1=val1,key2=val2”. The allowed character set is appropriately constrained. + */ + // TODO: update the resource attributes once we have an agreement on the resource specs: + // https://github.com/census-instrumentation/opencensus-specs/pull/162. + @VisibleForTesting + static Map<String, String> getAttributeMap(@Nullable MonitoredResource resource) { + if (resource == null) { + return Collections.emptyMap(); + } else { + Map<String, String> resourceAttributes = new HashMap<String, String>(); + resourceAttributes.put(RESOURCE_TYPE_ATTRIBUTE_KEY, resource.getResourceType().name()); + resourceAttributes.put(RESOURCE_LABEL_ATTRIBUTE_KEY, getConcatenatedResourceLabels(resource)); + return resourceAttributes; + } + } + + // Encodes the attributes of MonitoredResource into a comma-separated list of labels. + // For example "aws_account=account1,instance_id=instance1,region=us-east-2". + private static String getConcatenatedResourceLabels(MonitoredResource resource) { + StringBuilder resourceLabels = new StringBuilder(); + if (resource instanceof AwsEc2InstanceMonitoredResource) { + AwsEc2InstanceMonitoredResource awsEc2Resource = (AwsEc2InstanceMonitoredResource) resource; + putIntoBuilderIfHasValue(resourceLabels, "aws_account", awsEc2Resource.getAccount()); + putIntoBuilderIfHasValue(resourceLabels, "instance_id", awsEc2Resource.getInstanceId()); + putIntoBuilderIfHasValue(resourceLabels, "region", awsEc2Resource.getRegion()); + } else if (resource instanceof GcpGceInstanceMonitoredResource) { + GcpGceInstanceMonitoredResource gceResource = (GcpGceInstanceMonitoredResource) resource; + putIntoBuilderIfHasValue(resourceLabels, "gcp_account", gceResource.getAccount()); + putIntoBuilderIfHasValue(resourceLabels, "instance_id", gceResource.getInstanceId()); + putIntoBuilderIfHasValue(resourceLabels, "zone", gceResource.getZone()); + } else if (resource instanceof GcpGkeContainerMonitoredResource) { + GcpGkeContainerMonitoredResource gkeResource = (GcpGkeContainerMonitoredResource) resource; + putIntoBuilderIfHasValue(resourceLabels, "gcp_account", gkeResource.getAccount()); + putIntoBuilderIfHasValue(resourceLabels, "instance_id", gkeResource.getInstanceId()); + putIntoBuilderIfHasValue(resourceLabels, "location", gkeResource.getZone()); + putIntoBuilderIfHasValue(resourceLabels, "namespace_name", gkeResource.getNamespaceId()); + putIntoBuilderIfHasValue(resourceLabels, "cluster_name", gkeResource.getClusterName()); + putIntoBuilderIfHasValue(resourceLabels, "container_name", gkeResource.getContainerName()); + putIntoBuilderIfHasValue(resourceLabels, "pod_name", gkeResource.getPodId()); + } + return resourceLabels.toString(); + } + + // If the given resourceValue is not empty, encodes resourceKey and resourceValue as + // "resourceKey:resourceValue" and puts it into the given StringBuilder. Otherwise skip the value. + private static void putIntoBuilderIfHasValue( + StringBuilder builder, String resourceKey, String resourceValue) { + if (resourceValue.isEmpty()) { + return; + } + if (!(builder.length() == 0)) { + // Appends the comma separator to the front, if the StringBuilder already has entries. + builder.append(','); + } + builder.append(resourceKey); + builder.append('='); + builder.append(resourceValue); + } + + private OcAgentNodeUtils() {} +} diff --git a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporter.java b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporter.java new file mode 100644 index 00000000..5c468ded --- /dev/null +++ b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporter.java @@ -0,0 +1,126 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.annotations.VisibleForTesting; +import io.opencensus.trace.Tracing; +import io.opencensus.trace.export.SpanExporter; +import io.opencensus.trace.export.SpanExporter.Handler; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +/** + * The implementation of the OpenCensus Agent (OC-Agent) Trace Exporter. + * + * <p>Example of usage: + * + * <pre>{@code + * public static void main(String[] args) { + * OcAgentTraceExporter.createAndRegister(); + * ... // Do work. + * } + * }</pre> + * + * @since 0.17 + */ +@ThreadSafe +public final class OcAgentTraceExporter { + + private static final Object monitor = new Object(); + private static final String REGISTER_NAME = OcAgentTraceExporter.class.getName(); + + @GuardedBy("monitor") + @Nullable + private static Handler handler = null; + + private OcAgentTraceExporter() {} + + /** + * Creates a {@code OcAgentTraceExporterHandler} with default configurations and registers it to + * the OpenCensus library. + * + * @since 0.17 + */ + public static void createAndRegister() { + synchronized (monitor) { + checkState(handler == null, "OC-Agent exporter is already registered."); + OcAgentTraceExporterHandler newHandler = new OcAgentTraceExporterHandler(); + registerInternal(newHandler); + } + } + + /** + * Creates a {@code OcAgentTraceExporterHandler} with the given configurations and registers it to + * the OpenCensus library. + * + * @param configuration the {@code OcAgentTraceExporterConfiguration}. + * @since 0.17 + */ + public static void createAndRegister(OcAgentTraceExporterConfiguration configuration) { + synchronized (monitor) { + checkState(handler == null, "OC-Agent exporter is already registered."); + OcAgentTraceExporterHandler newHandler = + new OcAgentTraceExporterHandler( + configuration.getEndPoint(), + configuration.getServiceName(), + configuration.getUseInsecure(), + configuration.getRetryInterval(), + configuration.getEnableConfig()); + registerInternal(newHandler); + } + } + + /** + * Registers the {@code OcAgentTraceExporterHandler}. + * + * @param spanExporter the instance of the {@code SpanExporter} where this service is registered. + */ + @VisibleForTesting + static void register(SpanExporter spanExporter, Handler handler) { + spanExporter.registerHandler(REGISTER_NAME, handler); + } + + private static void registerInternal(Handler newHandler) { + synchronized (monitor) { + handler = newHandler; + register(Tracing.getExportComponent().getSpanExporter(), newHandler); + } + } + + /** + * Unregisters the OC-Agent exporter from the OpenCensus library. + * + * @since 0.17 + */ + public static void unregister() { + unregister(Tracing.getExportComponent().getSpanExporter()); + } + + /** + * Unregisters the {@code OcAgentTraceExporterHandler}. + * + * @param spanExporter the instance of the {@code SpanExporter} from where this service is + * unregistered. + */ + @VisibleForTesting + static void unregister(SpanExporter spanExporter) { + spanExporter.unregisterHandler(REGISTER_NAME); + } +} diff --git a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterConfiguration.java b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterConfiguration.java new file mode 100644 index 00000000..c7bf1e95 --- /dev/null +++ b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterConfiguration.java @@ -0,0 +1,155 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.Duration; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Configurations for {@link OcAgentTraceExporter}. + * + * @since 0.17 + */ +@AutoValue +@Immutable +public abstract class OcAgentTraceExporterConfiguration { + + OcAgentTraceExporterConfiguration() {} + + /** + * Returns the end point of OC-Agent. The end point can be dns, ip:port, etc. + * + * @return the end point of OC-Agent. + * @since 0.17 + */ + @Nullable + public abstract String getEndPoint(); + + /** + * Returns whether to disable client transport security for the exporter's gRPC connection or not. + * + * @return whether to disable client transport security for the exporter's gRPC connection or not. + * @since 0.17 + */ + @Nullable + public abstract Boolean getUseInsecure(); + + /** + * Returns the service name to be used for this {@link OcAgentTraceExporter}. + * + * @return the service name. + * @since 0.17 + */ + @Nullable + public abstract String getServiceName(); + + /** + * Returns the retry time interval when trying to connect to Agent. + * + * @return the retry time interval. + * @since 0.17 + */ + @Nullable + public abstract Duration getRetryInterval(); + + /** + * Returns whether the {@link OcAgentTraceExporter} should handle the config streams. + * + * @return whether the {@code OcAgentTraceExporter} should handle the config streams. + * @since 0.17 + */ + public abstract boolean getEnableConfig(); + + /** + * Returns a new {@link Builder}. + * + * @return a {@code Builder}. + * @since 0.17 + */ + public static Builder builder() { + return new AutoValue_OcAgentTraceExporterConfiguration.Builder().setEnableConfig(true); + } + + /** + * Builder for {@link OcAgentTraceExporterConfiguration}. + * + * @since 0.17 + */ + @AutoValue.Builder + public abstract static class Builder { + + Builder() {} + + /** + * Sets the end point of OC-Agent server. + * + * @param endPoint the end point of OC-Agent. + * @return this. + * @since 0.17 + */ + public abstract Builder setEndPoint(String endPoint); + + /** + * Sets whether to disable client transport security for the exporter's gRPC connection or not. + * + * @param useInsecure whether disable client transport security for the exporter's gRPC + * connection. + * @return this. + * @since 0.17 + */ + public abstract Builder setUseInsecure(Boolean useInsecure); + + /** + * Sets the service name to be used for this {@link OcAgentTraceExporter}. + * + * @param serviceName the service name. + * @return this. + * @since 0.17 + */ + public abstract Builder setServiceName(String serviceName); + + /** + * Sets the retry time interval when trying to connect to Agent. + * + * @param retryInterval the retry time interval. + * @return this. + * @since 0.17 + */ + public abstract Builder setRetryInterval(Duration retryInterval); + + /** + * Sets whether {@link OcAgentTraceExporter} should handle the config streams. + * + * @param enableConfig whether {@code OcAgentTraceExporter} should handle the config streams. + * @return this. + * @since 0.17 + */ + public abstract Builder setEnableConfig(boolean enableConfig); + + // TODO(songya): add an option that controls whether to always keep the RPC connection alive. + + /** + * Builds a {@link OcAgentTraceExporterConfiguration}. + * + * @return a {@code OcAgentTraceExporterConfiguration}. + * @since 0.17 + */ + public abstract OcAgentTraceExporterConfiguration build(); + } +} diff --git a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterHandler.java b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterHandler.java new file mode 100644 index 00000000..5edc06df --- /dev/null +++ b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterHandler.java @@ -0,0 +1,62 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import io.opencensus.common.Duration; +import io.opencensus.trace.export.SpanData; +import io.opencensus.trace.export.SpanExporter.Handler; +import java.util.Collection; +import javax.annotation.Nullable; + +/** Exporting handler for OC-Agent Tracing. */ +final class OcAgentTraceExporterHandler extends Handler { + + private static final String DEFAULT_END_POINT = "localhost:55678"; + private static final String DEFAULT_SERVICE_NAME = "OpenCensus"; + private static final Duration DEFAULT_RETRY_INTERVAL = Duration.create(300, 0); // 5 minutes + + OcAgentTraceExporterHandler() { + this(null, null, null, null, /* enableConfig= */ true); + } + + OcAgentTraceExporterHandler( + @Nullable String endPoint, + @Nullable String serviceName, + @Nullable Boolean useInsecure, + @Nullable Duration retryInterval, + boolean enableConfig) { + // if (endPoint == null) { + // endPoint = DEFAULT_END_POINT; + // } + // if (serviceName == null) { + // serviceName = DEFAULT_SERVICE_NAME; + // } + // if (useInsecure == null) { + // useInsecure = false; + // } + // if (retryInterval == null) { + // retryInterval = DEFAULT_RETRY_INTERVAL; + // } + // OcAgentTraceServiceClients.startAttemptsToConnectToAgent( + // endPoint, useInsecure, serviceName, retryInterval.toMillis(), enableConfig); + } + + @Override + public void export(Collection<SpanData> spanDataList) { + // OcAgentTraceServiceClients.onExport(spanDataList); + } +} diff --git a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtils.java b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtils.java new file mode 100644 index 00000000..ec778ba6 --- /dev/null +++ b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtils.java @@ -0,0 +1,390 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import com.google.common.annotations.VisibleForTesting; +import com.google.protobuf.BoolValue; +import com.google.protobuf.ByteString; +import com.google.protobuf.UInt32Value; +import io.opencensus.common.Function; +import io.opencensus.common.Functions; +import io.opencensus.common.Timestamp; +import io.opencensus.proto.agent.trace.v1.UpdatedLibraryConfig; +import io.opencensus.proto.trace.v1.AttributeValue; +import io.opencensus.proto.trace.v1.ConstantSampler; +import io.opencensus.proto.trace.v1.ProbabilitySampler; +import io.opencensus.proto.trace.v1.Span; +import io.opencensus.proto.trace.v1.Span.Attributes; +import io.opencensus.proto.trace.v1.Span.Link; +import io.opencensus.proto.trace.v1.Span.Links; +import io.opencensus.proto.trace.v1.Span.SpanKind; +import io.opencensus.proto.trace.v1.Span.TimeEvent; +import io.opencensus.proto.trace.v1.Span.TimeEvent.MessageEvent; +import io.opencensus.proto.trace.v1.Span.Tracestate; +import io.opencensus.proto.trace.v1.Span.Tracestate.Entry; +import io.opencensus.proto.trace.v1.Status; +import io.opencensus.proto.trace.v1.TraceConfig; +import io.opencensus.proto.trace.v1.TruncatableString; +import io.opencensus.trace.Annotation; +import io.opencensus.trace.MessageEvent.Type; +import io.opencensus.trace.Sampler; +import io.opencensus.trace.Span.Kind; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.SpanId; +import io.opencensus.trace.TraceId; +import io.opencensus.trace.config.TraceParams; +import io.opencensus.trace.export.SpanData; +import io.opencensus.trace.export.SpanData.TimedEvent; +import io.opencensus.trace.export.SpanData.TimedEvents; +import io.opencensus.trace.samplers.Samplers; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/*>>> +import org.checkerframework.checker.nullness.qual.Nullable; +*/ + +/** Utilities for converting the Tracing data models in OpenCensus Java to/from OpenCensus Proto. */ +final class TraceProtoUtils { + + // Constant functions for AttributeValue. + private static final Function<String, /*@Nullable*/ AttributeValue> stringAttributeValueFunction = + new Function<String, /*@Nullable*/ AttributeValue>() { + @Override + public AttributeValue apply(String stringValue) { + return AttributeValue.newBuilder() + .setStringValue(toTruncatableStringProto(stringValue)) + .build(); + } + }; + + private static final Function<Boolean, /*@Nullable*/ AttributeValue> + booleanAttributeValueFunction = + new Function<Boolean, /*@Nullable*/ AttributeValue>() { + @Override + public AttributeValue apply(Boolean booleanValue) { + return AttributeValue.newBuilder().setBoolValue(booleanValue).build(); + } + }; + + private static final Function<Long, /*@Nullable*/ AttributeValue> longAttributeValueFunction = + new Function<Long, /*@Nullable*/ AttributeValue>() { + @Override + public AttributeValue apply(Long longValue) { + return AttributeValue.newBuilder().setIntValue(longValue).build(); + } + }; + + private static final Function<Double, /*@Nullable*/ AttributeValue> doubleAttributeValueFunction = + new Function<Double, /*@Nullable*/ AttributeValue>() { + @Override + public AttributeValue apply(Double doubleValue) { + return AttributeValue.newBuilder().setDoubleValue(doubleValue).build(); + } + }; + + /** + * Converts {@link SpanData} to {@link Span} proto. + * + * @param spanData the {@code SpanData}. + * @return proto representation of {@code Span}. + */ + static Span toSpanProto(SpanData spanData) { + SpanContext spanContext = spanData.getContext(); + TraceId traceId = spanContext.getTraceId(); + SpanId spanId = spanContext.getSpanId(); + Span.Builder spanBuilder = + Span.newBuilder() + .setTraceId(toByteString(traceId.getBytes())) + .setSpanId(toByteString(spanId.getBytes())) + .setTracestate(toTracestateProto(spanContext.getTracestate())) + .setName(toTruncatableStringProto(spanData.getName())) + .setStartTime(toTimestampProto(spanData.getStartTimestamp())) + .setAttributes(toAttributesProto(spanData.getAttributes())) + .setTimeEvents( + toTimeEventsProto(spanData.getAnnotations(), spanData.getMessageEvents())) + .setLinks(toLinksProto(spanData.getLinks())); + + Kind kind = spanData.getKind(); + if (kind != null) { + spanBuilder.setKind(toSpanKindProto(kind)); + } + + io.opencensus.trace.Status status = spanData.getStatus(); + if (status != null) { + spanBuilder.setStatus(toStatusProto(status)); + } + + Timestamp end = spanData.getEndTimestamp(); + if (end != null) { + spanBuilder.setEndTime(toTimestampProto(end)); + } + + Integer childSpanCount = spanData.getChildSpanCount(); + if (childSpanCount != null) { + spanBuilder.setChildSpanCount(UInt32Value.newBuilder().setValue(childSpanCount).build()); + } + + Boolean hasRemoteParent = spanData.getHasRemoteParent(); + if (hasRemoteParent != null) { + spanBuilder.setSameProcessAsParentSpan(BoolValue.of(!hasRemoteParent)); + } + + SpanId parentSpanId = spanData.getParentSpanId(); + if (parentSpanId != null && parentSpanId.isValid()) { + spanBuilder.setParentSpanId(toByteString(parentSpanId.getBytes())); + } + + return spanBuilder.build(); + } + + @VisibleForTesting + static ByteString toByteString(byte[] bytes) { + return ByteString.copyFrom(bytes); + } + + private static Tracestate toTracestateProto(io.opencensus.trace.Tracestate tracestate) { + return Tracestate.newBuilder().addAllEntries(toEntriesProto(tracestate.getEntries())).build(); + } + + private static List<Entry> toEntriesProto(List<io.opencensus.trace.Tracestate.Entry> entries) { + List<Entry> entriesProto = new ArrayList<Entry>(); + for (io.opencensus.trace.Tracestate.Entry entry : entries) { + entriesProto.add( + Entry.newBuilder().setKey(entry.getKey()).setValue(entry.getValue()).build()); + } + return entriesProto; + } + + private static SpanKind toSpanKindProto(Kind kind) { + switch (kind) { + case CLIENT: + return SpanKind.CLIENT; + case SERVER: + return SpanKind.SERVER; + } + return SpanKind.UNRECOGNIZED; + } + + private static Span.TimeEvents toTimeEventsProto( + TimedEvents<Annotation> annotationTimedEvents, + TimedEvents<io.opencensus.trace.MessageEvent> messageEventTimedEvents) { + Span.TimeEvents.Builder timeEventsBuilder = Span.TimeEvents.newBuilder(); + timeEventsBuilder.setDroppedAnnotationsCount(annotationTimedEvents.getDroppedEventsCount()); + for (TimedEvent<Annotation> annotation : annotationTimedEvents.getEvents()) { + timeEventsBuilder.addTimeEvent(toTimeAnnotationProto(annotation)); + } + timeEventsBuilder.setDroppedMessageEventsCount(messageEventTimedEvents.getDroppedEventsCount()); + for (TimedEvent<io.opencensus.trace.MessageEvent> networkEvent : + messageEventTimedEvents.getEvents()) { + timeEventsBuilder.addTimeEvent(toTimeMessageEventProto(networkEvent)); + } + return timeEventsBuilder.build(); + } + + private static TimeEvent toTimeAnnotationProto(TimedEvent<Annotation> timedEvent) { + TimeEvent.Builder timeEventBuilder = + TimeEvent.newBuilder().setTime(toTimestampProto(timedEvent.getTimestamp())); + Annotation annotation = timedEvent.getEvent(); + timeEventBuilder.setAnnotation( + TimeEvent.Annotation.newBuilder() + .setDescription(toTruncatableStringProto(annotation.getDescription())) + .setAttributes(toAttributesBuilderProto(annotation.getAttributes(), 0)) + .build()); + return timeEventBuilder.build(); + } + + private static TimeEvent toTimeMessageEventProto( + TimedEvent<io.opencensus.trace.MessageEvent> timedEvent) { + TimeEvent.Builder timeEventBuilder = + TimeEvent.newBuilder().setTime(toTimestampProto(timedEvent.getTimestamp())); + io.opencensus.trace.MessageEvent messageEvent = timedEvent.getEvent(); + timeEventBuilder.setMessageEvent( + TimeEvent.MessageEvent.newBuilder() + .setId(messageEvent.getMessageId()) + .setCompressedSize(messageEvent.getCompressedMessageSize()) + .setUncompressedSize(messageEvent.getUncompressedMessageSize()) + .setType(toMessageEventTypeProto(messageEvent)) + .build()); + return timeEventBuilder.build(); + } + + private static TimeEvent.MessageEvent.Type toMessageEventTypeProto( + io.opencensus.trace.MessageEvent messageEvent) { + if (messageEvent.getType() == Type.RECEIVED) { + return MessageEvent.Type.RECEIVED; + } else { + return MessageEvent.Type.SENT; + } + } + + private static Attributes toAttributesProto( + io.opencensus.trace.export.SpanData.Attributes attributes) { + Attributes.Builder attributesBuilder = + toAttributesBuilderProto( + attributes.getAttributeMap(), attributes.getDroppedAttributesCount()); + return attributesBuilder.build(); + } + + private static Attributes.Builder toAttributesBuilderProto( + Map<String, io.opencensus.trace.AttributeValue> attributes, int droppedAttributesCount) { + Attributes.Builder attributesBuilder = + Attributes.newBuilder().setDroppedAttributesCount(droppedAttributesCount); + for (Map.Entry<String, io.opencensus.trace.AttributeValue> label : attributes.entrySet()) { + AttributeValue value = toAttributeValueProto(label.getValue()); + if (value != null) { + attributesBuilder.putAttributeMap(label.getKey(), value); + } + } + return attributesBuilder; + } + + @javax.annotation.Nullable + private static AttributeValue toAttributeValueProto( + io.opencensus.trace.AttributeValue attributeValue) { + return attributeValue.match( + stringAttributeValueFunction, + booleanAttributeValueFunction, + longAttributeValueFunction, + doubleAttributeValueFunction, + Functions.</*@Nullable*/ AttributeValue>returnNull()); + } + + private static Status toStatusProto(io.opencensus.trace.Status status) { + Status.Builder statusBuilder = Status.newBuilder().setCode(status.getCanonicalCode().value()); + if (status.getDescription() != null) { + statusBuilder.setMessage(status.getDescription()); + } + return statusBuilder.build(); + } + + @VisibleForTesting + static TruncatableString toTruncatableStringProto(String string) { + return TruncatableString.newBuilder().setValue(string).setTruncatedByteCount(0).build(); + } + + static com.google.protobuf.Timestamp toTimestampProto(Timestamp timestamp) { + return com.google.protobuf.Timestamp.newBuilder() + .setSeconds(timestamp.getSeconds()) + .setNanos(timestamp.getNanos()) + .build(); + } + + private static Link.Type toLinkTypeProto(io.opencensus.trace.Link.Type type) { + if (type == io.opencensus.trace.Link.Type.PARENT_LINKED_SPAN) { + return Link.Type.PARENT_LINKED_SPAN; + } else { + return Link.Type.CHILD_LINKED_SPAN; + } + } + + private static Link toLinkProto(io.opencensus.trace.Link link) { + return Link.newBuilder() + .setTraceId(toByteString(link.getTraceId().getBytes())) + .setSpanId(toByteString(link.getSpanId().getBytes())) + .setType(toLinkTypeProto(link.getType())) + .setAttributes(toAttributesBuilderProto(link.getAttributes(), 0)) + .build(); + } + + private static Links toLinksProto(io.opencensus.trace.export.SpanData.Links links) { + final Links.Builder linksBuilder = + Links.newBuilder().setDroppedLinksCount(links.getDroppedLinksCount()); + for (io.opencensus.trace.Link link : links.getLinks()) { + linksBuilder.addLink(toLinkProto(link)); + } + return linksBuilder.build(); + } + + /** + * Converts {@link TraceParams} to {@link TraceConfig}. + * + * @param traceParams the {@code TraceParams}. + * @return {@code TraceConfig}. + */ + static TraceConfig toTraceConfigProto(TraceParams traceParams) { + TraceConfig.Builder traceConfigProtoBuilder = TraceConfig.newBuilder(); + Sampler librarySampler = traceParams.getSampler(); + + if (Samplers.alwaysSample().equals(librarySampler)) { + traceConfigProtoBuilder.setConstantSampler( + ConstantSampler.newBuilder().setDecision(true).build()); + } else if (Samplers.neverSample().equals(librarySampler)) { + traceConfigProtoBuilder.setConstantSampler( + ConstantSampler.newBuilder().setDecision(false).build()); + } else { + // TODO: consider exposing the sampling probability of ProbabilitySampler. + double samplingProbability = parseSamplingProbability(librarySampler); + traceConfigProtoBuilder.setProbabilitySampler( + ProbabilitySampler.newBuilder().setSamplingProbability(samplingProbability).build()); + } // TODO: add support for RateLimitingSampler. + + return traceConfigProtoBuilder.build(); + } + + private static double parseSamplingProbability(Sampler sampler) { + String description = sampler.getDescription(); + // description follows format "ProbabilitySampler{%.6f}", samplingProbability. + int leftParenIndex = description.indexOf("{"); + int rightParenIndex = description.indexOf("}"); + return Double.parseDouble(description.substring(leftParenIndex + 1, rightParenIndex)); + } + + /** + * Converts {@link TraceConfig} to {@link TraceParams}. + * + * @param traceConfigProto {@code TraceConfig}. + * @param currentTraceParams current {@code TraceParams}. + * @return updated {@code TraceParams}. + * @since 0.17 + */ + static TraceParams fromTraceConfigProto( + TraceConfig traceConfigProto, TraceParams currentTraceParams) { + TraceParams.Builder builder = currentTraceParams.toBuilder(); + if (traceConfigProto.hasConstantSampler()) { + ConstantSampler constantSampler = traceConfigProto.getConstantSampler(); + if (Boolean.TRUE.equals(constantSampler.getDecision())) { + builder.setSampler(Samplers.alwaysSample()); + } else { + builder.setSampler(Samplers.neverSample()); + } + } else if (traceConfigProto.hasProbabilitySampler()) { + builder.setSampler( + Samplers.probabilitySampler( + traceConfigProto.getProbabilitySampler().getSamplingProbability())); + } // TODO: add support for RateLimitingSampler. + return builder.build(); + } + + // Creates a TraceConfig proto message with current TraceParams. + static TraceConfig getCurrentTraceConfig(io.opencensus.trace.config.TraceConfig traceConfig) { + TraceParams traceParams = traceConfig.getActiveTraceParams(); + return toTraceConfigProto(traceParams); + } + + // Creates an updated TraceParams with the given UpdatedLibraryConfig message and current + // TraceParams, then applies the updated TraceParams. + static TraceParams getUpdatedTraceParams( + UpdatedLibraryConfig config, io.opencensus.trace.config.TraceConfig traceConfig) { + TraceParams currentParams = traceConfig.getActiveTraceParams(); + TraceConfig traceConfigProto = config.getConfig(); + return fromTraceConfigProto(traceConfigProto, currentParams); + } + + private TraceProtoUtils() {} +} diff --git a/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/package-info.java b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/package-info.java new file mode 100644 index 00000000..d01dd7eb --- /dev/null +++ b/exporters/trace/ocagent/src/main/java/io/opencensus/exporter/trace/ocagent/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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. + */ + +/** + * This package contains the Java implementation of the OpenCensus Agent (OC-Agent) Trace Exporter. + * + * <p>WARNING: Currently all the public classes under this package are marked as {@link + * io.opencensus.common.ExperimentalApi}. The classes and APIs under {@link + * io.opencensus.exporter.trace.ocagent} are likely to get backwards-incompatible updates in the + * future. DO NOT USE except for experimental purposes. + * + * <p>See more details on + * https://github.com/census-instrumentation/opencensus-proto/tree/master/src/opencensus/proto/agent. + */ +@io.opencensus.common.ExperimentalApi +package io.opencensus.exporter.trace.ocagent; diff --git a/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/FakeOcAgentTraceServiceGrpcImpl.java b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/FakeOcAgentTraceServiceGrpcImpl.java new file mode 100644 index 00000000..fbdb35e3 --- /dev/null +++ b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/FakeOcAgentTraceServiceGrpcImpl.java @@ -0,0 +1,169 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import com.google.common.util.concurrent.MoreExecutors; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.netty.NettyServerBuilder; +import io.grpc.stub.StreamObserver; +import io.opencensus.proto.agent.trace.v1.CurrentLibraryConfig; +import io.opencensus.proto.agent.trace.v1.ExportTraceServiceRequest; +import io.opencensus.proto.agent.trace.v1.ExportTraceServiceResponse; +import io.opencensus.proto.agent.trace.v1.TraceServiceGrpc; +import io.opencensus.proto.agent.trace.v1.UpdatedLibraryConfig; +import io.opencensus.proto.trace.v1.ConstantSampler; +import io.opencensus.proto.trace.v1.TraceConfig; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +/** Fake implementation of {@link TraceServiceGrpc}. */ +final class FakeOcAgentTraceServiceGrpcImpl extends TraceServiceGrpc.TraceServiceImplBase { + + private static final Logger logger = + Logger.getLogger(FakeOcAgentTraceServiceGrpcImpl.class.getName()); + + // Default updatedLibraryConfig uses an always sampler. + private UpdatedLibraryConfig updatedLibraryConfig = + UpdatedLibraryConfig.newBuilder() + .setConfig( + TraceConfig.newBuilder() + .setConstantSampler(ConstantSampler.newBuilder().setDecision(true).build()) + .build()) + .build(); + + private final List<CurrentLibraryConfig> currentLibraryConfigs = new ArrayList<>(); + private final List<ExportTraceServiceRequest> exportTraceServiceRequests = new ArrayList<>(); + + private final AtomicReference<StreamObserver<UpdatedLibraryConfig>> updatedConfigObserverRef = + new AtomicReference<>(); + + private final StreamObserver<CurrentLibraryConfig> currentConfigObserver = + new StreamObserver<CurrentLibraryConfig>() { + @Override + public void onNext(CurrentLibraryConfig value) { + currentLibraryConfigs.add(value); + @Nullable + StreamObserver<UpdatedLibraryConfig> updatedConfigObserver = + updatedConfigObserverRef.get(); + if (updatedConfigObserver != null) { + updatedConfigObserver.onNext(updatedLibraryConfig); + } + } + + @Override + public void onError(Throwable t) { + logger.warning("Exception thrown for config stream: " + t); + } + + @Override + public void onCompleted() {} + }; + + private final StreamObserver<ExportTraceServiceRequest> exportRequestObserver = + new StreamObserver<ExportTraceServiceRequest>() { + @Override + public void onNext(ExportTraceServiceRequest value) { + exportTraceServiceRequests.add(value); + } + + @Override + public void onError(Throwable t) { + logger.warning("Exception thrown for export stream: " + t); + } + + @Override + public void onCompleted() {} + }; + + @Override + public StreamObserver<CurrentLibraryConfig> config( + StreamObserver<UpdatedLibraryConfig> updatedLibraryConfigStreamObserver) { + updatedConfigObserverRef.set(updatedLibraryConfigStreamObserver); + return currentConfigObserver; + } + + @Override + public StreamObserver<ExportTraceServiceRequest> export( + StreamObserver<ExportTraceServiceResponse> exportTraceServiceResponseStreamObserver) { + return exportRequestObserver; + } + + // Returns the stored CurrentLibraryConfigs. + List<CurrentLibraryConfig> getCurrentLibraryConfigs() { + return Collections.unmodifiableList(currentLibraryConfigs); + } + + // Returns the stored ExportTraceServiceRequests. + List<ExportTraceServiceRequest> getExportTraceServiceRequests() { + return Collections.unmodifiableList(exportTraceServiceRequests); + } + + // Sets the UpdatedLibraryConfig that will be passed to client. + void setUpdatedLibraryConfig(UpdatedLibraryConfig updatedLibraryConfig) { + this.updatedLibraryConfig = updatedLibraryConfig; + } + + // Gets the UpdatedLibraryConfig that will be passed to client. + UpdatedLibraryConfig getUpdatedLibraryConfig() { + return updatedLibraryConfig; + } + + static void startServer(String endPoint) throws IOException { + ServerBuilder<?> builder = NettyServerBuilder.forAddress(parseEndpoint(endPoint)); + Executor executor = MoreExecutors.directExecutor(); + builder.executor(executor); + final Server server = builder.addService(new FakeOcAgentTraceServiceGrpcImpl()).build(); + server.start(); + logger.info("Server started at " + endPoint); + + Runtime.getRuntime() + .addShutdownHook( + new Thread() { + @Override + public void run() { + server.shutdown(); + } + }); + + try { + server.awaitTermination(); + } catch (InterruptedException e) { + logger.warning("Thread interrupted: " + e.getMessage()); + Thread.currentThread().interrupt(); + } + } + + private static InetSocketAddress parseEndpoint(String endPoint) { + try { + int colonIndex = endPoint.indexOf(":"); + String host = endPoint.substring(0, colonIndex); + int port = Integer.parseInt(endPoint.substring(colonIndex + 1)); + return new InetSocketAddress(host, port); + } catch (RuntimeException e) { + logger.warning("Unexpected format of end point: " + endPoint + ", use default end point."); + return new InetSocketAddress("localhost", 55678); + } + } +} diff --git a/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/FakeOcAgentTraceServiceGrpcImplTest.java b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/FakeOcAgentTraceServiceGrpcImplTest.java new file mode 100644 index 00000000..f619021b --- /dev/null +++ b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/FakeOcAgentTraceServiceGrpcImplTest.java @@ -0,0 +1,109 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import static com.google.common.truth.Truth.assertThat; + +import io.grpc.stub.StreamObserver; +import io.opencensus.proto.agent.trace.v1.CurrentLibraryConfig; +import io.opencensus.proto.agent.trace.v1.ExportTraceServiceRequest; +import io.opencensus.proto.agent.trace.v1.ExportTraceServiceResponse; +import io.opencensus.proto.agent.trace.v1.UpdatedLibraryConfig; +import io.opencensus.proto.trace.v1.ConstantSampler; +import io.opencensus.proto.trace.v1.TraceConfig; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link FakeOcAgentTraceServiceGrpcImpl}. */ +@RunWith(JUnit4.class) +public class FakeOcAgentTraceServiceGrpcImplTest { + + private final List<UpdatedLibraryConfig> updatedLibraryConfigs = new ArrayList<>(); + + private final StreamObserver<UpdatedLibraryConfig> updatedConfigObserver = + new StreamObserver<UpdatedLibraryConfig>() { + + @Override + public void onNext(UpdatedLibraryConfig value) { + updatedLibraryConfigs.add(value); + } + + @Override + public void onError(Throwable t) {} + + @Override + public void onCompleted() {} + }; + + private final StreamObserver<ExportTraceServiceResponse> exportResponseObserver = + new StreamObserver<ExportTraceServiceResponse>() { + @Override + public void onNext(ExportTraceServiceResponse value) {} + + @Override + public void onError(Throwable t) {} + + @Override + public void onCompleted() {} + }; + + private static final UpdatedLibraryConfig neverSampledLibraryConfig = + UpdatedLibraryConfig.newBuilder() + .setConfig( + TraceConfig.newBuilder() + .setConstantSampler(ConstantSampler.newBuilder().setDecision(false).build()) + .build()) + .build(); + + @Test + public void export() { + FakeOcAgentTraceServiceGrpcImpl traceServiceGrpc = new FakeOcAgentTraceServiceGrpcImpl(); + StreamObserver<ExportTraceServiceRequest> exportRequestObserver = + traceServiceGrpc.export(exportResponseObserver); + ExportTraceServiceRequest request = ExportTraceServiceRequest.getDefaultInstance(); + exportRequestObserver.onNext(request); + assertThat(traceServiceGrpc.getExportTraceServiceRequests()).containsExactly(request); + } + + @Test + public void config() { + FakeOcAgentTraceServiceGrpcImpl traceServiceGrpc = new FakeOcAgentTraceServiceGrpcImpl(); + StreamObserver<CurrentLibraryConfig> currentConfigObsever = + traceServiceGrpc.config(updatedConfigObserver); + CurrentLibraryConfig currentLibraryConfig = CurrentLibraryConfig.getDefaultInstance(); + currentConfigObsever.onNext(currentLibraryConfig); + assertThat(traceServiceGrpc.getCurrentLibraryConfigs()).containsExactly(currentLibraryConfig); + assertThat(updatedLibraryConfigs).containsExactly(traceServiceGrpc.getUpdatedLibraryConfig()); + updatedLibraryConfigs.clear(); + } + + @Test + public void config_WithNeverSampler() { + FakeOcAgentTraceServiceGrpcImpl traceServiceGrpc = new FakeOcAgentTraceServiceGrpcImpl(); + traceServiceGrpc.setUpdatedLibraryConfig(neverSampledLibraryConfig); + StreamObserver<CurrentLibraryConfig> currentConfigObsever = + traceServiceGrpc.config(updatedConfigObserver); + CurrentLibraryConfig currentLibraryConfig = CurrentLibraryConfig.getDefaultInstance(); + currentConfigObsever.onNext(currentLibraryConfig); + assertThat(traceServiceGrpc.getCurrentLibraryConfigs()).containsExactly(currentLibraryConfig); + assertThat(updatedLibraryConfigs).containsExactly(neverSampledLibraryConfig); + updatedLibraryConfigs.clear(); + } +} diff --git a/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtilsTest.java b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtilsTest.java new file mode 100644 index 00000000..813066bc --- /dev/null +++ b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentNodeUtilsTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import static com.google.common.truth.Truth.assertThat; +import static io.opencensus.exporter.trace.ocagent.OcAgentNodeUtils.OC_AGENT_EXPORTER_VERSION; +import static io.opencensus.exporter.trace.ocagent.OcAgentNodeUtils.RESOURCE_LABEL_ATTRIBUTE_KEY; +import static io.opencensus.exporter.trace.ocagent.OcAgentNodeUtils.RESOURCE_TYPE_ATTRIBUTE_KEY; + +import io.opencensus.common.Timestamp; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.AwsEc2InstanceMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGceInstanceMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGkeContainerMonitoredResource; +import io.opencensus.proto.agent.common.v1.LibraryInfo; +import io.opencensus.proto.agent.common.v1.LibraryInfo.Language; +import io.opencensus.proto.agent.common.v1.ProcessIdentifier; +import io.opencensus.proto.agent.common.v1.ServiceInfo; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link OcAgentNodeUtils}. */ +@RunWith(JUnit4.class) +public class OcAgentNodeUtilsTest { + + private static final AwsEc2InstanceMonitoredResource AWS_RESOURCE = + AwsEc2InstanceMonitoredResource.create("account1", "instance1", "us-east-2"); + private static final GcpGceInstanceMonitoredResource GCE_RESOURCE = + GcpGceInstanceMonitoredResource.create("account2", "instance2", "us-west2"); + private static final GcpGkeContainerMonitoredResource GKE_RESOURCE = + GcpGkeContainerMonitoredResource.create( + "account3", "cluster", "container", "", "instance3", "", "us-west4"); + + @Test + public void testConstants() { + assertThat(OC_AGENT_EXPORTER_VERSION).isEqualTo("0.17.0-SNAPSHOT"); + assertThat(RESOURCE_TYPE_ATTRIBUTE_KEY).isEqualTo("OPENCENSUS_SOURCE_TYPE"); + assertThat(RESOURCE_LABEL_ATTRIBUTE_KEY).isEqualTo("OPENCENSUS_SOURCE_LABELS"); + } + + @Test + public void getProcessIdentifier() { + String jvmName = "54321@my.org"; + Timestamp timestamp = Timestamp.create(10, 20); + ProcessIdentifier processIdentifier = OcAgentNodeUtils.getProcessIdentifier(jvmName, timestamp); + assertThat(processIdentifier.getHostName()).isEqualTo("my.org"); + assertThat(processIdentifier.getPid()).isEqualTo(54321); + assertThat(processIdentifier.getStartTimestamp()) + .isEqualTo(com.google.protobuf.Timestamp.newBuilder().setSeconds(10).setNanos(20).build()); + } + + @Test + public void getLibraryInfo() { + String currentOcJavaVersion = "0.16.0"; + LibraryInfo libraryInfo = OcAgentNodeUtils.getLibraryInfo(currentOcJavaVersion); + assertThat(libraryInfo.getLanguage()).isEqualTo(Language.JAVA); + assertThat(libraryInfo.getCoreLibraryVersion()).isEqualTo(currentOcJavaVersion); + assertThat(libraryInfo.getExporterVersion()).isEqualTo(OC_AGENT_EXPORTER_VERSION); + } + + @Test + public void getServiceInfo() { + String serviceName = "my-service"; + ServiceInfo serviceInfo = OcAgentNodeUtils.getServiceInfo(serviceName); + assertThat(serviceInfo.getName()).isEqualTo(serviceName); + } + + @Test + public void getAttributeMap_Null() { + Map<String, String> attributeMap = OcAgentNodeUtils.getAttributeMap(null); + assertThat(attributeMap).isEmpty(); + } + + @Test + public void getAttributeMap_AwsEc2Resource() { + Map<String, String> attributeMap = OcAgentNodeUtils.getAttributeMap(AWS_RESOURCE); + assertThat(attributeMap) + .containsExactly( + RESOURCE_TYPE_ATTRIBUTE_KEY, + "AWS_EC2_INSTANCE", + RESOURCE_LABEL_ATTRIBUTE_KEY, + "aws_account=account1,instance_id=instance1,region=us-east-2"); + } + + @Test + public void getAttributeMap_GceResource() { + Map<String, String> attributeMap = OcAgentNodeUtils.getAttributeMap(GCE_RESOURCE); + assertThat(attributeMap) + .containsExactly( + RESOURCE_TYPE_ATTRIBUTE_KEY, + "GCP_GCE_INSTANCE", + RESOURCE_LABEL_ATTRIBUTE_KEY, + "gcp_account=account2,instance_id=instance2,zone=us-west2"); + } + + @Test + public void getAttributeMap_GkeResource() { + Map<String, String> attributeMap = OcAgentNodeUtils.getAttributeMap(GKE_RESOURCE); + assertThat(attributeMap) + .containsExactly( + RESOURCE_TYPE_ATTRIBUTE_KEY, + "GCP_GKE_CONTAINER", + RESOURCE_LABEL_ATTRIBUTE_KEY, + "gcp_account=account3,instance_id=instance3,location=us-west4," + + "cluster_name=cluster,container_name=container"); + } +} diff --git a/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterConfigurationTest.java b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterConfigurationTest.java new file mode 100644 index 00000000..81bc5c60 --- /dev/null +++ b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterConfigurationTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import static com.google.common.truth.Truth.assertThat; + +import io.opencensus.common.Duration; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link OcAgentTraceExporterConfiguration}. */ +@RunWith(JUnit4.class) +public class OcAgentTraceExporterConfigurationTest { + + @Test + public void defaultConfiguration() { + OcAgentTraceExporterConfiguration configuration = + OcAgentTraceExporterConfiguration.builder().build(); + assertThat(configuration.getEndPoint()).isNull(); + assertThat(configuration.getServiceName()).isNull(); + assertThat(configuration.getUseInsecure()).isNull(); + assertThat(configuration.getRetryInterval()).isNull(); + assertThat(configuration.getEnableConfig()).isTrue(); + } + + @Test + public void setAndGet() { + Duration oneMinute = Duration.create(60, 0); + OcAgentTraceExporterConfiguration configuration = + OcAgentTraceExporterConfiguration.builder() + .setEndPoint("192.168.0.1:50051") + .setServiceName("service") + .setUseInsecure(true) + .setRetryInterval(oneMinute) + .setEnableConfig(false) + .build(); + assertThat(configuration.getEndPoint()).isEqualTo("192.168.0.1:50051"); + assertThat(configuration.getServiceName()).isEqualTo("service"); + assertThat(configuration.getUseInsecure()).isTrue(); + assertThat(configuration.getRetryInterval()).isEqualTo(oneMinute); + assertThat(configuration.getEnableConfig()).isFalse(); + } +} diff --git a/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterTest.java b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterTest.java new file mode 100644 index 00000000..c58acdb1 --- /dev/null +++ b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/OcAgentTraceExporterTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +import io.opencensus.trace.export.SpanExporter; +import io.opencensus.trace.export.SpanExporter.Handler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Unit tests for {@link OcAgentTraceExporter}. */ +@RunWith(JUnit4.class) +public class OcAgentTraceExporterTest { + @Mock private SpanExporter spanExporter; + @Mock private Handler handler; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void registerUnregisterOcAgentTraceExporter() { + OcAgentTraceExporter.register(spanExporter, handler); + verify(spanExporter) + .registerHandler( + eq("io.opencensus.exporter.trace.ocagent.OcAgentTraceExporter"), + any(OcAgentTraceExporterHandler.class)); + OcAgentTraceExporter.unregister(spanExporter); + verify(spanExporter) + .unregisterHandler(eq("io.opencensus.exporter.trace.ocagent.OcAgentTraceExporter")); + } +} diff --git a/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtilsTest.java b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtilsTest.java new file mode 100644 index 00000000..74c7c29e --- /dev/null +++ b/exporters/trace/ocagent/src/test/java/io/opencensus/exporter/trace/ocagent/TraceProtoUtilsTest.java @@ -0,0 +1,357 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * Licensed 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 io.opencensus.exporter.trace.ocagent; + +import static com.google.common.truth.Truth.assertThat; +import static io.opencensus.exporter.trace.ocagent.TraceProtoUtils.toByteString; +import static io.opencensus.exporter.trace.ocagent.TraceProtoUtils.toTruncatableStringProto; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.protobuf.BoolValue; +import com.google.protobuf.UInt32Value; +import io.opencensus.common.Timestamp; +import io.opencensus.proto.agent.trace.v1.UpdatedLibraryConfig; +import io.opencensus.proto.trace.v1.AttributeValue; +import io.opencensus.proto.trace.v1.ConstantSampler; +import io.opencensus.proto.trace.v1.ProbabilitySampler; +import io.opencensus.proto.trace.v1.Span; +import io.opencensus.proto.trace.v1.Span.SpanKind; +import io.opencensus.proto.trace.v1.Span.TimeEvent; +import io.opencensus.proto.trace.v1.Span.TimeEvent.MessageEvent; +import io.opencensus.proto.trace.v1.TraceConfig; +import io.opencensus.trace.Annotation; +import io.opencensus.trace.Link; +import io.opencensus.trace.Sampler; +import io.opencensus.trace.Span.Kind; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.SpanId; +import io.opencensus.trace.Status; +import io.opencensus.trace.TraceId; +import io.opencensus.trace.TraceOptions; +import io.opencensus.trace.Tracestate; +import io.opencensus.trace.config.TraceParams; +import io.opencensus.trace.export.SpanData; +import io.opencensus.trace.export.SpanData.TimedEvent; +import io.opencensus.trace.export.SpanData.TimedEvents; +import io.opencensus.trace.samplers.Samplers; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** Tests for {@link TraceProtoUtils}. */ +@RunWith(JUnit4.class) +public class TraceProtoUtilsTest { + + @Mock private io.opencensus.trace.config.TraceConfig mockTraceConfig; + + private static final TraceParams DEFAULT_PARAMS = TraceParams.DEFAULT; + + private static final Timestamp startTimestamp = Timestamp.create(123, 456); + private static final Timestamp eventTimestamp1 = Timestamp.create(123, 457); + private static final Timestamp eventTimestamp2 = Timestamp.create(123, 458); + private static final Timestamp eventTimestamp3 = Timestamp.create(123, 459); + private static final Timestamp endTimestamp = Timestamp.create(123, 460); + + private static final String TRACE_ID = "4bf92f3577b34da6a3ce929d0e0e4736"; + private static final String SPAN_ID = "24aa0b2d371f48c9"; + private static final String PARENT_SPAN_ID = "71da8d631536f5f1"; + private static final String SPAN_NAME = "MySpanName"; + private static final String ANNOTATION_TEXT = "MyAnnotationText"; + private static final String ATTRIBUTE_KEY_1 = "MyAttributeKey1"; + private static final String ATTRIBUTE_KEY_2 = "MyAttributeKey2"; + + private static final String FIRST_KEY = "key_1"; + private static final String SECOND_KEY = "key_2"; + private static final String FIRST_VALUE = "value.1"; + private static final String SECOND_VALUE = "value.2"; + private static final Tracestate multiValueTracestate = + Tracestate.builder().set(FIRST_KEY, FIRST_VALUE).set(SECOND_KEY, SECOND_VALUE).build(); + + private static final int DROPPED_ATTRIBUTES_COUNT = 1; + private static final int DROPPED_ANNOTATIONS_COUNT = 2; + private static final int DROPPED_NETWORKEVENTS_COUNT = 3; + private static final int DROPPED_LINKS_COUNT = 4; + private static final int CHILD_SPAN_COUNT = 13; + + private static final Annotation annotation = Annotation.fromDescription(ANNOTATION_TEXT); + private static final io.opencensus.trace.MessageEvent recvMessageEvent = + io.opencensus.trace.MessageEvent.builder(io.opencensus.trace.MessageEvent.Type.RECEIVED, 1) + .build(); + private static final io.opencensus.trace.MessageEvent sentMessageEvent = + io.opencensus.trace.MessageEvent.builder(io.opencensus.trace.MessageEvent.Type.SENT, 1) + .build(); + private static final Status status = Status.DEADLINE_EXCEEDED.withDescription("TooSlow"); + private static final SpanId parentSpanId = SpanId.fromLowerBase16(PARENT_SPAN_ID); + private static final SpanId spanId = SpanId.fromLowerBase16(SPAN_ID); + private static final TraceId traceId = TraceId.fromLowerBase16(TRACE_ID); + private static final TraceOptions traceOptions = TraceOptions.DEFAULT; + private static final SpanContext spanContext = + SpanContext.create(traceId, spanId, traceOptions, multiValueTracestate); + + private static final List<TimedEvent<Annotation>> annotationsList = + ImmutableList.of( + SpanData.TimedEvent.create(eventTimestamp1, annotation), + SpanData.TimedEvent.create(eventTimestamp3, annotation)); + private static final List<TimedEvent<io.opencensus.trace.MessageEvent>> networkEventsList = + ImmutableList.of( + SpanData.TimedEvent.create(eventTimestamp1, recvMessageEvent), + SpanData.TimedEvent.create(eventTimestamp2, sentMessageEvent)); + private static final List<Link> linksList = + ImmutableList.of(Link.fromSpanContext(spanContext, Link.Type.CHILD_LINKED_SPAN)); + + private static final SpanData.Attributes attributes = + SpanData.Attributes.create( + ImmutableMap.of( + ATTRIBUTE_KEY_1, + io.opencensus.trace.AttributeValue.longAttributeValue(10L), + ATTRIBUTE_KEY_2, + io.opencensus.trace.AttributeValue.booleanAttributeValue(true)), + DROPPED_ATTRIBUTES_COUNT); + private static final TimedEvents<Annotation> annotations = + TimedEvents.create(annotationsList, DROPPED_ANNOTATIONS_COUNT); + private static final TimedEvents<io.opencensus.trace.MessageEvent> messageEvents = + TimedEvents.create(networkEventsList, DROPPED_NETWORKEVENTS_COUNT); + private static final SpanData.Links links = SpanData.Links.create(linksList, DROPPED_LINKS_COUNT); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + Mockito.when(mockTraceConfig.getActiveTraceParams()).thenReturn(DEFAULT_PARAMS); + Mockito.doNothing() + .when(mockTraceConfig) + .updateActiveTraceParams(Mockito.any(TraceParams.class)); + } + + @Test + public void toSpanProto() { + SpanData spanData = + SpanData.create( + spanContext, + parentSpanId, + /* hasRemoteParent= */ false, + SPAN_NAME, + Kind.CLIENT, + startTimestamp, + attributes, + annotations, + messageEvents, + links, + CHILD_SPAN_COUNT, + status, + endTimestamp); + TimeEvent annotationTimeEvent1 = + TimeEvent.newBuilder() + .setAnnotation( + TimeEvent.Annotation.newBuilder() + .setDescription(toTruncatableStringProto(ANNOTATION_TEXT)) + .setAttributes(Span.Attributes.newBuilder().build()) + .build()) + .setTime( + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(eventTimestamp1.getSeconds()) + .setNanos(eventTimestamp1.getNanos()) + .build()) + .build(); + TimeEvent annotationTimeEvent2 = + TimeEvent.newBuilder() + .setAnnotation( + TimeEvent.Annotation.newBuilder() + .setDescription(toTruncatableStringProto(ANNOTATION_TEXT)) + .setAttributes(Span.Attributes.newBuilder().build()) + .build()) + .setTime( + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(eventTimestamp3.getSeconds()) + .setNanos(eventTimestamp3.getNanos()) + .build()) + .build(); + + TimeEvent sentTimeEvent = + TimeEvent.newBuilder() + .setMessageEvent( + TimeEvent.MessageEvent.newBuilder() + .setType(MessageEvent.Type.SENT) + .setId(sentMessageEvent.getMessageId())) + .setTime( + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(eventTimestamp2.getSeconds()) + .setNanos(eventTimestamp2.getNanos()) + .build()) + .build(); + TimeEvent recvTimeEvent = + TimeEvent.newBuilder() + .setMessageEvent( + TimeEvent.MessageEvent.newBuilder() + .setType(MessageEvent.Type.RECEIVED) + .setId(recvMessageEvent.getMessageId())) + .setTime( + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(eventTimestamp1.getSeconds()) + .setNanos(eventTimestamp1.getNanos()) + .build()) + .build(); + + Span.Links spanLinks = + Span.Links.newBuilder() + .setDroppedLinksCount(DROPPED_LINKS_COUNT) + .addLink( + Span.Link.newBuilder() + .setType(Span.Link.Type.CHILD_LINKED_SPAN) + .setTraceId(toByteString(traceId.getBytes())) + .setSpanId(toByteString(spanId.getBytes())) + .setAttributes(Span.Attributes.newBuilder().build()) + .build()) + .build(); + + io.opencensus.proto.trace.v1.Status spanStatus = + io.opencensus.proto.trace.v1.Status.newBuilder() + .setCode(com.google.rpc.Code.DEADLINE_EXCEEDED.getNumber()) + .setMessage("TooSlow") + .build(); + + com.google.protobuf.Timestamp startTime = + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(startTimestamp.getSeconds()) + .setNanos(startTimestamp.getNanos()) + .build(); + com.google.protobuf.Timestamp endTime = + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(endTimestamp.getSeconds()) + .setNanos(endTimestamp.getNanos()) + .build(); + + Span span = TraceProtoUtils.toSpanProto(spanData); + assertThat(span.getName()).isEqualTo(toTruncatableStringProto(SPAN_NAME)); + assertThat(span.getTraceId()).isEqualTo(toByteString(traceId.getBytes())); + assertThat(span.getSpanId()).isEqualTo(toByteString(spanId.getBytes())); + assertThat(span.getParentSpanId()).isEqualTo(toByteString(parentSpanId.getBytes())); + assertThat(span.getStartTime()).isEqualTo(startTime); + assertThat(span.getEndTime()).isEqualTo(endTime); + assertThat(span.getKind()).isEqualTo(SpanKind.CLIENT); + assertThat(span.getAttributes().getDroppedAttributesCount()) + .isEqualTo(DROPPED_ATTRIBUTES_COUNT); + // The generated attributes map contains more values (e.g. agent). We only test what we added. + assertThat(span.getAttributes().getAttributeMapMap()) + .containsEntry(ATTRIBUTE_KEY_1, AttributeValue.newBuilder().setIntValue(10L).build()); + assertThat(span.getAttributes().getAttributeMapMap()) + .containsEntry(ATTRIBUTE_KEY_2, AttributeValue.newBuilder().setBoolValue(true).build()); + assertThat(span.getTimeEvents().getDroppedMessageEventsCount()) + .isEqualTo(DROPPED_NETWORKEVENTS_COUNT); + assertThat(span.getTimeEvents().getDroppedAnnotationsCount()) + .isEqualTo(DROPPED_ANNOTATIONS_COUNT); + assertThat(span.getTimeEvents().getTimeEventList()) + .containsAllOf(annotationTimeEvent1, annotationTimeEvent2, sentTimeEvent, recvTimeEvent); + assertThat(span.getLinks()).isEqualTo(spanLinks); + assertThat(span.getStatus()).isEqualTo(spanStatus); + assertThat(span.getSameProcessAsParentSpan()).isEqualTo(BoolValue.of(true)); + assertThat(span.getChildSpanCount()) + .isEqualTo(UInt32Value.newBuilder().setValue(CHILD_SPAN_COUNT).build()); + } + + @Test + public void toTraceConfigProto_AlwaysSampler() { + assertThat(TraceProtoUtils.toTraceConfigProto(getTraceParams(Samplers.alwaysSample()))) + .isEqualTo( + TraceConfig.newBuilder() + .setConstantSampler(ConstantSampler.newBuilder().setDecision(true).build()) + .build()); + } + + @Test + public void toTraceConfigProto_NeverSampler() { + assertThat(TraceProtoUtils.toTraceConfigProto(getTraceParams(Samplers.neverSample()))) + .isEqualTo( + TraceConfig.newBuilder() + .setConstantSampler(ConstantSampler.newBuilder().setDecision(false).build()) + .build()); + } + + @Test + public void toTraceConfigProto_ProbabilitySampler() { + assertThat(TraceProtoUtils.toTraceConfigProto(getTraceParams(Samplers.probabilitySampler(0.5)))) + .isEqualTo( + TraceConfig.newBuilder() + .setProbabilitySampler( + ProbabilitySampler.newBuilder().setSamplingProbability(0.5).build()) + .build()); + } + + @Test + public void fromTraceConfigProto_AlwaysSampler() { + TraceConfig traceConfig = + TraceConfig.newBuilder() + .setConstantSampler(ConstantSampler.newBuilder().setDecision(true).build()) + .build(); + assertThat(TraceProtoUtils.fromTraceConfigProto(traceConfig, DEFAULT_PARAMS).getSampler()) + .isEqualTo(Samplers.alwaysSample()); + } + + @Test + public void fromTraceConfigProto_NeverSampler() { + TraceConfig traceConfig = + TraceConfig.newBuilder() + .setConstantSampler(ConstantSampler.newBuilder().setDecision(false).build()) + .build(); + assertThat(TraceProtoUtils.fromTraceConfigProto(traceConfig, DEFAULT_PARAMS).getSampler()) + .isEqualTo(Samplers.neverSample()); + } + + @Test + public void fromTraceConfigProto_ProbabilitySampler() { + TraceConfig traceConfig = + TraceConfig.newBuilder() + .setProbabilitySampler( + ProbabilitySampler.newBuilder().setSamplingProbability(0.01).build()) + .build(); + assertThat(TraceProtoUtils.fromTraceConfigProto(traceConfig, DEFAULT_PARAMS).getSampler()) + .isEqualTo(Samplers.probabilitySampler(0.01)); + } + + @Test + public void getCurrentTraceConfig() { + TraceConfig configProto = TraceProtoUtils.toTraceConfigProto(DEFAULT_PARAMS); + assertThat(TraceProtoUtils.getCurrentTraceConfig(mockTraceConfig)).isEqualTo(configProto); + Mockito.verify(mockTraceConfig, Mockito.times(1)).getActiveTraceParams(); + } + + @Test + public void applyUpdatedConfig() { + TraceConfig configProto = + TraceConfig.newBuilder() + .setProbabilitySampler( + ProbabilitySampler.newBuilder().setSamplingProbability(0.01).build()) + .build(); + UpdatedLibraryConfig updatedLibraryConfig = + UpdatedLibraryConfig.newBuilder().setConfig(configProto).build(); + TraceParams traceParams = + TraceProtoUtils.getUpdatedTraceParams(updatedLibraryConfig, mockTraceConfig); + TraceParams expectedParams = + DEFAULT_PARAMS.toBuilder().setSampler(Samplers.probabilitySampler(0.01)).build(); + Mockito.verify(mockTraceConfig, Mockito.times(1)).getActiveTraceParams(); + assertThat(traceParams).isEqualTo(expectedParams); + } + + private static TraceParams getTraceParams(Sampler sampler) { + return DEFAULT_PARAMS.toBuilder().setSampler(sampler).build(); + } +} |