From 7edf0c605414a38f14d719747235d3993791136e Mon Sep 17 00:00:00 2001 From: Kristen Kozak Date: Thu, 17 Aug 2017 12:00:51 -0700 Subject: Use different package names in each artifact. (fixes #379) This commit uses the following package name prefixes in each artifact, in order to ensure that no package is split across artifacts: opencensus-api: io.opencensus opencensus-impl-core: io.opencensus.implcore opencensus-impl: io.opencensus.impl opencensus-impl-lite: io.opencensus.impllite --- .../io/opencensus/impl/common/MillisClock.java | 45 -- .../io/opencensus/impl/internal/EventQueue.java | 31 -- .../opencensus/impl/internal/SimpleEventQueue.java | 26 -- .../impl/internal/TimestampConverter.java | 48 -- .../java/io/opencensus/impl/internal/VarInt.java | 279 ----------- .../io/opencensus/impl/trace/SpanBuilderImpl.java | 197 -------- .../java/io/opencensus/impl/trace/SpanImpl.java | 516 --------------------- .../opencensus/impl/trace/StartEndHandlerImpl.java | 124 ----- .../impl/trace/TraceComponentImplBase.java | 87 ---- .../java/io/opencensus/impl/trace/TracerImpl.java | 49 -- .../impl/trace/config/TraceConfigImpl.java | 40 -- .../impl/trace/export/ExportComponentImpl.java | 79 ---- .../impl/trace/export/RunningSpanStoreImpl.java | 86 ---- .../impl/trace/export/SampledSpanStoreImpl.java | 291 ------------ .../impl/trace/export/SpanExporterImpl.java | 187 -------- .../trace/internal/ConcurrentIntrusiveList.java | 174 ------- .../impl/trace/internal/RandomHandler.java | 47 -- .../impl/trace/propagation/BinaryFormatImpl.java | 117 ----- .../propagation/PropagationComponentImpl.java | 27 -- .../io/opencensus/implcore/common/MillisClock.java | 45 ++ .../opencensus/implcore/internal/EventQueue.java | 31 ++ .../implcore/internal/SimpleEventQueue.java | 26 ++ .../implcore/internal/TimestampConverter.java | 48 ++ .../io/opencensus/implcore/internal/VarInt.java | 279 +++++++++++ .../opencensus/implcore/trace/SpanBuilderImpl.java | 197 ++++++++ .../io/opencensus/implcore/trace/SpanImpl.java | 516 +++++++++++++++++++++ .../implcore/trace/StartEndHandlerImpl.java | 124 +++++ .../implcore/trace/TraceComponentImplBase.java | 87 ++++ .../io/opencensus/implcore/trace/TracerImpl.java | 49 ++ .../implcore/trace/config/TraceConfigImpl.java | 40 ++ .../implcore/trace/export/ExportComponentImpl.java | 79 ++++ .../trace/export/RunningSpanStoreImpl.java | 86 ++++ .../trace/export/SampledSpanStoreImpl.java | 291 ++++++++++++ .../implcore/trace/export/SpanExporterImpl.java | 187 ++++++++ .../trace/internal/ConcurrentIntrusiveList.java | 174 +++++++ .../implcore/trace/internal/RandomHandler.java | 47 ++ .../trace/propagation/BinaryFormatImpl.java | 117 +++++ .../propagation/PropagationComponentImpl.java | 27 ++ 38 files changed, 2450 insertions(+), 2450 deletions(-) delete mode 100644 impl_core/src/main/java/io/opencensus/impl/common/MillisClock.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/internal/EventQueue.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/internal/SimpleEventQueue.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/internal/TimestampConverter.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/internal/VarInt.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/SpanBuilderImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/SpanImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/StartEndHandlerImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/TraceComponentImplBase.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/TracerImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/config/TraceConfigImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/export/ExportComponentImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/export/RunningSpanStoreImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/export/SampledSpanStoreImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/export/SpanExporterImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/internal/ConcurrentIntrusiveList.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/internal/RandomHandler.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/propagation/BinaryFormatImpl.java delete mode 100644 impl_core/src/main/java/io/opencensus/impl/trace/propagation/PropagationComponentImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/common/MillisClock.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/internal/EventQueue.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/internal/SimpleEventQueue.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/internal/TimestampConverter.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/internal/VarInt.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/SpanBuilderImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/SpanImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/StartEndHandlerImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/TraceComponentImplBase.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/TracerImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/config/TraceConfigImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/export/ExportComponentImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/export/RunningSpanStoreImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/export/SampledSpanStoreImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/export/SpanExporterImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/internal/ConcurrentIntrusiveList.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/internal/RandomHandler.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java create mode 100644 impl_core/src/main/java/io/opencensus/implcore/trace/propagation/PropagationComponentImpl.java (limited to 'impl_core/src/main') diff --git a/impl_core/src/main/java/io/opencensus/impl/common/MillisClock.java b/impl_core/src/main/java/io/opencensus/impl/common/MillisClock.java deleted file mode 100644 index 50f03d48..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/common/MillisClock.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.common; - -import io.opencensus.common.Clock; -import io.opencensus.common.Timestamp; -import javax.annotation.concurrent.ThreadSafe; - -/** A {@link Clock} that uses {@link System#currentTimeMillis()} and {@link System#nanoTime()}. */ -@ThreadSafe -public final class MillisClock extends Clock { - private static final MillisClock INSTANCE = new MillisClock(); - - private MillisClock() {} - - /** - * Returns a {@code MillisClock}. - * - * @return a {@code MillisClock}. - */ - public static MillisClock getInstance() { - return INSTANCE; - } - - @Override - public Timestamp now() { - return Timestamp.fromMillis(System.currentTimeMillis()); - } - - @Override - public long nowNanos() { - return System.nanoTime(); - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/internal/EventQueue.java b/impl_core/src/main/java/io/opencensus/impl/internal/EventQueue.java deleted file mode 100644 index a7ef7581..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/internal/EventQueue.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.internal; - -/** A queue that processes events. See {@code DisruptorEventQueue} for an example. */ -public interface EventQueue { - void enqueue(Entry entry); - - /** - * Base interface to be used for all entries in {@link EventQueue}. For example usage, see {@code - * DisruptorEventQueue}. - */ - public interface Entry { - /** - * Process the event associated with this entry. This will be called for every event in the - * associated {@link EventQueue}. - */ - void process(); - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/internal/SimpleEventQueue.java b/impl_core/src/main/java/io/opencensus/impl/internal/SimpleEventQueue.java deleted file mode 100644 index 27481cac..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/internal/SimpleEventQueue.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.internal; - -/** - * An {@link EventQueue} that processes events in the current thread. This class can be used for - * testing. - */ -public class SimpleEventQueue implements EventQueue { - - @Override - public void enqueue(Entry entry) { - entry.process(); - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/internal/TimestampConverter.java b/impl_core/src/main/java/io/opencensus/impl/internal/TimestampConverter.java deleted file mode 100644 index 02f0266e..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/internal/TimestampConverter.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.internal; - -import io.opencensus.common.Clock; -import io.opencensus.common.Timestamp; -import javax.annotation.concurrent.Immutable; - -/** - * This class provides a mechanism for converting {@link System#nanoTime() nanoTime} values to - * {@link Timestamp}. - */ -@Immutable -public final class TimestampConverter { - private final Timestamp timestamp; - private final long nanoTime; - - // Returns a WallTimeConverter initialized to now. - public static TimestampConverter now(Clock clock) { - return new TimestampConverter(clock.now(), clock.nowNanos()); - } - - /** - * Converts a {@link System#nanoTime() nanoTime} value to {@link Timestamp}. - * - * @param nanoTime value to convert. - * @return the {@code Timestamp} representation of the {@code time}. - */ - public Timestamp convertNanoTime(long nanoTime) { - return timestamp.addNanos(nanoTime - this.nanoTime); - } - - private TimestampConverter(Timestamp timestamp, long nanoTime) { - this.timestamp = timestamp; - this.nanoTime = nanoTime; - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/internal/VarInt.java b/impl_core/src/main/java/io/opencensus/impl/internal/VarInt.java deleted file mode 100644 index 1b7fc265..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/internal/VarInt.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright 2016, Google Inc. - * 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.impl.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; - -/** Common methods to encode and decode varints and varlongs into ByteBuffers and arrays. */ -// CHECKSTYLE:OFF -public class VarInt { - - /** Maximum encoded size of 32-bit positive integers (in bytes) */ - public static final int MAX_VARINT_SIZE = 5; - - /** maximum encoded size of 64-bit longs, and negative 32-bit ints (in bytes) */ - public static final int MAX_VARLONG_SIZE = 10; - - private VarInt() {} - - /** - * Returns the encoding size in bytes of its input value. - * - * @param i the integer to be measured - * @return the encoding size in bytes of its input value - */ - public static int varIntSize(int i) { - int result = 0; - do { - result++; - i >>>= 7; - } while (i != 0); - return result; - } - - /** - * Reads a varint from src, places its values into the first element of dst and returns the offset - * in to src of the first byte after the varint. - * - * @param src source buffer to retrieve from - * @param offset offset within src - * @param dst the resulting int value - * @return the updated offset after reading the varint - */ - public static int getVarInt(byte[] src, int offset, int[] dst) { - int result = 0; - int shift = 0; - int b; - do { - if (shift >= 32) { - // Out of range - throw new IndexOutOfBoundsException("varint too long"); - } - // Get 7 bits from next byte - b = src[offset++]; - result |= (b & 0x7F) << shift; - shift += 7; - } while ((b & 0x80) != 0); - dst[0] = result; - return offset; - } - - /** - * Encodes an integer in a variable-length encoding, 7 bits per byte, into a destination byte[], - * following the protocol buffer convention. - * - * @param v the int value to write to sink - * @param sink the sink buffer to write to - * @param offset the offset within sink to begin writing - * @return the updated offset after writing the varint - */ - public static int putVarInt(int v, byte[] sink, int offset) { - do { - // Encode next 7 bits + terminator bit - int bits = v & 0x7F; - v >>>= 7; - byte b = (byte) (bits + ((v != 0) ? 0x80 : 0)); - sink[offset++] = b; - } while (v != 0); - return offset; - } - - /** - * Reads a varint from the current position of the given ByteBuffer and returns the decoded value - * as 32 bit integer. - * - *

The position of the buffer is advanced to the first byte after the decoded varint. - * - * @param src the ByteBuffer to get the var int from - * @return The integer value of the decoded varint - */ - public static int getVarInt(ByteBuffer src) { - int tmp; - if ((tmp = src.get()) >= 0) { - return tmp; - } - int result = tmp & 0x7f; - if ((tmp = src.get()) >= 0) { - result |= tmp << 7; - } else { - result |= (tmp & 0x7f) << 7; - if ((tmp = src.get()) >= 0) { - result |= tmp << 14; - } else { - result |= (tmp & 0x7f) << 14; - if ((tmp = src.get()) >= 0) { - result |= tmp << 21; - } else { - result |= (tmp & 0x7f) << 21; - result |= (tmp = src.get()) << 28; - while (tmp < 0) { - // We get into this loop only in the case of overflow. - // By doing this, we can call getVarInt() instead of - // getVarLong() when we only need an int. - tmp = src.get(); - } - } - } - } - return result; - } - - /** - * Encodes an integer in a variable-length encoding, 7 bits per byte, to a ByteBuffer sink. - * - * @param v the value to encode - * @param sink the ByteBuffer to add the encoded value - */ - public static void putVarInt(int v, ByteBuffer sink) { - while (true) { - int bits = v & 0x7f; - v >>>= 7; - if (v == 0) { - sink.put((byte) bits); - return; - } - sink.put((byte) (bits | 0x80)); - } - } - - /** - * Reads a varint from the given InputStream and returns the decoded value as an int. - * - * @param inputStream the InputStream to read from - */ - public static int getVarInt(InputStream inputStream) throws IOException { - int result = 0; - int shift = 0; - int b; - do { - if (shift >= 32) { - // Out of range - throw new IndexOutOfBoundsException("varint too long"); - } - // Get 7 bits from next byte - b = inputStream.read(); - result |= (b & 0x7F) << shift; - shift += 7; - } while ((b & 0x80) != 0); - return result; - } - - /** - * Encodes an integer in a variable-length encoding, 7 bits per byte, and writes it to the given - * OutputStream. - * - * @param v the value to encode - * @param outputStream the OutputStream to write to - */ - public static void putVarInt(int v, OutputStream outputStream) throws IOException { - byte[] bytes = new byte[varIntSize(v)]; - putVarInt(v, bytes, 0); - outputStream.write(bytes); - } - - /** - * Returns the encoding size in bytes of its input value. - * - * @param v the long to be measured - * @return the encoding size in bytes of a given long value. - */ - public static int varLongSize(long v) { - int result = 0; - do { - result++; - v >>>= 7; - } while (v != 0); - return result; - } - - /** - * Reads an up to 64 bit long varint from the current position of the given ByteBuffer and returns - * the decoded value as long. - * - *

The position of the buffer is advanced to the first byte after the decoded varint. - * - * @param src the ByteBuffer to get the var int from - * @return The integer value of the decoded long varint - */ - public static long getVarLong(ByteBuffer src) { - long tmp; - if ((tmp = src.get()) >= 0) { - return tmp; - } - long result = tmp & 0x7f; - if ((tmp = src.get()) >= 0) { - result |= tmp << 7; - } else { - result |= (tmp & 0x7f) << 7; - if ((tmp = src.get()) >= 0) { - result |= tmp << 14; - } else { - result |= (tmp & 0x7f) << 14; - if ((tmp = src.get()) >= 0) { - result |= tmp << 21; - } else { - result |= (tmp & 0x7f) << 21; - if ((tmp = src.get()) >= 0) { - result |= tmp << 28; - } else { - result |= (tmp & 0x7f) << 28; - if ((tmp = src.get()) >= 0) { - result |= tmp << 35; - } else { - result |= (tmp & 0x7f) << 35; - if ((tmp = src.get()) >= 0) { - result |= tmp << 42; - } else { - result |= (tmp & 0x7f) << 42; - if ((tmp = src.get()) >= 0) { - result |= tmp << 49; - } else { - result |= (tmp & 0x7f) << 49; - if ((tmp = src.get()) >= 0) { - result |= tmp << 56; - } else { - result |= (tmp & 0x7f) << 56; - result |= ((long) src.get()) << 63; - } - } - } - } - } - } - } - } - return result; - } - - /** - * Encodes a long integer in a variable-length encoding, 7 bits per byte, to a ByteBuffer sink. - * - * @param v the value to encode - * @param sink the ByteBuffer to add the encoded value - */ - public static void putVarLong(long v, ByteBuffer sink) { - while (true) { - int bits = ((int) v) & 0x7f; - v >>>= 7; - if (v == 0) { - sink.put((byte) bits); - return; - } - sink.put((byte) (bits | 0x80)); - } - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/SpanBuilderImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/SpanBuilderImpl.java deleted file mode 100644 index 2bef6540..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/SpanBuilderImpl.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace; - -import static com.google.common.base.Preconditions.checkNotNull; - -import io.opencensus.common.Clock; -import io.opencensus.impl.internal.TimestampConverter; -import io.opencensus.impl.trace.internal.RandomHandler; -import io.opencensus.trace.Link; -import io.opencensus.trace.Link.Type; -import io.opencensus.trace.Sampler; -import io.opencensus.trace.Span; -import io.opencensus.trace.SpanBuilder; -import io.opencensus.trace.SpanContext; -import io.opencensus.trace.SpanId; -import io.opencensus.trace.TraceId; -import io.opencensus.trace.TraceOptions; -import io.opencensus.trace.config.TraceConfig; -import io.opencensus.trace.config.TraceParams; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Random; -import javax.annotation.Nullable; - -/** Implementation of the {@link SpanBuilder}. */ -final class SpanBuilderImpl extends SpanBuilder { - private final Options options; - - private final String name; - private final Span parent; - private final SpanContext remoteParentSpanContext; - private Sampler sampler; - private List parentLinks = Collections.emptyList(); - private Boolean recordEvents; - - private SpanImpl startSpanInternal( - @Nullable SpanContext parent, - @Nullable Boolean hasRemoteParent, - String name, - Sampler sampler, - List parentLinks, - Boolean recordEvents, - @Nullable TimestampConverter timestampConverter) { - TraceParams activeTraceParams = options.traceConfig.getActiveTraceParams(); - Random random = options.randomHandler.current(); - TraceId traceId; - SpanId spanId = SpanId.generateRandomId(random); - SpanId parentSpanId = null; - TraceOptions.Builder traceOptionsBuilder; - if (parent == null || !parent.isValid()) { - // New root span. - traceId = TraceId.generateRandomId(random); - traceOptionsBuilder = TraceOptions.builder(); - // This is a root span so no remote or local parent. - hasRemoteParent = null; - } else { - // New child span. - traceId = parent.getTraceId(); - parentSpanId = parent.getSpanId(); - traceOptionsBuilder = TraceOptions.builder(parent.getTraceOptions()); - } - if (sampler == null) { - sampler = activeTraceParams.getSampler(); - } - if (sampler.shouldSample(parent, hasRemoteParent, traceId, spanId, name, parentLinks)) { - traceOptionsBuilder.setIsSampled(); - } - TraceOptions traceOptions = traceOptionsBuilder.build(); - EnumSet spanOptions = EnumSet.noneOf(Span.Options.class); - if (traceOptions.isSampled() || Boolean.TRUE.equals(recordEvents)) { - spanOptions.add(Span.Options.RECORD_EVENTS); - } - SpanImpl span = - SpanImpl.startSpan( - SpanContext.create(traceId, spanId, traceOptions), - spanOptions, - name, - parentSpanId, - hasRemoteParent, - activeTraceParams, - options.startEndHandler, - timestampConverter, - options.clock); - linkSpans(span, parentLinks); - return span; - } - - private static void linkSpans(Span span, List parentLinks) { - if (!parentLinks.isEmpty()) { - Link childLink = Link.fromSpanContext(span.getContext(), Type.CHILD_LINKED_SPAN); - for (Span linkedSpan : parentLinks) { - linkedSpan.addLink(childLink); - span.addLink(Link.fromSpanContext(linkedSpan.getContext(), Type.PARENT_LINKED_SPAN)); - } - } - } - - static SpanBuilderImpl createWithParent(String spanName, @Nullable Span parent, Options options) { - return new SpanBuilderImpl(spanName, null, parent, options); - } - - static SpanBuilderImpl createWithRemoteParent( - String spanName, @Nullable SpanContext remoteParentSpanContext, Options options) { - return new SpanBuilderImpl(spanName, remoteParentSpanContext, null, options); - } - - private SpanBuilderImpl( - String name, - @Nullable SpanContext remoteParentSpanContext, - @Nullable Span parent, - Options options) { - this.name = checkNotNull(name, "name"); - this.parent = parent; - this.remoteParentSpanContext = remoteParentSpanContext; - this.options = options; - } - - @Override - public SpanImpl startSpan() { - SpanContext parentContext = remoteParentSpanContext; - Boolean hasRemoteParent = Boolean.TRUE; - TimestampConverter timestampConverter = null; - if (remoteParentSpanContext == null) { - // This is not a child of a remote Span. Get the parent SpanContext from the parent Span if - // any. - Span parent = this.parent; - hasRemoteParent = Boolean.FALSE; - if (parent != null) { - parentContext = parent.getContext(); - // Pass the timestamp converter from the parent to ensure that the recorded events are in - // the right order. Implementation uses System.nanoTime() which is monotonically increasing. - if (parent instanceof SpanImpl) { - timestampConverter = ((SpanImpl) parent).getTimestampConverter(); - } - } else { - hasRemoteParent = null; - } - } - return startSpanInternal( - parentContext, - hasRemoteParent, - name, - sampler, - parentLinks, - recordEvents, - timestampConverter); - } - - static final class Options { - private final RandomHandler randomHandler; - private final SpanImpl.StartEndHandler startEndHandler; - private final Clock clock; - private final TraceConfig traceConfig; - - Options( - RandomHandler randomHandler, - SpanImpl.StartEndHandler startEndHandler, - Clock clock, - TraceConfig traceConfig) { - this.randomHandler = checkNotNull(randomHandler, "randomHandler"); - this.startEndHandler = checkNotNull(startEndHandler, "startEndHandler"); - this.clock = checkNotNull(clock, "clock"); - this.traceConfig = checkNotNull(traceConfig, "traceConfig"); - } - } - - @Override - public SpanBuilderImpl setSampler(Sampler sampler) { - this.sampler = checkNotNull(sampler, "sampler"); - return this; - } - - @Override - public SpanBuilderImpl setParentLinks(List parentLinks) { - this.parentLinks = checkNotNull(parentLinks, "parentLinks"); - return this; - } - - @Override - public SpanBuilderImpl setRecordEvents(boolean recordEvents) { - this.recordEvents = recordEvents; - return this; - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/SpanImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/SpanImpl.java deleted file mode 100644 index d2a66609..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/SpanImpl.java +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.EvictingQueue; -import io.opencensus.common.Clock; -import io.opencensus.impl.internal.TimestampConverter; -import io.opencensus.impl.trace.internal.ConcurrentIntrusiveList.Element; -import io.opencensus.trace.Annotation; -import io.opencensus.trace.AttributeValue; -import io.opencensus.trace.EndSpanOptions; -import io.opencensus.trace.Link; -import io.opencensus.trace.NetworkEvent; -import io.opencensus.trace.Span; -import io.opencensus.trace.SpanContext; -import io.opencensus.trace.SpanId; -import io.opencensus.trace.Status; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.config.TraceParams; -import io.opencensus.trace.export.SpanData; -import io.opencensus.trace.export.SpanData.TimedEvent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.Nullable; -import javax.annotation.concurrent.GuardedBy; -import javax.annotation.concurrent.ThreadSafe; - -/** Implementation for the {@link Span} class. */ -@ThreadSafe -public final class SpanImpl extends Span implements Element { - private static final Logger logger = Logger.getLogger(Tracer.class.getName()); - - // The parent SpanId of this span. Null if this is a root span. - private final SpanId parentSpanId; - // True if the parent is on a different process. - private final Boolean hasRemoteParent; - // Active trace params when the Span was created. - private final TraceParams traceParams; - // Handler called when the span starts and ends. - private final StartEndHandler startEndHandler; - // The displayed name of the span. - private final String name; - // The clock used to get the time. - private final Clock clock; - // The time converter used to convert nano time to Timestamp. This is needed because Java has - // millisecond granularity for Timestamp and tracing events are recorded more often. - private final TimestampConverter timestampConverter; - // The start time of the span. Set when the span is created iff the RECORD_EVENTS options is - // set, otherwise 0. - private final long startNanoTime; - // Set of recorded attributes. DO NOT CALL any other method that changes the ordering of events. - @GuardedBy("this") - private AttributesWithCapacity attributes; - // List of recorded annotations. - @GuardedBy("this") - private TraceEvents> annotations; - // List of recorded network events. - @GuardedBy("this") - private TraceEvents> networkEvents; - // List of recorded links to parent and child spans. - @GuardedBy("this") - private TraceEvents links; - // The status of the span. Set when the span is ended iff the RECORD_EVENTS options is set. - @GuardedBy("this") - private Status status; - // The end time of the span. Set when the span is ended iff the RECORD_EVENTS options is set, - // otherwise 0. - @GuardedBy("this") - private long endNanoTime; - // True if the span is ended. - @GuardedBy("this") - private boolean hasBeenEnded; - - // Pointers for the ConcurrentIntrusiveList$Element. Guarded by the ConcurrentIntrusiveList. - private SpanImpl next = null; - private SpanImpl prev = null; - - /** - * Creates and starts a span with the given configuration. - * - * @param context supplies the trace_id and span_id for the newly started span. - * @param options the options for the new span, importantly Options.RECORD_EVENTS. - * @param name the displayed name for the new span. - * @param parentSpanId the span_id of the parent span, or null if the new span is a root span. - * @param hasRemoteParent {@code true} if the parentContext is remote. {@code null} if this is a - * root span. - * @param traceParams trace parameters like sampler and probability. - * @param startEndHandler handler called when the span starts and ends. - * @param timestampConverter null if the span is a root span or the parent is not sampled. If the - * parent is sampled, we should use the same converter to ensure ordering between tracing - * events. - * @param clock the clock used to get the time. - * @return a new and started span. - */ - @VisibleForTesting - public static SpanImpl startSpan( - SpanContext context, - @Nullable EnumSet options, - String name, - @Nullable SpanId parentSpanId, - @Nullable Boolean hasRemoteParent, - TraceParams traceParams, - StartEndHandler startEndHandler, - @Nullable TimestampConverter timestampConverter, - Clock clock) { - SpanImpl span = - new SpanImpl( - context, - options, - name, - parentSpanId, - hasRemoteParent, - traceParams, - startEndHandler, - timestampConverter, - clock); - // Call onStart here instead of calling in the constructor to make sure the span is completely - // initialized. - if (span.getOptions().contains(Options.RECORD_EVENTS)) { - startEndHandler.onStart(span); - } - return span; - } - - /** - * Returns the name of the {@code Span}. - * - * @return the name of the {@code Span}. - */ - public String getName() { - return name; - } - - /** - * Returns the status of the {@code Span}. If not set defaults to {@link Status#OK}. - * - * @return the status of the {@code Span}. - */ - public Status getStatus() { - synchronized (this) { - return status; - } - } - - /** - * Returns the end nano time (see {@link System#nanoTime()}). If the current {@code Span} is not - * ended then returns {@link Clock#nowNanos()}. - * - * @return the end nano time. - */ - public long getEndNanoTime() { - synchronized (this) { - return hasBeenEnded ? endNanoTime : clock.nowNanos(); - } - } - - /** - * Returns the latency of the {@code Span} in nanos. If still active then returns now() - start - * time. - * - * @return the latency of the {@code Span} in nanos. - */ - public long getLatencyNs() { - synchronized (this) { - return hasBeenEnded ? endNanoTime - startNanoTime : clock.nowNanos() - startNanoTime; - } - } - - /** - * Returns the {@code TimestampConverter} used by this {@code Span}. - * - * @return the {@code TimestampConverter} used by this {@code Span}. - */ - @Nullable - TimestampConverter getTimestampConverter() { - return timestampConverter; - } - - /** - * Returns an immutable representation of all the data from this {@code Span}. - * - * @return an immutable representation of all the data from this {@code Span}. - * @throws IllegalStateException if the Span doesn't have RECORD_EVENTS option. - */ - public SpanData toSpanData() { - checkState( - getOptions().contains(Options.RECORD_EVENTS), - "Getting SpanData for a Span without RECORD_EVENTS option."); - synchronized (this) { - SpanData.Attributes attributesSpanData = - attributes == null - ? SpanData.Attributes.create(Collections.emptyMap(), 0) - : SpanData.Attributes.create(attributes, attributes.getNumberOfDroppedAttributes()); - SpanData.TimedEvents annotationsSpanData = - createTimedEvents(annotations, timestampConverter); - SpanData.TimedEvents networkEventsSpanData = - createTimedEvents(networkEvents, timestampConverter); - SpanData.Links linksSpanData = - links == null - ? SpanData.Links.create(Collections.emptyList(), 0) - : SpanData.Links.create( - new ArrayList(links.events), links.getNumberOfDroppedEvents()); - return SpanData.create( - getContext(), - parentSpanId, - hasRemoteParent, - name, - timestampConverter.convertNanoTime(startNanoTime), - attributesSpanData, - annotationsSpanData, - networkEventsSpanData, - linksSpanData, - null, // Not supported yet. - hasBeenEnded ? status : null, - hasBeenEnded ? timestampConverter.convertNanoTime(endNanoTime) : null); - } - } - - @Override - public void addAttributes(Map attributes) { - if (!getOptions().contains(Options.RECORD_EVENTS)) { - return; - } - synchronized (this) { - if (hasBeenEnded) { - logger.log(Level.FINE, "Calling addAttributes() on an ended Span."); - return; - } - getInitializedAttributes().addAttributes(attributes); - } - } - - @Override - public void addAnnotation(String description, Map attributes) { - if (!getOptions().contains(Options.RECORD_EVENTS)) { - return; - } - synchronized (this) { - if (hasBeenEnded) { - logger.log(Level.FINE, "Calling addAnnotation() on an ended Span."); - return; - } - getInitializedAnnotations() - .addEvent( - new EventWithNanoTime( - clock.nowNanos(), - Annotation.fromDescriptionAndAttributes(description, attributes))); - } - } - - @Override - public void addAnnotation(Annotation annotation) { - if (!getOptions().contains(Options.RECORD_EVENTS)) { - return; - } - synchronized (this) { - if (hasBeenEnded) { - logger.log(Level.FINE, "Calling addAnnotation() on an ended Span."); - return; - } - getInitializedAnnotations() - .addEvent( - new EventWithNanoTime( - clock.nowNanos(), checkNotNull(annotation, "annotation"))); - } - } - - @Override - public void addNetworkEvent(NetworkEvent networkEvent) { - if (!getOptions().contains(Options.RECORD_EVENTS)) { - return; - } - synchronized (this) { - if (hasBeenEnded) { - logger.log(Level.FINE, "Calling addNetworkEvent() on an ended Span."); - return; - } - getInitializedNetworkEvents() - .addEvent( - new EventWithNanoTime( - clock.nowNanos(), checkNotNull(networkEvent, "networkEvent"))); - } - } - - @Override - public void addLink(Link link) { - if (!getOptions().contains(Options.RECORD_EVENTS)) { - return; - } - synchronized (this) { - if (hasBeenEnded) { - logger.log(Level.FINE, "Calling addLink() on an ended Span."); - return; - } - getInitializedLinks().addEvent(checkNotNull(link, "link")); - } - } - - @Override - public void end(EndSpanOptions options) { - if (!getOptions().contains(Options.RECORD_EVENTS)) { - return; - } - synchronized (this) { - if (hasBeenEnded) { - logger.log(Level.FINE, "Calling end() on an ended Span."); - return; - } - status = options.getStatus(); - endNanoTime = clock.nowNanos(); - hasBeenEnded = true; - } - startEndHandler.onEnd(this); - } - - @GuardedBy("this") - private AttributesWithCapacity getInitializedAttributes() { - if (attributes == null) { - attributes = new AttributesWithCapacity(traceParams.getMaxNumberOfAttributes()); - } - return attributes; - } - - @GuardedBy("this") - private TraceEvents> getInitializedAnnotations() { - if (annotations == null) { - annotations = - new TraceEvents>(traceParams.getMaxNumberOfAnnotations()); - } - return annotations; - } - - @GuardedBy("this") - private TraceEvents> getInitializedNetworkEvents() { - if (networkEvents == null) { - networkEvents = - new TraceEvents>( - traceParams.getMaxNumberOfNetworkEvents()); - } - return networkEvents; - } - - @GuardedBy("this") - private TraceEvents getInitializedLinks() { - if (links == null) { - links = new TraceEvents(traceParams.getMaxNumberOfLinks()); - } - return links; - } - - private static SpanData.TimedEvents createTimedEvents( - TraceEvents> events, TimestampConverter timestampConverter) { - if (events == null) { - return SpanData.TimedEvents.create(Collections.>emptyList(), 0); - } - List> eventsList = new ArrayList>(events.events.size()); - for (EventWithNanoTime networkEvent : events.events) { - eventsList.add(networkEvent.toSpanDataTimedEvent(timestampConverter)); - } - return SpanData.TimedEvents.create(eventsList, events.getNumberOfDroppedEvents()); - } - - @Override - public SpanImpl getNext() { - return next; - } - - @Override - public void setNext(SpanImpl element) { - next = element; - } - - @Override - public SpanImpl getPrev() { - return prev; - } - - @Override - public void setPrev(SpanImpl element) { - prev = element; - } - - /** - * Interface to handle the start and end operations for a {@link Span} only when the {@code Span} - * has {@link Options#RECORD_EVENTS} option. - * - *

Implementation must avoid high overhead work in any of the methods because the code is - * executed on the critical path. - * - *

One instance can be called by multiple threads in the same time, so the implementation must - * be thread-safe. - */ - public interface StartEndHandler { - void onStart(SpanImpl span); - - void onEnd(SpanImpl span); - } - - // A map implementation with a fixed capacity that drops events when the map gets full. Eviction - // is based on the access order. - private static final class AttributesWithCapacity extends LinkedHashMap { - private final int capacity; - private int totalRecordedAttributes = 0; - // Here because -Werror complains about this: [serial] serializable class AttributesWithCapacity - // has no definition of serialVersionUID. This class shouldn't be serialized. - private static final long serialVersionUID = 42L; - - private AttributesWithCapacity(int capacity) { - // Capacity of the map is capacity + 1 to avoid resizing because removeEldestEntry is invoked - // by put and putAll after inserting a new entry into the map. The loadFactor is set to 1 - // to avoid resizing because. The accessOrder is set to true. - super(capacity + 1, 1, true); - this.capacity = capacity; - } - - // Users must call this method instead of put or putAll to keep count of the total number of - // entries inserted. - private void addAttributes(Map attributes) { - totalRecordedAttributes += attributes.size(); - putAll(attributes); - } - - private int getNumberOfDroppedAttributes() { - return totalRecordedAttributes - size(); - } - - // It is called after each put or putAll call in order to determine if the eldest inserted - // entry should be removed or not. - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > this.capacity; - } - } - - private static final class TraceEvents { - private int totalRecordedEvents = 0; - private final EvictingQueue events; - - private int getNumberOfDroppedEvents() { - return totalRecordedEvents - events.size(); - } - - TraceEvents(int maxNumEvents) { - events = EvictingQueue.create(maxNumEvents); - } - - void addEvent(T event) { - totalRecordedEvents++; - events.add(event); - } - } - - // Timed event that uses nanoTime to represent the Timestamp. - private static final class EventWithNanoTime { - private final long nanoTime; - private final T event; - - private EventWithNanoTime(long nanoTime, T event) { - this.nanoTime = nanoTime; - this.event = event; - } - - private TimedEvent toSpanDataTimedEvent(TimestampConverter timestampConverter) { - return TimedEvent.create(timestampConverter.convertNanoTime(nanoTime), event); - } - } - - private SpanImpl( - SpanContext context, - @Nullable EnumSet options, - String name, - @Nullable SpanId parentSpanId, - @Nullable Boolean hasRemoteParent, - TraceParams traceParams, - StartEndHandler startEndHandler, - @Nullable TimestampConverter timestampConverter, - Clock clock) { - super(context, options); - this.parentSpanId = parentSpanId; - this.hasRemoteParent = hasRemoteParent; - this.name = name; - this.traceParams = traceParams; - this.startEndHandler = startEndHandler; - this.clock = clock; - this.hasBeenEnded = false; - if (getOptions().contains(Options.RECORD_EVENTS)) { - this.timestampConverter = - timestampConverter != null ? timestampConverter : TimestampConverter.now(clock); - startNanoTime = clock.nowNanos(); - } else { - this.startNanoTime = 0; - this.timestampConverter = timestampConverter; - } - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/StartEndHandlerImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/StartEndHandlerImpl.java deleted file mode 100644 index 162871dd..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/StartEndHandlerImpl.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace; - -import io.opencensus.impl.internal.EventQueue; -import io.opencensus.impl.trace.SpanImpl.StartEndHandler; -import io.opencensus.impl.trace.export.RunningSpanStoreImpl; -import io.opencensus.impl.trace.export.SampledSpanStoreImpl; -import io.opencensus.impl.trace.export.SpanExporterImpl; -import io.opencensus.trace.Span.Options; -import io.opencensus.trace.export.SpanData; -import javax.annotation.Nullable; -import javax.annotation.concurrent.ThreadSafe; - -/** - * Uses the provided {@link EventQueue} to defer processing/exporting of the {@link SpanData} to - * avoid impacting the critical path. - */ -@ThreadSafe -public final class StartEndHandlerImpl implements StartEndHandler { - private final SpanExporterImpl spanExporter; - private final RunningSpanStoreImpl runningSpanStore; - private final SampledSpanStoreImpl sampledSpanStore; - private final EventQueue eventQueue; - // true if any of (runningSpanStore OR sampledSpanStore) are different than null, which - // means the spans with RECORD_EVENTS should be enqueued in the queue. - private final boolean enqueueEventForNonSampledSpans; - - /** - * Constructs a new {@code StartEndHandlerImpl}. - * - * @param spanExporter the {@code SpanExporter} implementation. - * @param runningSpanStore the {@code RunningSpanStore} implementation. - * @param sampledSpanStore the {@code SampledSpanStore} implementation. - * @param eventQueue the event queue where all the events are enqueued. - */ - public StartEndHandlerImpl( - SpanExporterImpl spanExporter, - @Nullable RunningSpanStoreImpl runningSpanStore, - @Nullable SampledSpanStoreImpl sampledSpanStore, - EventQueue eventQueue) { - this.spanExporter = spanExporter; - this.runningSpanStore = runningSpanStore; - this.sampledSpanStore = sampledSpanStore; - this.enqueueEventForNonSampledSpans = runningSpanStore != null || sampledSpanStore != null; - this.eventQueue = eventQueue; - } - - @Override - public void onStart(SpanImpl span) { - if (span.getOptions().contains(Options.RECORD_EVENTS) && enqueueEventForNonSampledSpans) { - eventQueue.enqueue(new SpanStartEvent(span, runningSpanStore)); - } - } - - @Override - public void onEnd(SpanImpl span) { - if ((span.getOptions().contains(Options.RECORD_EVENTS) && enqueueEventForNonSampledSpans) - || span.getContext().getTraceOptions().isSampled()) { - eventQueue.enqueue(new SpanEndEvent(span, spanExporter, runningSpanStore, sampledSpanStore)); - } - } - - // An EventQueue entry that records the start of the span event. - private static final class SpanStartEvent implements EventQueue.Entry { - private final SpanImpl span; - private final RunningSpanStoreImpl activeSpansExporter; - - SpanStartEvent(SpanImpl span, @Nullable RunningSpanStoreImpl activeSpansExporter) { - this.span = span; - this.activeSpansExporter = activeSpansExporter; - } - - @Override - public void process() { - if (activeSpansExporter != null) { - activeSpansExporter.onStart(span); - } - } - } - - // An EventQueue entry that records the end of the span event. - private static final class SpanEndEvent implements EventQueue.Entry { - private final SpanImpl span; - private final RunningSpanStoreImpl runningSpanStore; - private final SpanExporterImpl spanExporter; - private final SampledSpanStoreImpl sampledSpanStore; - - SpanEndEvent( - SpanImpl span, - SpanExporterImpl spanExporter, - @Nullable RunningSpanStoreImpl runningSpanStore, - @Nullable SampledSpanStoreImpl sampledSpanStore) { - this.span = span; - this.runningSpanStore = runningSpanStore; - this.spanExporter = spanExporter; - this.sampledSpanStore = sampledSpanStore; - } - - @Override - public void process() { - if (span.getContext().getTraceOptions().isSampled()) { - spanExporter.addSpan(span); - } - if (runningSpanStore != null) { - runningSpanStore.onEnd(span); - } - if (sampledSpanStore != null) { - sampledSpanStore.considerForSampling(span); - } - } - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/TraceComponentImplBase.java b/impl_core/src/main/java/io/opencensus/impl/trace/TraceComponentImplBase.java deleted file mode 100644 index b4a6c3d3..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/TraceComponentImplBase.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace; - -import io.opencensus.common.Clock; -import io.opencensus.impl.internal.EventQueue; -import io.opencensus.impl.internal.SimpleEventQueue; -import io.opencensus.impl.trace.SpanImpl.StartEndHandler; -import io.opencensus.impl.trace.config.TraceConfigImpl; -import io.opencensus.impl.trace.export.ExportComponentImpl; -import io.opencensus.impl.trace.internal.RandomHandler; -import io.opencensus.impl.trace.propagation.PropagationComponentImpl; -import io.opencensus.trace.TraceComponent; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.config.TraceConfig; -import io.opencensus.trace.export.ExportComponent; -import io.opencensus.trace.propagation.PropagationComponent; - -/** Base implementation of the {@link TraceComponent}. */ -public class TraceComponentImplBase extends TraceComponent { - private final ExportComponentImpl exportComponent; - private final PropagationComponent propagationComponent = new PropagationComponentImpl(); - private final Clock clock; - private final StartEndHandler startEndHandler; - private final TraceConfig traceConfig = new TraceConfigImpl(); - private final Tracer tracer; - - /** - * Creates a new {@code TraceComponentImplBase}. - * - * @param clock the clock to use throughout tracing. - * @param randomHandler the random number generator for generating trace and span IDs. - * @param eventQueue the queue implementation. - */ - public TraceComponentImplBase(Clock clock, RandomHandler randomHandler, EventQueue eventQueue) { - this.clock = clock; - // TODO(bdrutu): Add a config/argument for supportInProcessStores. - if (eventQueue instanceof SimpleEventQueue) { - exportComponent = ExportComponentImpl.createWithoutInProcessStores(); - } else { - exportComponent = ExportComponentImpl.createWithInProcessStores(); - } - startEndHandler = - new StartEndHandlerImpl( - exportComponent.getSpanExporter(), - exportComponent.getRunningSpanStore(), - exportComponent.getSampledSpanStore(), - eventQueue); - tracer = new TracerImpl(randomHandler, startEndHandler, clock, traceConfig); - } - - @Override - public Tracer getTracer() { - return tracer; - } - - @Override - public PropagationComponent getPropagationComponent() { - return propagationComponent; - } - - @Override - public final Clock getClock() { - return clock; - } - - @Override - public ExportComponent getExportComponent() { - return exportComponent; - } - - @Override - public TraceConfig getTraceConfig() { - return traceConfig; - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/TracerImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/TracerImpl.java deleted file mode 100644 index 735fdca1..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/TracerImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace; - -import io.opencensus.common.Clock; -import io.opencensus.impl.trace.internal.RandomHandler; -import io.opencensus.trace.Span; -import io.opencensus.trace.SpanBuilder; -import io.opencensus.trace.SpanContext; -import io.opencensus.trace.Tracer; -import io.opencensus.trace.config.TraceConfig; -import javax.annotation.Nullable; - -/** Implementation of the {@link Tracer}. */ -final class TracerImpl extends Tracer { - private final SpanBuilderImpl.Options spanBuilderOptions; - - TracerImpl( - RandomHandler randomHandler, - SpanImpl.StartEndHandler startEndHandler, - Clock clock, - TraceConfig traceConfig) { - spanBuilderOptions = - new SpanBuilderImpl.Options(randomHandler, startEndHandler, clock, traceConfig); - } - - @Override - public SpanBuilder spanBuilderWithExplicitParent(String spanName, @Nullable Span parent) { - return SpanBuilderImpl.createWithParent(spanName, parent, spanBuilderOptions); - } - - @Override - public SpanBuilder spanBuilderWithRemoteParent( - String spanName, @Nullable SpanContext remoteParentSpanContext) { - return SpanBuilderImpl.createWithRemoteParent( - spanName, remoteParentSpanContext, spanBuilderOptions); - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/config/TraceConfigImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/config/TraceConfigImpl.java deleted file mode 100644 index 0973acdc..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/config/TraceConfigImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace.config; - -import io.opencensus.trace.config.TraceConfig; -import io.opencensus.trace.config.TraceParams; - -/** - * Global configuration of the trace service. This allows users to change configs for the default - * sampler, maximum events to be kept, etc. - */ -public final class TraceConfigImpl extends TraceConfig { - // Reads and writes are atomic for reference variables. Use volatile to ensure that these - // operations are visible on other CPUs as well. - private volatile TraceParams activeTraceParams = TraceParams.DEFAULT; - - /** Constructs a new {@code TraceConfigImpl}. */ - public TraceConfigImpl() {} - - @Override - public TraceParams getActiveTraceParams() { - return activeTraceParams; - } - - @Override - public void updateActiveTraceParams(TraceParams traceParams) { - activeTraceParams = traceParams; - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/export/ExportComponentImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/export/ExportComponentImpl.java deleted file mode 100644 index 826c5a72..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/export/ExportComponentImpl.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace.export; - -import io.opencensus.trace.export.ExportComponent; -import io.opencensus.trace.export.RunningSpanStore; -import io.opencensus.trace.export.SampledSpanStore; -import javax.annotation.Nullable; - -/** Implementation of the {@link ExportComponent}. */ -public final class ExportComponentImpl extends ExportComponent { - private static final int EXPORTER_BUFFER_SIZE = 32; - // Enforces that trace export exports data at least once every 2 seconds. - private static final long EXPORTER_SCHEDULE_DELAY_MS = 2000; - - private final SpanExporterImpl spanExporter; - private final RunningSpanStoreImpl runningSpanStore; - private final SampledSpanStoreImpl sampledSpanStore; - - @Override - public SpanExporterImpl getSpanExporter() { - return spanExporter; - } - - @Nullable - @Override - public RunningSpanStoreImpl getRunningSpanStore() { - return runningSpanStore; - } - - @Nullable - @Override - public SampledSpanStoreImpl getSampledSpanStore() { - return sampledSpanStore; - } - - /** - * Returns a new {@code ExportComponentImpl} that has valid instances for {@link RunningSpanStore} - * and {@link SampledSpanStore}. - * - * @return a new {@code ExportComponentImpl}. - */ - public static ExportComponentImpl createWithInProcessStores() { - return new ExportComponentImpl(true); - } - - /** - * Returns a new {@code ExportComponentImpl} that has {@code null} instances for {@link - * RunningSpanStore} and {@link SampledSpanStore}. - * - * @return a new {@code ExportComponentImpl}. - */ - public static ExportComponentImpl createWithoutInProcessStores() { - return new ExportComponentImpl(false); - } - - /** - * Constructs a new {@code ExportComponentImpl}. - * - * @param supportInProcessStores {@code true} to instantiate {@link RunningSpanStore} and {@link - * SampledSpanStore}. - */ - private ExportComponentImpl(boolean supportInProcessStores) { - this.spanExporter = SpanExporterImpl.create(EXPORTER_BUFFER_SIZE, EXPORTER_SCHEDULE_DELAY_MS); - this.runningSpanStore = supportInProcessStores ? new RunningSpanStoreImpl() : null; - this.sampledSpanStore = supportInProcessStores ? new SampledSpanStoreImpl() : null; - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/export/RunningSpanStoreImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/export/RunningSpanStoreImpl.java deleted file mode 100644 index 04628767..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/export/RunningSpanStoreImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace.export; - -import io.opencensus.impl.trace.SpanImpl; -import io.opencensus.impl.trace.internal.ConcurrentIntrusiveList; -import io.opencensus.trace.export.RunningSpanStore; -import io.opencensus.trace.export.SpanData; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.annotation.concurrent.ThreadSafe; - -/** Implementation of the {@link RunningSpanStore}. */ -@ThreadSafe -public final class RunningSpanStoreImpl extends RunningSpanStore { - private final ConcurrentIntrusiveList runningSpans; - - public RunningSpanStoreImpl() { - runningSpans = new ConcurrentIntrusiveList(); - } - - /** - * Adds the {@code Span} into the running spans list when the {@code Span} starts. - * - * @param span the {@code Span} that started. - */ - public void onStart(SpanImpl span) { - runningSpans.addElement(span); - } - - /** - * Removes the {@code Span} from the running spans list when the {@code Span} ends. - * - * @param span the {@code Span} that ended. - */ - public void onEnd(SpanImpl span) { - runningSpans.removeElement(span); - } - - @Override - public Summary getSummary() { - Collection allRunningSpans = runningSpans.getAll(); - Map numSpansPerName = new HashMap(); - for (SpanImpl span : allRunningSpans) { - Integer prevValue = numSpansPerName.get(span.getName()); - numSpansPerName.put(span.getName(), prevValue != null ? prevValue + 1 : 1); - } - Map perSpanNameSummary = new HashMap(); - for (Map.Entry it : numSpansPerName.entrySet()) { - perSpanNameSummary.put(it.getKey(), PerSpanNameSummary.create(it.getValue())); - } - Summary summary = Summary.create(perSpanNameSummary); - return summary; - } - - @Override - public Collection getRunningSpans(Filter filter) { - Collection allRunningSpans = runningSpans.getAll(); - int maxSpansToReturn = - filter.getMaxSpansToReturn() == 0 ? allRunningSpans.size() : filter.getMaxSpansToReturn(); - List ret = new ArrayList(maxSpansToReturn); - for (SpanImpl span : allRunningSpans) { - if (ret.size() == maxSpansToReturn) { - break; - } - if (span.getName().equals(filter.getSpanName())) { - ret.add(span.toSpanData()); - } - } - return ret; - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/export/SampledSpanStoreImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/export/SampledSpanStoreImpl.java deleted file mode 100644 index c7532152..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/export/SampledSpanStoreImpl.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace.export; - -import com.google.common.collect.EvictingQueue; -import io.opencensus.impl.trace.SpanImpl; -import io.opencensus.trace.Status; -import io.opencensus.trace.Status.CanonicalCode; -import io.opencensus.trace.export.SampledSpanStore; -import io.opencensus.trace.export.SpanData; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import javax.annotation.concurrent.GuardedBy; -import javax.annotation.concurrent.ThreadSafe; - -/** Implementation of the {@link SampledSpanStore}. */ -@ThreadSafe -public final class SampledSpanStoreImpl extends SampledSpanStore { - private static final int NUM_SAMPLES_PER_LATENCY_BUCKET = 10; - private static final int NUM_SAMPLES_PER_ERROR_BUCKET = 5; - private static final long TIME_BETWEEN_SAMPLES = TimeUnit.SECONDS.toNanos(1); - private static final int NUM_LATENCY_BUCKETS = LatencyBucketBoundaries.values().length; - // The total number of canonical codes - 1 (the OK code). - private static final int NUM_ERROR_BUCKETS = CanonicalCode.values().length - 1; - private static final int MAX_PER_SPAN_NAME_SAMPLES = - NUM_SAMPLES_PER_LATENCY_BUCKET * NUM_LATENCY_BUCKETS - + NUM_SAMPLES_PER_ERROR_BUCKET * NUM_ERROR_BUCKETS; - - @GuardedBy("samples") - private final Map samples; - - private static final class Bucket { - - private final EvictingQueue queue; - private long lastSampleNanoTime; - - private Bucket(int numSamples) { - queue = EvictingQueue.create(numSamples); - } - - private void considerForSampling(SpanImpl span) { - long spanEndNanoTime = span.getEndNanoTime(); - // Need to compare by doing the subtraction all the time because in case of an overflow, - // this may never sample again (at least for the next ~200 years). No real chance to - // overflow two times because that means the process runs for ~200 years. - if (spanEndNanoTime - lastSampleNanoTime > TIME_BETWEEN_SAMPLES) { - queue.add(span); - lastSampleNanoTime = spanEndNanoTime; - } - } - - private void getSamples(int maxSpansToReturn, List output) { - for (SpanImpl span : queue) { - if (output.size() >= maxSpansToReturn) { - break; - } - output.add(span); - } - } - - private void getSamplesFilteredByLatency( - long latencyLowerNs, long latencyUpperNs, int maxSpansToReturn, List output) { - for (SpanImpl span : queue) { - if (output.size() >= maxSpansToReturn) { - break; - } - long spanLatencyNs = span.getLatencyNs(); - if (spanLatencyNs >= latencyLowerNs && spanLatencyNs < latencyUpperNs) { - output.add(span); - } - } - } - - private int getNumSamples() { - return queue.size(); - } - } - - /** - * Keeps samples for a given span name. Samples for all the latency buckets and for all canonical - * codes other than OK. - */ - private static final class PerSpanNameSamples { - - private final Bucket[] latencyBuckets; - private final Bucket[] errorBuckets; - - private PerSpanNameSamples() { - latencyBuckets = new Bucket[NUM_LATENCY_BUCKETS]; - for (int i = 0; i < NUM_LATENCY_BUCKETS; i++) { - latencyBuckets[i] = new Bucket(NUM_SAMPLES_PER_LATENCY_BUCKET); - } - errorBuckets = new Bucket[NUM_ERROR_BUCKETS]; - for (int i = 0; i < NUM_ERROR_BUCKETS; i++) { - errorBuckets[i] = new Bucket(NUM_SAMPLES_PER_ERROR_BUCKET); - } - } - - private Bucket getLatencyBucket(long latencyNs) { - for (int i = 0; i < NUM_LATENCY_BUCKETS; i++) { - LatencyBucketBoundaries boundaries = LatencyBucketBoundaries.values()[i]; - if (latencyNs >= boundaries.getLatencyLowerNs() - && latencyNs < boundaries.getLatencyUpperNs()) { - return latencyBuckets[i]; - } - } - // latencyNs is negative or Long.MAX_VALUE, so this Span can be ignored. This cannot happen - // in real production because System#nanoTime is monotonic. - return null; - } - - private Bucket getErrorBucket(CanonicalCode code) { - return errorBuckets[code.value() - 1]; - } - - private void considerForSampling(SpanImpl span) { - Status status = span.getStatus(); - // Null status means running Span, this should not happen in production, but the library - // should not crash because of this. - if (status != null) { - Bucket bucket = - status.isOk() - ? getLatencyBucket(span.getLatencyNs()) - : getErrorBucket(status.getCanonicalCode()); - // If unable to find the bucket, ignore this Span. - if (bucket != null) { - bucket.considerForSampling(span); - } - } - } - - private Map getNumbersOfLatencySampledSpans() { - Map latencyBucketSummaries = - new EnumMap(LatencyBucketBoundaries.class); - for (int i = 0; i < NUM_LATENCY_BUCKETS; i++) { - latencyBucketSummaries.put( - LatencyBucketBoundaries.values()[i], latencyBuckets[i].getNumSamples()); - } - return latencyBucketSummaries; - } - - private Map getNumbersOfErrorSampledSpans() { - Map errorBucketSummaries = - new EnumMap(CanonicalCode.class); - for (int i = 0; i < NUM_ERROR_BUCKETS; i++) { - errorBucketSummaries.put(CanonicalCode.values()[i + 1], errorBuckets[i].getNumSamples()); - } - return errorBucketSummaries; - } - - private List getErrorSamples(CanonicalCode code, int maxSpansToReturn) { - ArrayList output = new ArrayList(maxSpansToReturn); - if (code != null) { - getErrorBucket(code).getSamples(maxSpansToReturn, output); - } else { - for (int i = 0; i < NUM_ERROR_BUCKETS; i++) { - errorBuckets[i].getSamples(maxSpansToReturn, output); - } - } - return output; - } - - private List getLatencySamples( - long latencyLowerNs, long latencyUpperNs, int maxSpansToReturn) { - ArrayList output = new ArrayList(maxSpansToReturn); - for (int i = 0; i < NUM_LATENCY_BUCKETS; i++) { - LatencyBucketBoundaries boundaries = LatencyBucketBoundaries.values()[i]; - if (latencyUpperNs >= boundaries.getLatencyLowerNs() - && latencyLowerNs < boundaries.getLatencyUpperNs()) { - latencyBuckets[i].getSamplesFilteredByLatency( - latencyLowerNs, latencyUpperNs, maxSpansToReturn, output); - } - } - return output; - } - } - - /** Constructs a new {@code SampledSpanStoreImpl}. */ - public SampledSpanStoreImpl() { - samples = new HashMap(); - } - - @Override - public Summary getSummary() { - Map ret = new HashMap(); - synchronized (samples) { - for (Map.Entry it : samples.entrySet()) { - ret.put( - it.getKey(), - PerSpanNameSummary.create( - it.getValue().getNumbersOfLatencySampledSpans(), - it.getValue().getNumbersOfErrorSampledSpans())); - } - } - return Summary.create(ret); - } - - /** - * Considers to save the given spans to the stored samples. This must be called at the end of each - * Span with the option RECORD_EVENTS. - * - * @param span the span to be consider for storing into the store buckets. - */ - public void considerForSampling(SpanImpl span) { - synchronized (samples) { - PerSpanNameSamples perSpanNameSamples = samples.get(span.getName()); - if (perSpanNameSamples != null) { - perSpanNameSamples.considerForSampling(span); - } - } - } - - @Override - public void registerSpanNamesForCollection(Collection spanNames) { - synchronized (samples) { - for (String spanName : spanNames) { - if (!samples.containsKey(spanName)) { - samples.put(spanName, new PerSpanNameSamples()); - } - } - } - } - - @Override - public void unregisterSpanNamesForCollection(Collection spanNames) { - synchronized (samples) { - samples.keySet().removeAll(spanNames); - } - } - - @Override - public Collection getErrorSampledSpans(ErrorFilter filter) { - int numSpansToReturn = - filter.getMaxSpansToReturn() == 0 - ? MAX_PER_SPAN_NAME_SAMPLES - : filter.getMaxSpansToReturn(); - List spans = Collections.emptyList(); - // Try to not keep the lock to much, do the SpanImpl -> SpanData conversion outside the lock. - synchronized (samples) { - PerSpanNameSamples perSpanNameSamples = samples.get(filter.getSpanName()); - if (perSpanNameSamples != null) { - spans = perSpanNameSamples.getErrorSamples(filter.getCanonicalCode(), numSpansToReturn); - } - } - List ret = new ArrayList(spans.size()); - for (SpanImpl span : spans) { - ret.add(span.toSpanData()); - } - return Collections.unmodifiableList(ret); - } - - @Override - public Collection getLatencySampledSpans(LatencyFilter filter) { - int numSpansToReturn = - filter.getMaxSpansToReturn() == 0 - ? MAX_PER_SPAN_NAME_SAMPLES - : filter.getMaxSpansToReturn(); - List spans = Collections.emptyList(); - // Try to not keep the lock to much, do the SpanImpl -> SpanData conversion outside the lock. - synchronized (samples) { - PerSpanNameSamples perSpanNameSamples = samples.get(filter.getSpanName()); - if (perSpanNameSamples != null) { - spans = - perSpanNameSamples.getLatencySamples( - filter.getLatencyLowerNs(), filter.getLatencyUpperNs(), numSpansToReturn); - } - } - List ret = new ArrayList(spans.size()); - for (SpanImpl span : spans) { - ret.add(span.toSpanData()); - } - return Collections.unmodifiableList(ret); - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/export/SpanExporterImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/export/SpanExporterImpl.java deleted file mode 100644 index 90d1134f..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/export/SpanExporterImpl.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace.export; - -import com.google.common.annotations.VisibleForTesting; -import io.opencensus.impl.trace.SpanImpl; -import io.opencensus.trace.export.ExportComponent; -import io.opencensus.trace.export.SpanData; -import io.opencensus.trace.export.SpanExporter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.concurrent.GuardedBy; - -/** Implementation of the {@link SpanExporter}. */ -public final class SpanExporterImpl extends SpanExporter { - private static final Logger logger = Logger.getLogger(ExportComponent.class.getName()); - - private final WorkerThread workerThread; - - /** - * Constructs a {@code SpanExporterImpl} that exports the {@link SpanData} asynchronously. - * - *

Starts a separate thread that wakes up every {@code scheduleDelay} and exports any available - * spans data. If the number of buffered SpanData objects is greater than {@code bufferSize} then - * the thread wakes up sooner. - * - * @param bufferSize the size of the buffered span data. - * @param scheduleDelayMillis the maximum delay in milliseconds. - */ - static SpanExporterImpl create(int bufferSize, long scheduleDelayMillis) { - // TODO(bdrutu): Consider to add a shutdown hook to not avoid dropping data. - WorkerThread workerThread = new WorkerThread(bufferSize, scheduleDelayMillis); - workerThread.start(); - return new SpanExporterImpl(workerThread); - } - - /** - * Adds a Span to the exporting service. - * - * @param span the {@code Span} to be added. - */ - public void addSpan(SpanImpl span) { - workerThread.addSpan(span); - } - - @Override - public void registerHandler(String name, Handler handler) { - workerThread.registerHandler(name, handler); - } - - @Override - public void unregisterHandler(String name) { - workerThread.unregisterHandler(name); - } - - private SpanExporterImpl(WorkerThread workerThread) { - this.workerThread = workerThread; - } - - @VisibleForTesting - Thread getServiceExporterThread() { - return workerThread; - } - - // Worker thread that batches multiple span data and calls the registered services to export - // that data. - // - // The map of registered handlers is implemented using ConcurrentHashMap ensuring full - // concurrency of retrievals and adjustable expected concurrency for updates. Retrievals - // reflect the results of the most recently completed update operations held upon their onset. - // - // The list of batched data is protected by an explicit monitor object which ensures full - // concurrency. - private static final class WorkerThread extends Thread { - private final Object monitor = new Object(); - - @GuardedBy("monitor") - private final List spans; - - private final Map serviceHandlers = new ConcurrentHashMap(); - private final int bufferSize; - private final long scheduleDelayMillis; - - // See SpanExporterImpl#addSpan. - private void addSpan(SpanImpl span) { - synchronized (monitor) { - this.spans.add(span); - if (spans.size() > bufferSize) { - monitor.notifyAll(); - } - } - } - - // See SpanExporter#registerHandler. - private void registerHandler(String name, Handler serviceHandler) { - serviceHandlers.put(name, serviceHandler); - } - - // See SpanExporter#unregisterHandler. - private void unregisterHandler(String name) { - serviceHandlers.remove(name); - } - - // Exports the list of SpanData to all the ServiceHandlers. - private void onBatchExport(List spanDataList) { - // From the java documentation of the ConcurrentHashMap#entrySet(): - // The view's iterator is a "weakly consistent" iterator that will never throw - // ConcurrentModificationException, and guarantees to traverse elements as they existed - // upon construction of the iterator, and may (but is not guaranteed to) reflect any - // modifications subsequent to construction. - for (Map.Entry it : serviceHandlers.entrySet()) { - // In case of any exception thrown by the service handlers continue to run. - try { - it.getValue().export(spanDataList); - } catch (Throwable e) { - logger.log(Level.WARNING, "Exception thrown by the service export " + it.getKey(), e); - } - } - } - - private WorkerThread(int bufferSize, long scheduleDelayMillis) { - spans = new LinkedList(); - this.bufferSize = bufferSize; - this.scheduleDelayMillis = scheduleDelayMillis; - setDaemon(true); - setName("ExportComponent.ServiceExporterThread"); - } - - // Returns an unmodifiable list of all buffered spans data to ensure that any registered - // service handler cannot modify the list. - private static List fromSpanImplToSpanData(List spans) { - List spanDatas = new ArrayList(spans.size()); - for (SpanImpl span : spans) { - spanDatas.add(span.toSpanData()); - } - return Collections.unmodifiableList(spanDatas); - } - - @Override - public void run() { - while (true) { - // Copy all the batched spans in a separate list to release the monitor lock asap to - // avoid blocking the producer thread. - List spansCopy; - synchronized (monitor) { - if (spans.size() < bufferSize) { - do { - // In the case of a spurious wakeup we export only if we have at least one span in - // the batch. It is acceptable because batching is a best effort mechanism here. - try { - monitor.wait(scheduleDelayMillis); - } catch (InterruptedException ie) { - // Preserve the interruption status as per guidance and stop doing any work. - Thread.currentThread().interrupt(); - return; - } - } while (spans.isEmpty()); - } - spansCopy = new ArrayList(spans); - spans.clear(); - } - // Execute the batch export outside the synchronized to not block all producers. - final List spanDataList = fromSpanImplToSpanData(spansCopy); - if (!spanDataList.isEmpty()) { - onBatchExport(spanDataList); - } - } - } - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/internal/ConcurrentIntrusiveList.java b/impl_core/src/main/java/io/opencensus/impl/trace/internal/ConcurrentIntrusiveList.java deleted file mode 100644 index 09b24637..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/internal/ConcurrentIntrusiveList.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace.internal; - -import static com.google.common.base.Preconditions.checkArgument; - -import io.opencensus.impl.trace.internal.ConcurrentIntrusiveList.Element; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import javax.annotation.concurrent.ThreadSafe; - -/** - * An {@code ConcurrentIntrusiveList} is a doubly-linked list where the link pointers are - * embedded in the elements. This makes insertion and removal into a known position constant time. - * - *

Elements must derive from the {@code Element>} interface: - * - *

{@code
- * class MyClass implements Element {
- *   private MyClass next = null;
- *   private MyClass prev = null;
- *
- *   {@literal @}Override
- *   MyClass getNext() {
- *     return next;
- *   }
- *
- *   {@literal @}Override
- *   void setNext(MyClass element) {
- *     next = element;
- *   }
- *
- *   {@literal @}Override
- *   MyClass getPrev() {
- *     return prev;
- *   }
- *
- *   {@literal @}Override
- *   void setPrev(MyClass element) {
- *     prev = element;
- *   }
- * }
- * }
- */ -@ThreadSafe -public final class ConcurrentIntrusiveList> { - private int size = 0; - private T head = null; - - public ConcurrentIntrusiveList() {} - - /** - * Adds the given {@code element} to the list. - * - * @param element the element to add. - * @throws IllegalArgumentException if the element is already in a list. - */ - public synchronized void addElement(T element) { - checkArgument( - element.getNext() == null && element.getPrev() == null && element != head, - "Element already in a list."); - size++; - if (head == null) { - head = element; - } else { - element.setNext(head); - head.setPrev(element); - head = element; - } - } - - /** - * Removes the given {@code element} from the list. - * - * @param element the element to remove. - * @throws IllegalArgumentException if the element is not in the list. - */ - public synchronized void removeElement(T element) { - checkArgument( - element.getNext() != null || element.getPrev() != null || element == head, - "Element not in the list."); - size--; - if (element.getPrev() == null) { - // This is the first element - head = element.getNext(); - if (head != null) { - // If more than one element in the list. - head.setPrev(null); - element.setNext(null); - } - } else if (element.getNext() == null) { - // This is the last element, and there is at least another element because - // element.getPrev() != null. - element.getPrev().setNext(null); - element.setPrev(null); - } else { - element.getPrev().setNext(element.getNext()); - element.getNext().setPrev(element.getPrev()); - element.setNext(null); - element.setPrev(null); - } - } - - /** - * Returns the number of elements in this list. - * - * @return the number of elements in this list. - */ - public synchronized int size() { - return size; - } - - /** - * Returns all the elements from this list. - * - * @return all the elements from this list. - */ - public synchronized Collection getAll() { - List all = new ArrayList(size); - for (T e = head; e != null; e = e.getNext()) { - all.add(e); - } - return all; - } - - /** - * This is an interface that must be implemented by any element that uses {@link - * ConcurrentIntrusiveList}. - * - * @param the element that will be used for the list. - */ - public interface Element> { - - /** - * Returns a reference to the next element in the list. - * - * @return a reference to the next element in the list. - */ - T getNext(); - - /** - * Sets the reference to the next element in the list. - * - * @param element the reference to the next element in the list. - */ - void setNext(T element); - - /** - * Returns a reference to the previous element in the list. - * - * @return a reference to the previous element in the list. - */ - T getPrev(); - - /** - * Sets the reference to the previous element in the list. - * - * @param element the reference to the previous element in the list. - */ - void setPrev(T element); - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/internal/RandomHandler.java b/impl_core/src/main/java/io/opencensus/impl/trace/internal/RandomHandler.java deleted file mode 100644 index 3ce4502f..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/internal/RandomHandler.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace.internal; - -import java.security.SecureRandom; -import java.util.Random; -import javax.annotation.concurrent.ThreadSafe; - -/** - * Abstract class to access the current {@link Random}. - * - *

Implementation can have a per thread instance or a single global instance. - */ -@ThreadSafe -public abstract class RandomHandler { - /** - * Returns the current {@link Random}. - * - * @return the current {@code Random}. - */ - public abstract Random current(); - - /** Implementation of the {@link RandomHandler} using {@link SecureRandom}. */ - @ThreadSafe - public static final class SecureRandomHandler extends RandomHandler { - private final Random random = new SecureRandom(); - - /** Constructs a new {@link SecureRandomHandler}. */ - public SecureRandomHandler() {} - - @Override - public Random current() { - return random; - } - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/propagation/BinaryFormatImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/propagation/BinaryFormatImpl.java deleted file mode 100644 index ecf69695..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/propagation/BinaryFormatImpl.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace.propagation; - -import static com.google.common.base.Preconditions.checkNotNull; - -import io.opencensus.trace.SpanContext; -import io.opencensus.trace.SpanId; -import io.opencensus.trace.TraceId; -import io.opencensus.trace.TraceOptions; -import io.opencensus.trace.propagation.BinaryFormat; -import java.text.ParseException; - -/** - * Implementation of the {@link BinaryFormat}. - * - *

BinaryFormat format: - * - *

    - *
  • Binary value: <version_id><version_format> - *
  • version_id: 1-byte representing the version id. - *
  • For version_id = 0: - *
      - *
    • version_format: <field><field> - *
    • field_format: <field_id><field_format> - *
    • Fields: - *
        - *
      • TraceId: (field_id = 0, len = 16, default = "0000000000000000") - - * 16-byte array representing the trace_id. - *
      • SpanId: (field_id = 1, len = 8, default = "00000000") - 8-byte array - * representing the span_id. - *
      • TraceOptions: (field_id = 2, len = 1, default = "0") - 1-byte array - * representing the trace_options. - *
      - *
    • Fields MUST be encoded using the field id order (smaller to higher). - *
    • Valid value example: - *
        - *
      • {0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97, - * 98, 99, 100, 101, 102, 103, 104, 2, 1} - *
      • version_id = 0; - *
      • trace_id = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79} - *
      • span_id = {97, 98, 99, 100, 101, 102, 103, 104}; - *
      • trace_options = {1}; - *
      - *
    - *
- */ -public final class BinaryFormatImpl extends BinaryFormat { - private static final byte VERSION_ID = 0; - private static final int VERSION_ID_OFFSET = 0; - // The version_id/field_id size in bytes. - private static final byte ID_SIZE = 1; - private static final byte TRACE_ID_FIELD_ID = 0; - private static final int TRACE_ID_FIELD_ID_OFFSET = VERSION_ID_OFFSET + ID_SIZE; - private static final int TRACE_ID_OFFSET = TRACE_ID_FIELD_ID_OFFSET + ID_SIZE; - private static final byte SPAN_ID_FIELD_ID = 1; - private static final int SPAN_ID_FIELD_ID_OFFSET = TRACE_ID_OFFSET + TraceId.SIZE; - private static final int SPAN_ID_OFFSET = SPAN_ID_FIELD_ID_OFFSET + ID_SIZE; - private static final byte TRACE_OPTION_FIELD_ID = 2; - private static final int TRACE_OPTION_FIELD_ID_OFFSET = SPAN_ID_OFFSET + SpanId.SIZE; - private static final int TRACE_OPTIONS_OFFSET = TRACE_OPTION_FIELD_ID_OFFSET + ID_SIZE; - private static final int FORMAT_LENGTH = - 4 * ID_SIZE + TraceId.SIZE + SpanId.SIZE + TraceOptions.SIZE; - - @Override - public byte[] toBinaryValue(SpanContext spanContext) { - checkNotNull(spanContext, "spanContext"); - byte[] bytes = new byte[FORMAT_LENGTH]; - bytes[VERSION_ID_OFFSET] = VERSION_ID; - bytes[TRACE_ID_FIELD_ID_OFFSET] = TRACE_ID_FIELD_ID; - spanContext.getTraceId().copyBytesTo(bytes, TRACE_ID_OFFSET); - bytes[SPAN_ID_FIELD_ID_OFFSET] = SPAN_ID_FIELD_ID; - spanContext.getSpanId().copyBytesTo(bytes, SPAN_ID_OFFSET); - bytes[TRACE_OPTION_FIELD_ID_OFFSET] = TRACE_OPTION_FIELD_ID; - spanContext.getTraceOptions().copyBytesTo(bytes, TRACE_OPTIONS_OFFSET); - return bytes; - } - - @Override - public SpanContext fromBinaryValue(byte[] bytes) throws ParseException { - checkNotNull(bytes, "bytes"); - if (bytes.length == 0 || bytes[0] != VERSION_ID) { - throw new ParseException("Unsupported version.", 0); - } - TraceId traceId = TraceId.INVALID; - SpanId spanId = SpanId.INVALID; - TraceOptions traceOptions = TraceOptions.DEFAULT; - int pos = 1; - try { - if (bytes.length > pos && bytes[pos] == TRACE_ID_FIELD_ID) { - traceId = TraceId.fromBytes(bytes, pos + ID_SIZE); - pos += ID_SIZE + TraceId.SIZE; - } - if (bytes.length > pos && bytes[pos] == SPAN_ID_FIELD_ID) { - spanId = SpanId.fromBytes(bytes, pos + ID_SIZE); - pos += ID_SIZE + SpanId.SIZE; - } - if (bytes.length > pos && bytes[pos] == TRACE_OPTION_FIELD_ID) { - traceOptions = TraceOptions.fromBytes(bytes, pos + ID_SIZE); - } - return SpanContext.create(traceId, spanId, traceOptions); - } catch (IndexOutOfBoundsException e) { - throw new ParseException("Invalid input: " + e.toString(), pos); - } - } -} diff --git a/impl_core/src/main/java/io/opencensus/impl/trace/propagation/PropagationComponentImpl.java b/impl_core/src/main/java/io/opencensus/impl/trace/propagation/PropagationComponentImpl.java deleted file mode 100644 index 755ba276..00000000 --- a/impl_core/src/main/java/io/opencensus/impl/trace/propagation/PropagationComponentImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.impl.trace.propagation; - -import io.opencensus.trace.propagation.BinaryFormat; -import io.opencensus.trace.propagation.PropagationComponent; - -/** Implementation of the {@link PropagationComponent}. */ -public class PropagationComponentImpl extends PropagationComponent { - private final BinaryFormat binaryFormat = new BinaryFormatImpl(); - - @Override - public BinaryFormat getBinaryFormat() { - return binaryFormat; - } -} diff --git a/impl_core/src/main/java/io/opencensus/implcore/common/MillisClock.java b/impl_core/src/main/java/io/opencensus/implcore/common/MillisClock.java new file mode 100644 index 00000000..2630db2d --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/common/MillisClock.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.common; + +import io.opencensus.common.Clock; +import io.opencensus.common.Timestamp; +import javax.annotation.concurrent.ThreadSafe; + +/** A {@link Clock} that uses {@link System#currentTimeMillis()} and {@link System#nanoTime()}. */ +@ThreadSafe +public final class MillisClock extends Clock { + private static final MillisClock INSTANCE = new MillisClock(); + + private MillisClock() {} + + /** + * Returns a {@code MillisClock}. + * + * @return a {@code MillisClock}. + */ + public static MillisClock getInstance() { + return INSTANCE; + } + + @Override + public Timestamp now() { + return Timestamp.fromMillis(System.currentTimeMillis()); + } + + @Override + public long nowNanos() { + return System.nanoTime(); + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/internal/EventQueue.java b/impl_core/src/main/java/io/opencensus/implcore/internal/EventQueue.java new file mode 100644 index 00000000..8f09c3c2 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/internal/EventQueue.java @@ -0,0 +1,31 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.internal; + +/** A queue that processes events. See {@code DisruptorEventQueue} for an example. */ +public interface EventQueue { + void enqueue(Entry entry); + + /** + * Base interface to be used for all entries in {@link EventQueue}. For example usage, see {@code + * DisruptorEventQueue}. + */ + public interface Entry { + /** + * Process the event associated with this entry. This will be called for every event in the + * associated {@link EventQueue}. + */ + void process(); + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/internal/SimpleEventQueue.java b/impl_core/src/main/java/io/opencensus/implcore/internal/SimpleEventQueue.java new file mode 100644 index 00000000..68a31905 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/internal/SimpleEventQueue.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.internal; + +/** + * An {@link EventQueue} that processes events in the current thread. This class can be used for + * testing. + */ +public class SimpleEventQueue implements EventQueue { + + @Override + public void enqueue(Entry entry) { + entry.process(); + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/internal/TimestampConverter.java b/impl_core/src/main/java/io/opencensus/implcore/internal/TimestampConverter.java new file mode 100644 index 00000000..d1c52c4b --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/internal/TimestampConverter.java @@ -0,0 +1,48 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.internal; + +import io.opencensus.common.Clock; +import io.opencensus.common.Timestamp; +import javax.annotation.concurrent.Immutable; + +/** + * This class provides a mechanism for converting {@link System#nanoTime() nanoTime} values to + * {@link Timestamp}. + */ +@Immutable +public final class TimestampConverter { + private final Timestamp timestamp; + private final long nanoTime; + + // Returns a WallTimeConverter initialized to now. + public static TimestampConverter now(Clock clock) { + return new TimestampConverter(clock.now(), clock.nowNanos()); + } + + /** + * Converts a {@link System#nanoTime() nanoTime} value to {@link Timestamp}. + * + * @param nanoTime value to convert. + * @return the {@code Timestamp} representation of the {@code time}. + */ + public Timestamp convertNanoTime(long nanoTime) { + return timestamp.addNanos(nanoTime - this.nanoTime); + } + + private TimestampConverter(Timestamp timestamp, long nanoTime) { + this.timestamp = timestamp; + this.nanoTime = nanoTime; + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/internal/VarInt.java b/impl_core/src/main/java/io/opencensus/implcore/internal/VarInt.java new file mode 100644 index 00000000..cf614f1d --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/internal/VarInt.java @@ -0,0 +1,279 @@ +/* + * Copyright 2016, Google Inc. + * 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.implcore.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +/** Common methods to encode and decode varints and varlongs into ByteBuffers and arrays. */ +// CHECKSTYLE:OFF +public class VarInt { + + /** Maximum encoded size of 32-bit positive integers (in bytes) */ + public static final int MAX_VARINT_SIZE = 5; + + /** maximum encoded size of 64-bit longs, and negative 32-bit ints (in bytes) */ + public static final int MAX_VARLONG_SIZE = 10; + + private VarInt() {} + + /** + * Returns the encoding size in bytes of its input value. + * + * @param i the integer to be measured + * @return the encoding size in bytes of its input value + */ + public static int varIntSize(int i) { + int result = 0; + do { + result++; + i >>>= 7; + } while (i != 0); + return result; + } + + /** + * Reads a varint from src, places its values into the first element of dst and returns the offset + * in to src of the first byte after the varint. + * + * @param src source buffer to retrieve from + * @param offset offset within src + * @param dst the resulting int value + * @return the updated offset after reading the varint + */ + public static int getVarInt(byte[] src, int offset, int[] dst) { + int result = 0; + int shift = 0; + int b; + do { + if (shift >= 32) { + // Out of range + throw new IndexOutOfBoundsException("varint too long"); + } + // Get 7 bits from next byte + b = src[offset++]; + result |= (b & 0x7F) << shift; + shift += 7; + } while ((b & 0x80) != 0); + dst[0] = result; + return offset; + } + + /** + * Encodes an integer in a variable-length encoding, 7 bits per byte, into a destination byte[], + * following the protocol buffer convention. + * + * @param v the int value to write to sink + * @param sink the sink buffer to write to + * @param offset the offset within sink to begin writing + * @return the updated offset after writing the varint + */ + public static int putVarInt(int v, byte[] sink, int offset) { + do { + // Encode next 7 bits + terminator bit + int bits = v & 0x7F; + v >>>= 7; + byte b = (byte) (bits + ((v != 0) ? 0x80 : 0)); + sink[offset++] = b; + } while (v != 0); + return offset; + } + + /** + * Reads a varint from the current position of the given ByteBuffer and returns the decoded value + * as 32 bit integer. + * + *

The position of the buffer is advanced to the first byte after the decoded varint. + * + * @param src the ByteBuffer to get the var int from + * @return The integer value of the decoded varint + */ + public static int getVarInt(ByteBuffer src) { + int tmp; + if ((tmp = src.get()) >= 0) { + return tmp; + } + int result = tmp & 0x7f; + if ((tmp = src.get()) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 0x7f) << 7; + if ((tmp = src.get()) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 0x7f) << 14; + if ((tmp = src.get()) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 0x7f) << 21; + result |= (tmp = src.get()) << 28; + while (tmp < 0) { + // We get into this loop only in the case of overflow. + // By doing this, we can call getVarInt() instead of + // getVarLong() when we only need an int. + tmp = src.get(); + } + } + } + } + return result; + } + + /** + * Encodes an integer in a variable-length encoding, 7 bits per byte, to a ByteBuffer sink. + * + * @param v the value to encode + * @param sink the ByteBuffer to add the encoded value + */ + public static void putVarInt(int v, ByteBuffer sink) { + while (true) { + int bits = v & 0x7f; + v >>>= 7; + if (v == 0) { + sink.put((byte) bits); + return; + } + sink.put((byte) (bits | 0x80)); + } + } + + /** + * Reads a varint from the given InputStream and returns the decoded value as an int. + * + * @param inputStream the InputStream to read from + */ + public static int getVarInt(InputStream inputStream) throws IOException { + int result = 0; + int shift = 0; + int b; + do { + if (shift >= 32) { + // Out of range + throw new IndexOutOfBoundsException("varint too long"); + } + // Get 7 bits from next byte + b = inputStream.read(); + result |= (b & 0x7F) << shift; + shift += 7; + } while ((b & 0x80) != 0); + return result; + } + + /** + * Encodes an integer in a variable-length encoding, 7 bits per byte, and writes it to the given + * OutputStream. + * + * @param v the value to encode + * @param outputStream the OutputStream to write to + */ + public static void putVarInt(int v, OutputStream outputStream) throws IOException { + byte[] bytes = new byte[varIntSize(v)]; + putVarInt(v, bytes, 0); + outputStream.write(bytes); + } + + /** + * Returns the encoding size in bytes of its input value. + * + * @param v the long to be measured + * @return the encoding size in bytes of a given long value. + */ + public static int varLongSize(long v) { + int result = 0; + do { + result++; + v >>>= 7; + } while (v != 0); + return result; + } + + /** + * Reads an up to 64 bit long varint from the current position of the given ByteBuffer and returns + * the decoded value as long. + * + *

The position of the buffer is advanced to the first byte after the decoded varint. + * + * @param src the ByteBuffer to get the var int from + * @return The integer value of the decoded long varint + */ + public static long getVarLong(ByteBuffer src) { + long tmp; + if ((tmp = src.get()) >= 0) { + return tmp; + } + long result = tmp & 0x7f; + if ((tmp = src.get()) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 0x7f) << 7; + if ((tmp = src.get()) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 0x7f) << 14; + if ((tmp = src.get()) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 0x7f) << 21; + if ((tmp = src.get()) >= 0) { + result |= tmp << 28; + } else { + result |= (tmp & 0x7f) << 28; + if ((tmp = src.get()) >= 0) { + result |= tmp << 35; + } else { + result |= (tmp & 0x7f) << 35; + if ((tmp = src.get()) >= 0) { + result |= tmp << 42; + } else { + result |= (tmp & 0x7f) << 42; + if ((tmp = src.get()) >= 0) { + result |= tmp << 49; + } else { + result |= (tmp & 0x7f) << 49; + if ((tmp = src.get()) >= 0) { + result |= tmp << 56; + } else { + result |= (tmp & 0x7f) << 56; + result |= ((long) src.get()) << 63; + } + } + } + } + } + } + } + } + return result; + } + + /** + * Encodes a long integer in a variable-length encoding, 7 bits per byte, to a ByteBuffer sink. + * + * @param v the value to encode + * @param sink the ByteBuffer to add the encoded value + */ + public static void putVarLong(long v, ByteBuffer sink) { + while (true) { + int bits = ((int) v) & 0x7f; + v >>>= 7; + if (v == 0) { + sink.put((byte) bits); + return; + } + sink.put((byte) (bits | 0x80)); + } + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/SpanBuilderImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/SpanBuilderImpl.java new file mode 100644 index 00000000..21bc3757 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/SpanBuilderImpl.java @@ -0,0 +1,197 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.opencensus.common.Clock; +import io.opencensus.implcore.internal.TimestampConverter; +import io.opencensus.implcore.trace.internal.RandomHandler; +import io.opencensus.trace.Link; +import io.opencensus.trace.Link.Type; +import io.opencensus.trace.Sampler; +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanBuilder; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.SpanId; +import io.opencensus.trace.TraceId; +import io.opencensus.trace.TraceOptions; +import io.opencensus.trace.config.TraceConfig; +import io.opencensus.trace.config.TraceParams; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Random; +import javax.annotation.Nullable; + +/** Implementation of the {@link SpanBuilder}. */ +final class SpanBuilderImpl extends SpanBuilder { + private final Options options; + + private final String name; + private final Span parent; + private final SpanContext remoteParentSpanContext; + private Sampler sampler; + private List parentLinks = Collections.emptyList(); + private Boolean recordEvents; + + private SpanImpl startSpanInternal( + @Nullable SpanContext parent, + @Nullable Boolean hasRemoteParent, + String name, + Sampler sampler, + List parentLinks, + Boolean recordEvents, + @Nullable TimestampConverter timestampConverter) { + TraceParams activeTraceParams = options.traceConfig.getActiveTraceParams(); + Random random = options.randomHandler.current(); + TraceId traceId; + SpanId spanId = SpanId.generateRandomId(random); + SpanId parentSpanId = null; + TraceOptions.Builder traceOptionsBuilder; + if (parent == null || !parent.isValid()) { + // New root span. + traceId = TraceId.generateRandomId(random); + traceOptionsBuilder = TraceOptions.builder(); + // This is a root span so no remote or local parent. + hasRemoteParent = null; + } else { + // New child span. + traceId = parent.getTraceId(); + parentSpanId = parent.getSpanId(); + traceOptionsBuilder = TraceOptions.builder(parent.getTraceOptions()); + } + if (sampler == null) { + sampler = activeTraceParams.getSampler(); + } + if (sampler.shouldSample(parent, hasRemoteParent, traceId, spanId, name, parentLinks)) { + traceOptionsBuilder.setIsSampled(); + } + TraceOptions traceOptions = traceOptionsBuilder.build(); + EnumSet spanOptions = EnumSet.noneOf(Span.Options.class); + if (traceOptions.isSampled() || Boolean.TRUE.equals(recordEvents)) { + spanOptions.add(Span.Options.RECORD_EVENTS); + } + SpanImpl span = + SpanImpl.startSpan( + SpanContext.create(traceId, spanId, traceOptions), + spanOptions, + name, + parentSpanId, + hasRemoteParent, + activeTraceParams, + options.startEndHandler, + timestampConverter, + options.clock); + linkSpans(span, parentLinks); + return span; + } + + private static void linkSpans(Span span, List parentLinks) { + if (!parentLinks.isEmpty()) { + Link childLink = Link.fromSpanContext(span.getContext(), Type.CHILD_LINKED_SPAN); + for (Span linkedSpan : parentLinks) { + linkedSpan.addLink(childLink); + span.addLink(Link.fromSpanContext(linkedSpan.getContext(), Type.PARENT_LINKED_SPAN)); + } + } + } + + static SpanBuilderImpl createWithParent(String spanName, @Nullable Span parent, Options options) { + return new SpanBuilderImpl(spanName, null, parent, options); + } + + static SpanBuilderImpl createWithRemoteParent( + String spanName, @Nullable SpanContext remoteParentSpanContext, Options options) { + return new SpanBuilderImpl(spanName, remoteParentSpanContext, null, options); + } + + private SpanBuilderImpl( + String name, + @Nullable SpanContext remoteParentSpanContext, + @Nullable Span parent, + Options options) { + this.name = checkNotNull(name, "name"); + this.parent = parent; + this.remoteParentSpanContext = remoteParentSpanContext; + this.options = options; + } + + @Override + public SpanImpl startSpan() { + SpanContext parentContext = remoteParentSpanContext; + Boolean hasRemoteParent = Boolean.TRUE; + TimestampConverter timestampConverter = null; + if (remoteParentSpanContext == null) { + // This is not a child of a remote Span. Get the parent SpanContext from the parent Span if + // any. + Span parent = this.parent; + hasRemoteParent = Boolean.FALSE; + if (parent != null) { + parentContext = parent.getContext(); + // Pass the timestamp converter from the parent to ensure that the recorded events are in + // the right order. Implementation uses System.nanoTime() which is monotonically increasing. + if (parent instanceof SpanImpl) { + timestampConverter = ((SpanImpl) parent).getTimestampConverter(); + } + } else { + hasRemoteParent = null; + } + } + return startSpanInternal( + parentContext, + hasRemoteParent, + name, + sampler, + parentLinks, + recordEvents, + timestampConverter); + } + + static final class Options { + private final RandomHandler randomHandler; + private final SpanImpl.StartEndHandler startEndHandler; + private final Clock clock; + private final TraceConfig traceConfig; + + Options( + RandomHandler randomHandler, + SpanImpl.StartEndHandler startEndHandler, + Clock clock, + TraceConfig traceConfig) { + this.randomHandler = checkNotNull(randomHandler, "randomHandler"); + this.startEndHandler = checkNotNull(startEndHandler, "startEndHandler"); + this.clock = checkNotNull(clock, "clock"); + this.traceConfig = checkNotNull(traceConfig, "traceConfig"); + } + } + + @Override + public SpanBuilderImpl setSampler(Sampler sampler) { + this.sampler = checkNotNull(sampler, "sampler"); + return this; + } + + @Override + public SpanBuilderImpl setParentLinks(List parentLinks) { + this.parentLinks = checkNotNull(parentLinks, "parentLinks"); + return this; + } + + @Override + public SpanBuilderImpl setRecordEvents(boolean recordEvents) { + this.recordEvents = recordEvents; + return this; + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/SpanImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/SpanImpl.java new file mode 100644 index 00000000..fbc753f5 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/SpanImpl.java @@ -0,0 +1,516 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.EvictingQueue; +import io.opencensus.common.Clock; +import io.opencensus.implcore.internal.TimestampConverter; +import io.opencensus.implcore.trace.internal.ConcurrentIntrusiveList.Element; +import io.opencensus.trace.Annotation; +import io.opencensus.trace.AttributeValue; +import io.opencensus.trace.EndSpanOptions; +import io.opencensus.trace.Link; +import io.opencensus.trace.NetworkEvent; +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.SpanId; +import io.opencensus.trace.Status; +import io.opencensus.trace.Tracer; +import io.opencensus.trace.config.TraceParams; +import io.opencensus.trace.export.SpanData; +import io.opencensus.trace.export.SpanData.TimedEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +/** Implementation for the {@link Span} class. */ +@ThreadSafe +public final class SpanImpl extends Span implements Element { + private static final Logger logger = Logger.getLogger(Tracer.class.getName()); + + // The parent SpanId of this span. Null if this is a root span. + private final SpanId parentSpanId; + // True if the parent is on a different process. + private final Boolean hasRemoteParent; + // Active trace params when the Span was created. + private final TraceParams traceParams; + // Handler called when the span starts and ends. + private final StartEndHandler startEndHandler; + // The displayed name of the span. + private final String name; + // The clock used to get the time. + private final Clock clock; + // The time converter used to convert nano time to Timestamp. This is needed because Java has + // millisecond granularity for Timestamp and tracing events are recorded more often. + private final TimestampConverter timestampConverter; + // The start time of the span. Set when the span is created iff the RECORD_EVENTS options is + // set, otherwise 0. + private final long startNanoTime; + // Set of recorded attributes. DO NOT CALL any other method that changes the ordering of events. + @GuardedBy("this") + private AttributesWithCapacity attributes; + // List of recorded annotations. + @GuardedBy("this") + private TraceEvents> annotations; + // List of recorded network events. + @GuardedBy("this") + private TraceEvents> networkEvents; + // List of recorded links to parent and child spans. + @GuardedBy("this") + private TraceEvents links; + // The status of the span. Set when the span is ended iff the RECORD_EVENTS options is set. + @GuardedBy("this") + private Status status; + // The end time of the span. Set when the span is ended iff the RECORD_EVENTS options is set, + // otherwise 0. + @GuardedBy("this") + private long endNanoTime; + // True if the span is ended. + @GuardedBy("this") + private boolean hasBeenEnded; + + // Pointers for the ConcurrentIntrusiveList$Element. Guarded by the ConcurrentIntrusiveList. + private SpanImpl next = null; + private SpanImpl prev = null; + + /** + * Creates and starts a span with the given configuration. + * + * @param context supplies the trace_id and span_id for the newly started span. + * @param options the options for the new span, importantly Options.RECORD_EVENTS. + * @param name the displayed name for the new span. + * @param parentSpanId the span_id of the parent span, or null if the new span is a root span. + * @param hasRemoteParent {@code true} if the parentContext is remote. {@code null} if this is a + * root span. + * @param traceParams trace parameters like sampler and probability. + * @param startEndHandler handler called when the span starts and ends. + * @param timestampConverter null if the span is a root span or the parent is not sampled. If the + * parent is sampled, we should use the same converter to ensure ordering between tracing + * events. + * @param clock the clock used to get the time. + * @return a new and started span. + */ + @VisibleForTesting + public static SpanImpl startSpan( + SpanContext context, + @Nullable EnumSet options, + String name, + @Nullable SpanId parentSpanId, + @Nullable Boolean hasRemoteParent, + TraceParams traceParams, + StartEndHandler startEndHandler, + @Nullable TimestampConverter timestampConverter, + Clock clock) { + SpanImpl span = + new SpanImpl( + context, + options, + name, + parentSpanId, + hasRemoteParent, + traceParams, + startEndHandler, + timestampConverter, + clock); + // Call onStart here instead of calling in the constructor to make sure the span is completely + // initialized. + if (span.getOptions().contains(Options.RECORD_EVENTS)) { + startEndHandler.onStart(span); + } + return span; + } + + /** + * Returns the name of the {@code Span}. + * + * @return the name of the {@code Span}. + */ + public String getName() { + return name; + } + + /** + * Returns the status of the {@code Span}. If not set defaults to {@link Status#OK}. + * + * @return the status of the {@code Span}. + */ + public Status getStatus() { + synchronized (this) { + return status; + } + } + + /** + * Returns the end nano time (see {@link System#nanoTime()}). If the current {@code Span} is not + * ended then returns {@link Clock#nowNanos()}. + * + * @return the end nano time. + */ + public long getEndNanoTime() { + synchronized (this) { + return hasBeenEnded ? endNanoTime : clock.nowNanos(); + } + } + + /** + * Returns the latency of the {@code Span} in nanos. If still active then returns now() - start + * time. + * + * @return the latency of the {@code Span} in nanos. + */ + public long getLatencyNs() { + synchronized (this) { + return hasBeenEnded ? endNanoTime - startNanoTime : clock.nowNanos() - startNanoTime; + } + } + + /** + * Returns the {@code TimestampConverter} used by this {@code Span}. + * + * @return the {@code TimestampConverter} used by this {@code Span}. + */ + @Nullable + TimestampConverter getTimestampConverter() { + return timestampConverter; + } + + /** + * Returns an immutable representation of all the data from this {@code Span}. + * + * @return an immutable representation of all the data from this {@code Span}. + * @throws IllegalStateException if the Span doesn't have RECORD_EVENTS option. + */ + public SpanData toSpanData() { + checkState( + getOptions().contains(Options.RECORD_EVENTS), + "Getting SpanData for a Span without RECORD_EVENTS option."); + synchronized (this) { + SpanData.Attributes attributesSpanData = + attributes == null + ? SpanData.Attributes.create(Collections.emptyMap(), 0) + : SpanData.Attributes.create(attributes, attributes.getNumberOfDroppedAttributes()); + SpanData.TimedEvents annotationsSpanData = + createTimedEvents(annotations, timestampConverter); + SpanData.TimedEvents networkEventsSpanData = + createTimedEvents(networkEvents, timestampConverter); + SpanData.Links linksSpanData = + links == null + ? SpanData.Links.create(Collections.emptyList(), 0) + : SpanData.Links.create( + new ArrayList(links.events), links.getNumberOfDroppedEvents()); + return SpanData.create( + getContext(), + parentSpanId, + hasRemoteParent, + name, + timestampConverter.convertNanoTime(startNanoTime), + attributesSpanData, + annotationsSpanData, + networkEventsSpanData, + linksSpanData, + null, // Not supported yet. + hasBeenEnded ? status : null, + hasBeenEnded ? timestampConverter.convertNanoTime(endNanoTime) : null); + } + } + + @Override + public void addAttributes(Map attributes) { + if (!getOptions().contains(Options.RECORD_EVENTS)) { + return; + } + synchronized (this) { + if (hasBeenEnded) { + logger.log(Level.FINE, "Calling addAttributes() on an ended Span."); + return; + } + getInitializedAttributes().addAttributes(attributes); + } + } + + @Override + public void addAnnotation(String description, Map attributes) { + if (!getOptions().contains(Options.RECORD_EVENTS)) { + return; + } + synchronized (this) { + if (hasBeenEnded) { + logger.log(Level.FINE, "Calling addAnnotation() on an ended Span."); + return; + } + getInitializedAnnotations() + .addEvent( + new EventWithNanoTime( + clock.nowNanos(), + Annotation.fromDescriptionAndAttributes(description, attributes))); + } + } + + @Override + public void addAnnotation(Annotation annotation) { + if (!getOptions().contains(Options.RECORD_EVENTS)) { + return; + } + synchronized (this) { + if (hasBeenEnded) { + logger.log(Level.FINE, "Calling addAnnotation() on an ended Span."); + return; + } + getInitializedAnnotations() + .addEvent( + new EventWithNanoTime( + clock.nowNanos(), checkNotNull(annotation, "annotation"))); + } + } + + @Override + public void addNetworkEvent(NetworkEvent networkEvent) { + if (!getOptions().contains(Options.RECORD_EVENTS)) { + return; + } + synchronized (this) { + if (hasBeenEnded) { + logger.log(Level.FINE, "Calling addNetworkEvent() on an ended Span."); + return; + } + getInitializedNetworkEvents() + .addEvent( + new EventWithNanoTime( + clock.nowNanos(), checkNotNull(networkEvent, "networkEvent"))); + } + } + + @Override + public void addLink(Link link) { + if (!getOptions().contains(Options.RECORD_EVENTS)) { + return; + } + synchronized (this) { + if (hasBeenEnded) { + logger.log(Level.FINE, "Calling addLink() on an ended Span."); + return; + } + getInitializedLinks().addEvent(checkNotNull(link, "link")); + } + } + + @Override + public void end(EndSpanOptions options) { + if (!getOptions().contains(Options.RECORD_EVENTS)) { + return; + } + synchronized (this) { + if (hasBeenEnded) { + logger.log(Level.FINE, "Calling end() on an ended Span."); + return; + } + status = options.getStatus(); + endNanoTime = clock.nowNanos(); + hasBeenEnded = true; + } + startEndHandler.onEnd(this); + } + + @GuardedBy("this") + private AttributesWithCapacity getInitializedAttributes() { + if (attributes == null) { + attributes = new AttributesWithCapacity(traceParams.getMaxNumberOfAttributes()); + } + return attributes; + } + + @GuardedBy("this") + private TraceEvents> getInitializedAnnotations() { + if (annotations == null) { + annotations = + new TraceEvents>(traceParams.getMaxNumberOfAnnotations()); + } + return annotations; + } + + @GuardedBy("this") + private TraceEvents> getInitializedNetworkEvents() { + if (networkEvents == null) { + networkEvents = + new TraceEvents>( + traceParams.getMaxNumberOfNetworkEvents()); + } + return networkEvents; + } + + @GuardedBy("this") + private TraceEvents getInitializedLinks() { + if (links == null) { + links = new TraceEvents(traceParams.getMaxNumberOfLinks()); + } + return links; + } + + private static SpanData.TimedEvents createTimedEvents( + TraceEvents> events, TimestampConverter timestampConverter) { + if (events == null) { + return SpanData.TimedEvents.create(Collections.>emptyList(), 0); + } + List> eventsList = new ArrayList>(events.events.size()); + for (EventWithNanoTime networkEvent : events.events) { + eventsList.add(networkEvent.toSpanDataTimedEvent(timestampConverter)); + } + return SpanData.TimedEvents.create(eventsList, events.getNumberOfDroppedEvents()); + } + + @Override + public SpanImpl getNext() { + return next; + } + + @Override + public void setNext(SpanImpl element) { + next = element; + } + + @Override + public SpanImpl getPrev() { + return prev; + } + + @Override + public void setPrev(SpanImpl element) { + prev = element; + } + + /** + * Interface to handle the start and end operations for a {@link Span} only when the {@code Span} + * has {@link Options#RECORD_EVENTS} option. + * + *

Implementation must avoid high overhead work in any of the methods because the code is + * executed on the critical path. + * + *

One instance can be called by multiple threads in the same time, so the implementation must + * be thread-safe. + */ + public interface StartEndHandler { + void onStart(SpanImpl span); + + void onEnd(SpanImpl span); + } + + // A map implementation with a fixed capacity that drops events when the map gets full. Eviction + // is based on the access order. + private static final class AttributesWithCapacity extends LinkedHashMap { + private final int capacity; + private int totalRecordedAttributes = 0; + // Here because -Werror complains about this: [serial] serializable class AttributesWithCapacity + // has no definition of serialVersionUID. This class shouldn't be serialized. + private static final long serialVersionUID = 42L; + + private AttributesWithCapacity(int capacity) { + // Capacity of the map is capacity + 1 to avoid resizing because removeEldestEntry is invoked + // by put and putAll after inserting a new entry into the map. The loadFactor is set to 1 + // to avoid resizing because. The accessOrder is set to true. + super(capacity + 1, 1, true); + this.capacity = capacity; + } + + // Users must call this method instead of put or putAll to keep count of the total number of + // entries inserted. + private void addAttributes(Map attributes) { + totalRecordedAttributes += attributes.size(); + putAll(attributes); + } + + private int getNumberOfDroppedAttributes() { + return totalRecordedAttributes - size(); + } + + // It is called after each put or putAll call in order to determine if the eldest inserted + // entry should be removed or not. + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > this.capacity; + } + } + + private static final class TraceEvents { + private int totalRecordedEvents = 0; + private final EvictingQueue events; + + private int getNumberOfDroppedEvents() { + return totalRecordedEvents - events.size(); + } + + TraceEvents(int maxNumEvents) { + events = EvictingQueue.create(maxNumEvents); + } + + void addEvent(T event) { + totalRecordedEvents++; + events.add(event); + } + } + + // Timed event that uses nanoTime to represent the Timestamp. + private static final class EventWithNanoTime { + private final long nanoTime; + private final T event; + + private EventWithNanoTime(long nanoTime, T event) { + this.nanoTime = nanoTime; + this.event = event; + } + + private TimedEvent toSpanDataTimedEvent(TimestampConverter timestampConverter) { + return TimedEvent.create(timestampConverter.convertNanoTime(nanoTime), event); + } + } + + private SpanImpl( + SpanContext context, + @Nullable EnumSet options, + String name, + @Nullable SpanId parentSpanId, + @Nullable Boolean hasRemoteParent, + TraceParams traceParams, + StartEndHandler startEndHandler, + @Nullable TimestampConverter timestampConverter, + Clock clock) { + super(context, options); + this.parentSpanId = parentSpanId; + this.hasRemoteParent = hasRemoteParent; + this.name = name; + this.traceParams = traceParams; + this.startEndHandler = startEndHandler; + this.clock = clock; + this.hasBeenEnded = false; + if (getOptions().contains(Options.RECORD_EVENTS)) { + this.timestampConverter = + timestampConverter != null ? timestampConverter : TimestampConverter.now(clock); + startNanoTime = clock.nowNanos(); + } else { + this.startNanoTime = 0; + this.timestampConverter = timestampConverter; + } + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/StartEndHandlerImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/StartEndHandlerImpl.java new file mode 100644 index 00000000..76276cd7 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/StartEndHandlerImpl.java @@ -0,0 +1,124 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace; + +import io.opencensus.implcore.internal.EventQueue; +import io.opencensus.implcore.trace.SpanImpl.StartEndHandler; +import io.opencensus.implcore.trace.export.RunningSpanStoreImpl; +import io.opencensus.implcore.trace.export.SampledSpanStoreImpl; +import io.opencensus.implcore.trace.export.SpanExporterImpl; +import io.opencensus.trace.Span.Options; +import io.opencensus.trace.export.SpanData; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; + +/** + * Uses the provided {@link EventQueue} to defer processing/exporting of the {@link SpanData} to + * avoid impacting the critical path. + */ +@ThreadSafe +public final class StartEndHandlerImpl implements StartEndHandler { + private final SpanExporterImpl spanExporter; + private final RunningSpanStoreImpl runningSpanStore; + private final SampledSpanStoreImpl sampledSpanStore; + private final EventQueue eventQueue; + // true if any of (runningSpanStore OR sampledSpanStore) are different than null, which + // means the spans with RECORD_EVENTS should be enqueued in the queue. + private final boolean enqueueEventForNonSampledSpans; + + /** + * Constructs a new {@code StartEndHandlerImpl}. + * + * @param spanExporter the {@code SpanExporter} implementation. + * @param runningSpanStore the {@code RunningSpanStore} implementation. + * @param sampledSpanStore the {@code SampledSpanStore} implementation. + * @param eventQueue the event queue where all the events are enqueued. + */ + public StartEndHandlerImpl( + SpanExporterImpl spanExporter, + @Nullable RunningSpanStoreImpl runningSpanStore, + @Nullable SampledSpanStoreImpl sampledSpanStore, + EventQueue eventQueue) { + this.spanExporter = spanExporter; + this.runningSpanStore = runningSpanStore; + this.sampledSpanStore = sampledSpanStore; + this.enqueueEventForNonSampledSpans = runningSpanStore != null || sampledSpanStore != null; + this.eventQueue = eventQueue; + } + + @Override + public void onStart(SpanImpl span) { + if (span.getOptions().contains(Options.RECORD_EVENTS) && enqueueEventForNonSampledSpans) { + eventQueue.enqueue(new SpanStartEvent(span, runningSpanStore)); + } + } + + @Override + public void onEnd(SpanImpl span) { + if ((span.getOptions().contains(Options.RECORD_EVENTS) && enqueueEventForNonSampledSpans) + || span.getContext().getTraceOptions().isSampled()) { + eventQueue.enqueue(new SpanEndEvent(span, spanExporter, runningSpanStore, sampledSpanStore)); + } + } + + // An EventQueue entry that records the start of the span event. + private static final class SpanStartEvent implements EventQueue.Entry { + private final SpanImpl span; + private final RunningSpanStoreImpl activeSpansExporter; + + SpanStartEvent(SpanImpl span, @Nullable RunningSpanStoreImpl activeSpansExporter) { + this.span = span; + this.activeSpansExporter = activeSpansExporter; + } + + @Override + public void process() { + if (activeSpansExporter != null) { + activeSpansExporter.onStart(span); + } + } + } + + // An EventQueue entry that records the end of the span event. + private static final class SpanEndEvent implements EventQueue.Entry { + private final SpanImpl span; + private final RunningSpanStoreImpl runningSpanStore; + private final SpanExporterImpl spanExporter; + private final SampledSpanStoreImpl sampledSpanStore; + + SpanEndEvent( + SpanImpl span, + SpanExporterImpl spanExporter, + @Nullable RunningSpanStoreImpl runningSpanStore, + @Nullable SampledSpanStoreImpl sampledSpanStore) { + this.span = span; + this.runningSpanStore = runningSpanStore; + this.spanExporter = spanExporter; + this.sampledSpanStore = sampledSpanStore; + } + + @Override + public void process() { + if (span.getContext().getTraceOptions().isSampled()) { + spanExporter.addSpan(span); + } + if (runningSpanStore != null) { + runningSpanStore.onEnd(span); + } + if (sampledSpanStore != null) { + sampledSpanStore.considerForSampling(span); + } + } + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/TraceComponentImplBase.java b/impl_core/src/main/java/io/opencensus/implcore/trace/TraceComponentImplBase.java new file mode 100644 index 00000000..8c16950d --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/TraceComponentImplBase.java @@ -0,0 +1,87 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace; + +import io.opencensus.common.Clock; +import io.opencensus.implcore.internal.EventQueue; +import io.opencensus.implcore.internal.SimpleEventQueue; +import io.opencensus.implcore.trace.SpanImpl.StartEndHandler; +import io.opencensus.implcore.trace.config.TraceConfigImpl; +import io.opencensus.implcore.trace.export.ExportComponentImpl; +import io.opencensus.implcore.trace.internal.RandomHandler; +import io.opencensus.implcore.trace.propagation.PropagationComponentImpl; +import io.opencensus.trace.TraceComponent; +import io.opencensus.trace.Tracer; +import io.opencensus.trace.config.TraceConfig; +import io.opencensus.trace.export.ExportComponent; +import io.opencensus.trace.propagation.PropagationComponent; + +/** Base implementation of the {@link TraceComponent}. */ +public class TraceComponentImplBase extends TraceComponent { + private final ExportComponentImpl exportComponent; + private final PropagationComponent propagationComponent = new PropagationComponentImpl(); + private final Clock clock; + private final StartEndHandler startEndHandler; + private final TraceConfig traceConfig = new TraceConfigImpl(); + private final Tracer tracer; + + /** + * Creates a new {@code TraceComponentImplBase}. + * + * @param clock the clock to use throughout tracing. + * @param randomHandler the random number generator for generating trace and span IDs. + * @param eventQueue the queue implementation. + */ + public TraceComponentImplBase(Clock clock, RandomHandler randomHandler, EventQueue eventQueue) { + this.clock = clock; + // TODO(bdrutu): Add a config/argument for supportInProcessStores. + if (eventQueue instanceof SimpleEventQueue) { + exportComponent = ExportComponentImpl.createWithoutInProcessStores(); + } else { + exportComponent = ExportComponentImpl.createWithInProcessStores(); + } + startEndHandler = + new StartEndHandlerImpl( + exportComponent.getSpanExporter(), + exportComponent.getRunningSpanStore(), + exportComponent.getSampledSpanStore(), + eventQueue); + tracer = new TracerImpl(randomHandler, startEndHandler, clock, traceConfig); + } + + @Override + public Tracer getTracer() { + return tracer; + } + + @Override + public PropagationComponent getPropagationComponent() { + return propagationComponent; + } + + @Override + public final Clock getClock() { + return clock; + } + + @Override + public ExportComponent getExportComponent() { + return exportComponent; + } + + @Override + public TraceConfig getTraceConfig() { + return traceConfig; + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/TracerImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/TracerImpl.java new file mode 100644 index 00000000..0da79120 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/TracerImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace; + +import io.opencensus.common.Clock; +import io.opencensus.implcore.trace.internal.RandomHandler; +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanBuilder; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.Tracer; +import io.opencensus.trace.config.TraceConfig; +import javax.annotation.Nullable; + +/** Implementation of the {@link Tracer}. */ +public final class TracerImpl extends Tracer { + private final SpanBuilderImpl.Options spanBuilderOptions; + + public TracerImpl( + RandomHandler randomHandler, + SpanImpl.StartEndHandler startEndHandler, + Clock clock, + TraceConfig traceConfig) { + spanBuilderOptions = + new SpanBuilderImpl.Options(randomHandler, startEndHandler, clock, traceConfig); + } + + @Override + public SpanBuilder spanBuilderWithExplicitParent(String spanName, @Nullable Span parent) { + return SpanBuilderImpl.createWithParent(spanName, parent, spanBuilderOptions); + } + + @Override + public SpanBuilder spanBuilderWithRemoteParent( + String spanName, @Nullable SpanContext remoteParentSpanContext) { + return SpanBuilderImpl.createWithRemoteParent( + spanName, remoteParentSpanContext, spanBuilderOptions); + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/config/TraceConfigImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/config/TraceConfigImpl.java new file mode 100644 index 00000000..1f14226b --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/config/TraceConfigImpl.java @@ -0,0 +1,40 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace.config; + +import io.opencensus.trace.config.TraceConfig; +import io.opencensus.trace.config.TraceParams; + +/** + * Global configuration of the trace service. This allows users to change configs for the default + * sampler, maximum events to be kept, etc. + */ +public final class TraceConfigImpl extends TraceConfig { + // Reads and writes are atomic for reference variables. Use volatile to ensure that these + // operations are visible on other CPUs as well. + private volatile TraceParams activeTraceParams = TraceParams.DEFAULT; + + /** Constructs a new {@code TraceConfigImpl}. */ + public TraceConfigImpl() {} + + @Override + public TraceParams getActiveTraceParams() { + return activeTraceParams; + } + + @Override + public void updateActiveTraceParams(TraceParams traceParams) { + activeTraceParams = traceParams; + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/export/ExportComponentImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/export/ExportComponentImpl.java new file mode 100644 index 00000000..ac0f8087 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/export/ExportComponentImpl.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace.export; + +import io.opencensus.trace.export.ExportComponent; +import io.opencensus.trace.export.RunningSpanStore; +import io.opencensus.trace.export.SampledSpanStore; +import javax.annotation.Nullable; + +/** Implementation of the {@link ExportComponent}. */ +public final class ExportComponentImpl extends ExportComponent { + private static final int EXPORTER_BUFFER_SIZE = 32; + // Enforces that trace export exports data at least once every 2 seconds. + private static final long EXPORTER_SCHEDULE_DELAY_MS = 2000; + + private final SpanExporterImpl spanExporter; + private final RunningSpanStoreImpl runningSpanStore; + private final SampledSpanStoreImpl sampledSpanStore; + + @Override + public SpanExporterImpl getSpanExporter() { + return spanExporter; + } + + @Nullable + @Override + public RunningSpanStoreImpl getRunningSpanStore() { + return runningSpanStore; + } + + @Nullable + @Override + public SampledSpanStoreImpl getSampledSpanStore() { + return sampledSpanStore; + } + + /** + * Returns a new {@code ExportComponentImpl} that has valid instances for {@link RunningSpanStore} + * and {@link SampledSpanStore}. + * + * @return a new {@code ExportComponentImpl}. + */ + public static ExportComponentImpl createWithInProcessStores() { + return new ExportComponentImpl(true); + } + + /** + * Returns a new {@code ExportComponentImpl} that has {@code null} instances for {@link + * RunningSpanStore} and {@link SampledSpanStore}. + * + * @return a new {@code ExportComponentImpl}. + */ + public static ExportComponentImpl createWithoutInProcessStores() { + return new ExportComponentImpl(false); + } + + /** + * Constructs a new {@code ExportComponentImpl}. + * + * @param supportInProcessStores {@code true} to instantiate {@link RunningSpanStore} and {@link + * SampledSpanStore}. + */ + private ExportComponentImpl(boolean supportInProcessStores) { + this.spanExporter = SpanExporterImpl.create(EXPORTER_BUFFER_SIZE, EXPORTER_SCHEDULE_DELAY_MS); + this.runningSpanStore = supportInProcessStores ? new RunningSpanStoreImpl() : null; + this.sampledSpanStore = supportInProcessStores ? new SampledSpanStoreImpl() : null; + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/export/RunningSpanStoreImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/export/RunningSpanStoreImpl.java new file mode 100644 index 00000000..a793644a --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/export/RunningSpanStoreImpl.java @@ -0,0 +1,86 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace.export; + +import io.opencensus.implcore.trace.SpanImpl; +import io.opencensus.implcore.trace.internal.ConcurrentIntrusiveList; +import io.opencensus.trace.export.RunningSpanStore; +import io.opencensus.trace.export.SpanData; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.concurrent.ThreadSafe; + +/** Implementation of the {@link RunningSpanStore}. */ +@ThreadSafe +public final class RunningSpanStoreImpl extends RunningSpanStore { + private final ConcurrentIntrusiveList runningSpans; + + public RunningSpanStoreImpl() { + runningSpans = new ConcurrentIntrusiveList(); + } + + /** + * Adds the {@code Span} into the running spans list when the {@code Span} starts. + * + * @param span the {@code Span} that started. + */ + public void onStart(SpanImpl span) { + runningSpans.addElement(span); + } + + /** + * Removes the {@code Span} from the running spans list when the {@code Span} ends. + * + * @param span the {@code Span} that ended. + */ + public void onEnd(SpanImpl span) { + runningSpans.removeElement(span); + } + + @Override + public Summary getSummary() { + Collection allRunningSpans = runningSpans.getAll(); + Map numSpansPerName = new HashMap(); + for (SpanImpl span : allRunningSpans) { + Integer prevValue = numSpansPerName.get(span.getName()); + numSpansPerName.put(span.getName(), prevValue != null ? prevValue + 1 : 1); + } + Map perSpanNameSummary = new HashMap(); + for (Map.Entry it : numSpansPerName.entrySet()) { + perSpanNameSummary.put(it.getKey(), PerSpanNameSummary.create(it.getValue())); + } + Summary summary = Summary.create(perSpanNameSummary); + return summary; + } + + @Override + public Collection getRunningSpans(Filter filter) { + Collection allRunningSpans = runningSpans.getAll(); + int maxSpansToReturn = + filter.getMaxSpansToReturn() == 0 ? allRunningSpans.size() : filter.getMaxSpansToReturn(); + List ret = new ArrayList(maxSpansToReturn); + for (SpanImpl span : allRunningSpans) { + if (ret.size() == maxSpansToReturn) { + break; + } + if (span.getName().equals(filter.getSpanName())) { + ret.add(span.toSpanData()); + } + } + return ret; + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/export/SampledSpanStoreImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/export/SampledSpanStoreImpl.java new file mode 100644 index 00000000..5331ade6 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/export/SampledSpanStoreImpl.java @@ -0,0 +1,291 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace.export; + +import com.google.common.collect.EvictingQueue; +import io.opencensus.implcore.trace.SpanImpl; +import io.opencensus.trace.Status; +import io.opencensus.trace.Status.CanonicalCode; +import io.opencensus.trace.export.SampledSpanStore; +import io.opencensus.trace.export.SpanData; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +/** Implementation of the {@link SampledSpanStore}. */ +@ThreadSafe +public final class SampledSpanStoreImpl extends SampledSpanStore { + private static final int NUM_SAMPLES_PER_LATENCY_BUCKET = 10; + private static final int NUM_SAMPLES_PER_ERROR_BUCKET = 5; + private static final long TIME_BETWEEN_SAMPLES = TimeUnit.SECONDS.toNanos(1); + private static final int NUM_LATENCY_BUCKETS = LatencyBucketBoundaries.values().length; + // The total number of canonical codes - 1 (the OK code). + private static final int NUM_ERROR_BUCKETS = CanonicalCode.values().length - 1; + private static final int MAX_PER_SPAN_NAME_SAMPLES = + NUM_SAMPLES_PER_LATENCY_BUCKET * NUM_LATENCY_BUCKETS + + NUM_SAMPLES_PER_ERROR_BUCKET * NUM_ERROR_BUCKETS; + + @GuardedBy("samples") + private final Map samples; + + private static final class Bucket { + + private final EvictingQueue queue; + private long lastSampleNanoTime; + + private Bucket(int numSamples) { + queue = EvictingQueue.create(numSamples); + } + + private void considerForSampling(SpanImpl span) { + long spanEndNanoTime = span.getEndNanoTime(); + // Need to compare by doing the subtraction all the time because in case of an overflow, + // this may never sample again (at least for the next ~200 years). No real chance to + // overflow two times because that means the process runs for ~200 years. + if (spanEndNanoTime - lastSampleNanoTime > TIME_BETWEEN_SAMPLES) { + queue.add(span); + lastSampleNanoTime = spanEndNanoTime; + } + } + + private void getSamples(int maxSpansToReturn, List output) { + for (SpanImpl span : queue) { + if (output.size() >= maxSpansToReturn) { + break; + } + output.add(span); + } + } + + private void getSamplesFilteredByLatency( + long latencyLowerNs, long latencyUpperNs, int maxSpansToReturn, List output) { + for (SpanImpl span : queue) { + if (output.size() >= maxSpansToReturn) { + break; + } + long spanLatencyNs = span.getLatencyNs(); + if (spanLatencyNs >= latencyLowerNs && spanLatencyNs < latencyUpperNs) { + output.add(span); + } + } + } + + private int getNumSamples() { + return queue.size(); + } + } + + /** + * Keeps samples for a given span name. Samples for all the latency buckets and for all canonical + * codes other than OK. + */ + private static final class PerSpanNameSamples { + + private final Bucket[] latencyBuckets; + private final Bucket[] errorBuckets; + + private PerSpanNameSamples() { + latencyBuckets = new Bucket[NUM_LATENCY_BUCKETS]; + for (int i = 0; i < NUM_LATENCY_BUCKETS; i++) { + latencyBuckets[i] = new Bucket(NUM_SAMPLES_PER_LATENCY_BUCKET); + } + errorBuckets = new Bucket[NUM_ERROR_BUCKETS]; + for (int i = 0; i < NUM_ERROR_BUCKETS; i++) { + errorBuckets[i] = new Bucket(NUM_SAMPLES_PER_ERROR_BUCKET); + } + } + + private Bucket getLatencyBucket(long latencyNs) { + for (int i = 0; i < NUM_LATENCY_BUCKETS; i++) { + LatencyBucketBoundaries boundaries = LatencyBucketBoundaries.values()[i]; + if (latencyNs >= boundaries.getLatencyLowerNs() + && latencyNs < boundaries.getLatencyUpperNs()) { + return latencyBuckets[i]; + } + } + // latencyNs is negative or Long.MAX_VALUE, so this Span can be ignored. This cannot happen + // in real production because System#nanoTime is monotonic. + return null; + } + + private Bucket getErrorBucket(CanonicalCode code) { + return errorBuckets[code.value() - 1]; + } + + private void considerForSampling(SpanImpl span) { + Status status = span.getStatus(); + // Null status means running Span, this should not happen in production, but the library + // should not crash because of this. + if (status != null) { + Bucket bucket = + status.isOk() + ? getLatencyBucket(span.getLatencyNs()) + : getErrorBucket(status.getCanonicalCode()); + // If unable to find the bucket, ignore this Span. + if (bucket != null) { + bucket.considerForSampling(span); + } + } + } + + private Map getNumbersOfLatencySampledSpans() { + Map latencyBucketSummaries = + new EnumMap(LatencyBucketBoundaries.class); + for (int i = 0; i < NUM_LATENCY_BUCKETS; i++) { + latencyBucketSummaries.put( + LatencyBucketBoundaries.values()[i], latencyBuckets[i].getNumSamples()); + } + return latencyBucketSummaries; + } + + private Map getNumbersOfErrorSampledSpans() { + Map errorBucketSummaries = + new EnumMap(CanonicalCode.class); + for (int i = 0; i < NUM_ERROR_BUCKETS; i++) { + errorBucketSummaries.put(CanonicalCode.values()[i + 1], errorBuckets[i].getNumSamples()); + } + return errorBucketSummaries; + } + + private List getErrorSamples(CanonicalCode code, int maxSpansToReturn) { + ArrayList output = new ArrayList(maxSpansToReturn); + if (code != null) { + getErrorBucket(code).getSamples(maxSpansToReturn, output); + } else { + for (int i = 0; i < NUM_ERROR_BUCKETS; i++) { + errorBuckets[i].getSamples(maxSpansToReturn, output); + } + } + return output; + } + + private List getLatencySamples( + long latencyLowerNs, long latencyUpperNs, int maxSpansToReturn) { + ArrayList output = new ArrayList(maxSpansToReturn); + for (int i = 0; i < NUM_LATENCY_BUCKETS; i++) { + LatencyBucketBoundaries boundaries = LatencyBucketBoundaries.values()[i]; + if (latencyUpperNs >= boundaries.getLatencyLowerNs() + && latencyLowerNs < boundaries.getLatencyUpperNs()) { + latencyBuckets[i].getSamplesFilteredByLatency( + latencyLowerNs, latencyUpperNs, maxSpansToReturn, output); + } + } + return output; + } + } + + /** Constructs a new {@code SampledSpanStoreImpl}. */ + public SampledSpanStoreImpl() { + samples = new HashMap(); + } + + @Override + public Summary getSummary() { + Map ret = new HashMap(); + synchronized (samples) { + for (Map.Entry it : samples.entrySet()) { + ret.put( + it.getKey(), + PerSpanNameSummary.create( + it.getValue().getNumbersOfLatencySampledSpans(), + it.getValue().getNumbersOfErrorSampledSpans())); + } + } + return Summary.create(ret); + } + + /** + * Considers to save the given spans to the stored samples. This must be called at the end of each + * Span with the option RECORD_EVENTS. + * + * @param span the span to be consider for storing into the store buckets. + */ + public void considerForSampling(SpanImpl span) { + synchronized (samples) { + PerSpanNameSamples perSpanNameSamples = samples.get(span.getName()); + if (perSpanNameSamples != null) { + perSpanNameSamples.considerForSampling(span); + } + } + } + + @Override + public void registerSpanNamesForCollection(Collection spanNames) { + synchronized (samples) { + for (String spanName : spanNames) { + if (!samples.containsKey(spanName)) { + samples.put(spanName, new PerSpanNameSamples()); + } + } + } + } + + @Override + public void unregisterSpanNamesForCollection(Collection spanNames) { + synchronized (samples) { + samples.keySet().removeAll(spanNames); + } + } + + @Override + public Collection getErrorSampledSpans(ErrorFilter filter) { + int numSpansToReturn = + filter.getMaxSpansToReturn() == 0 + ? MAX_PER_SPAN_NAME_SAMPLES + : filter.getMaxSpansToReturn(); + List spans = Collections.emptyList(); + // Try to not keep the lock to much, do the SpanImpl -> SpanData conversion outside the lock. + synchronized (samples) { + PerSpanNameSamples perSpanNameSamples = samples.get(filter.getSpanName()); + if (perSpanNameSamples != null) { + spans = perSpanNameSamples.getErrorSamples(filter.getCanonicalCode(), numSpansToReturn); + } + } + List ret = new ArrayList(spans.size()); + for (SpanImpl span : spans) { + ret.add(span.toSpanData()); + } + return Collections.unmodifiableList(ret); + } + + @Override + public Collection getLatencySampledSpans(LatencyFilter filter) { + int numSpansToReturn = + filter.getMaxSpansToReturn() == 0 + ? MAX_PER_SPAN_NAME_SAMPLES + : filter.getMaxSpansToReturn(); + List spans = Collections.emptyList(); + // Try to not keep the lock to much, do the SpanImpl -> SpanData conversion outside the lock. + synchronized (samples) { + PerSpanNameSamples perSpanNameSamples = samples.get(filter.getSpanName()); + if (perSpanNameSamples != null) { + spans = + perSpanNameSamples.getLatencySamples( + filter.getLatencyLowerNs(), filter.getLatencyUpperNs(), numSpansToReturn); + } + } + List ret = new ArrayList(spans.size()); + for (SpanImpl span : spans) { + ret.add(span.toSpanData()); + } + return Collections.unmodifiableList(ret); + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/export/SpanExporterImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/export/SpanExporterImpl.java new file mode 100644 index 00000000..af46cf54 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/export/SpanExporterImpl.java @@ -0,0 +1,187 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace.export; + +import com.google.common.annotations.VisibleForTesting; +import io.opencensus.implcore.trace.SpanImpl; +import io.opencensus.trace.export.ExportComponent; +import io.opencensus.trace.export.SpanData; +import io.opencensus.trace.export.SpanExporter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.concurrent.GuardedBy; + +/** Implementation of the {@link SpanExporter}. */ +public final class SpanExporterImpl extends SpanExporter { + private static final Logger logger = Logger.getLogger(ExportComponent.class.getName()); + + private final WorkerThread workerThread; + + /** + * Constructs a {@code SpanExporterImpl} that exports the {@link SpanData} asynchronously. + * + *

Starts a separate thread that wakes up every {@code scheduleDelay} and exports any available + * spans data. If the number of buffered SpanData objects is greater than {@code bufferSize} then + * the thread wakes up sooner. + * + * @param bufferSize the size of the buffered span data. + * @param scheduleDelayMillis the maximum delay in milliseconds. + */ + static SpanExporterImpl create(int bufferSize, long scheduleDelayMillis) { + // TODO(bdrutu): Consider to add a shutdown hook to not avoid dropping data. + WorkerThread workerThread = new WorkerThread(bufferSize, scheduleDelayMillis); + workerThread.start(); + return new SpanExporterImpl(workerThread); + } + + /** + * Adds a Span to the exporting service. + * + * @param span the {@code Span} to be added. + */ + public void addSpan(SpanImpl span) { + workerThread.addSpan(span); + } + + @Override + public void registerHandler(String name, Handler handler) { + workerThread.registerHandler(name, handler); + } + + @Override + public void unregisterHandler(String name) { + workerThread.unregisterHandler(name); + } + + private SpanExporterImpl(WorkerThread workerThread) { + this.workerThread = workerThread; + } + + @VisibleForTesting + Thread getServiceExporterThread() { + return workerThread; + } + + // Worker thread that batches multiple span data and calls the registered services to export + // that data. + // + // The map of registered handlers is implemented using ConcurrentHashMap ensuring full + // concurrency of retrievals and adjustable expected concurrency for updates. Retrievals + // reflect the results of the most recently completed update operations held upon their onset. + // + // The list of batched data is protected by an explicit monitor object which ensures full + // concurrency. + private static final class WorkerThread extends Thread { + private final Object monitor = new Object(); + + @GuardedBy("monitor") + private final List spans; + + private final Map serviceHandlers = new ConcurrentHashMap(); + private final int bufferSize; + private final long scheduleDelayMillis; + + // See SpanExporterImpl#addSpan. + private void addSpan(SpanImpl span) { + synchronized (monitor) { + this.spans.add(span); + if (spans.size() > bufferSize) { + monitor.notifyAll(); + } + } + } + + // See SpanExporter#registerHandler. + private void registerHandler(String name, Handler serviceHandler) { + serviceHandlers.put(name, serviceHandler); + } + + // See SpanExporter#unregisterHandler. + private void unregisterHandler(String name) { + serviceHandlers.remove(name); + } + + // Exports the list of SpanData to all the ServiceHandlers. + private void onBatchExport(List spanDataList) { + // From the java documentation of the ConcurrentHashMap#entrySet(): + // The view's iterator is a "weakly consistent" iterator that will never throw + // ConcurrentModificationException, and guarantees to traverse elements as they existed + // upon construction of the iterator, and may (but is not guaranteed to) reflect any + // modifications subsequent to construction. + for (Map.Entry it : serviceHandlers.entrySet()) { + // In case of any exception thrown by the service handlers continue to run. + try { + it.getValue().export(spanDataList); + } catch (Throwable e) { + logger.log(Level.WARNING, "Exception thrown by the service export " + it.getKey(), e); + } + } + } + + private WorkerThread(int bufferSize, long scheduleDelayMillis) { + spans = new LinkedList(); + this.bufferSize = bufferSize; + this.scheduleDelayMillis = scheduleDelayMillis; + setDaemon(true); + setName("ExportComponent.ServiceExporterThread"); + } + + // Returns an unmodifiable list of all buffered spans data to ensure that any registered + // service handler cannot modify the list. + private static List fromSpanImplToSpanData(List spans) { + List spanDatas = new ArrayList(spans.size()); + for (SpanImpl span : spans) { + spanDatas.add(span.toSpanData()); + } + return Collections.unmodifiableList(spanDatas); + } + + @Override + public void run() { + while (true) { + // Copy all the batched spans in a separate list to release the monitor lock asap to + // avoid blocking the producer thread. + List spansCopy; + synchronized (monitor) { + if (spans.size() < bufferSize) { + do { + // In the case of a spurious wakeup we export only if we have at least one span in + // the batch. It is acceptable because batching is a best effort mechanism here. + try { + monitor.wait(scheduleDelayMillis); + } catch (InterruptedException ie) { + // Preserve the interruption status as per guidance and stop doing any work. + Thread.currentThread().interrupt(); + return; + } + } while (spans.isEmpty()); + } + spansCopy = new ArrayList(spans); + spans.clear(); + } + // Execute the batch export outside the synchronized to not block all producers. + final List spanDataList = fromSpanImplToSpanData(spansCopy); + if (!spanDataList.isEmpty()) { + onBatchExport(spanDataList); + } + } + } + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/internal/ConcurrentIntrusiveList.java b/impl_core/src/main/java/io/opencensus/implcore/trace/internal/ConcurrentIntrusiveList.java new file mode 100644 index 00000000..ef2388c2 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/internal/ConcurrentIntrusiveList.java @@ -0,0 +1,174 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace.internal; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.opencensus.implcore.trace.internal.ConcurrentIntrusiveList.Element; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.annotation.concurrent.ThreadSafe; + +/** + * An {@code ConcurrentIntrusiveList} is a doubly-linked list where the link pointers are + * embedded in the elements. This makes insertion and removal into a known position constant time. + * + *

Elements must derive from the {@code Element>} interface: + * + *

{@code
+ * class MyClass implements Element {
+ *   private MyClass next = null;
+ *   private MyClass prev = null;
+ *
+ *   {@literal @}Override
+ *   MyClass getNext() {
+ *     return next;
+ *   }
+ *
+ *   {@literal @}Override
+ *   void setNext(MyClass element) {
+ *     next = element;
+ *   }
+ *
+ *   {@literal @}Override
+ *   MyClass getPrev() {
+ *     return prev;
+ *   }
+ *
+ *   {@literal @}Override
+ *   void setPrev(MyClass element) {
+ *     prev = element;
+ *   }
+ * }
+ * }
+ */ +@ThreadSafe +public final class ConcurrentIntrusiveList> { + private int size = 0; + private T head = null; + + public ConcurrentIntrusiveList() {} + + /** + * Adds the given {@code element} to the list. + * + * @param element the element to add. + * @throws IllegalArgumentException if the element is already in a list. + */ + public synchronized void addElement(T element) { + checkArgument( + element.getNext() == null && element.getPrev() == null && element != head, + "Element already in a list."); + size++; + if (head == null) { + head = element; + } else { + element.setNext(head); + head.setPrev(element); + head = element; + } + } + + /** + * Removes the given {@code element} from the list. + * + * @param element the element to remove. + * @throws IllegalArgumentException if the element is not in the list. + */ + public synchronized void removeElement(T element) { + checkArgument( + element.getNext() != null || element.getPrev() != null || element == head, + "Element not in the list."); + size--; + if (element.getPrev() == null) { + // This is the first element + head = element.getNext(); + if (head != null) { + // If more than one element in the list. + head.setPrev(null); + element.setNext(null); + } + } else if (element.getNext() == null) { + // This is the last element, and there is at least another element because + // element.getPrev() != null. + element.getPrev().setNext(null); + element.setPrev(null); + } else { + element.getPrev().setNext(element.getNext()); + element.getNext().setPrev(element.getPrev()); + element.setNext(null); + element.setPrev(null); + } + } + + /** + * Returns the number of elements in this list. + * + * @return the number of elements in this list. + */ + public synchronized int size() { + return size; + } + + /** + * Returns all the elements from this list. + * + * @return all the elements from this list. + */ + public synchronized Collection getAll() { + List all = new ArrayList(size); + for (T e = head; e != null; e = e.getNext()) { + all.add(e); + } + return all; + } + + /** + * This is an interface that must be implemented by any element that uses {@link + * ConcurrentIntrusiveList}. + * + * @param the element that will be used for the list. + */ + public interface Element> { + + /** + * Returns a reference to the next element in the list. + * + * @return a reference to the next element in the list. + */ + T getNext(); + + /** + * Sets the reference to the next element in the list. + * + * @param element the reference to the next element in the list. + */ + void setNext(T element); + + /** + * Returns a reference to the previous element in the list. + * + * @return a reference to the previous element in the list. + */ + T getPrev(); + + /** + * Sets the reference to the previous element in the list. + * + * @param element the reference to the previous element in the list. + */ + void setPrev(T element); + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/internal/RandomHandler.java b/impl_core/src/main/java/io/opencensus/implcore/trace/internal/RandomHandler.java new file mode 100644 index 00000000..702bd474 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/internal/RandomHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace.internal; + +import java.security.SecureRandom; +import java.util.Random; +import javax.annotation.concurrent.ThreadSafe; + +/** + * Abstract class to access the current {@link Random}. + * + *

Implementation can have a per thread instance or a single global instance. + */ +@ThreadSafe +public abstract class RandomHandler { + /** + * Returns the current {@link Random}. + * + * @return the current {@code Random}. + */ + public abstract Random current(); + + /** Implementation of the {@link RandomHandler} using {@link SecureRandom}. */ + @ThreadSafe + public static final class SecureRandomHandler extends RandomHandler { + private final Random random = new SecureRandom(); + + /** Constructs a new {@link SecureRandomHandler}. */ + public SecureRandomHandler() {} + + @Override + public Random current() { + return random; + } + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java new file mode 100644 index 00000000..0aa70189 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java @@ -0,0 +1,117 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace.propagation; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.SpanId; +import io.opencensus.trace.TraceId; +import io.opencensus.trace.TraceOptions; +import io.opencensus.trace.propagation.BinaryFormat; +import java.text.ParseException; + +/** + * Implementation of the {@link BinaryFormat}. + * + *

BinaryFormat format: + * + *

    + *
  • Binary value: <version_id><version_format> + *
  • version_id: 1-byte representing the version id. + *
  • For version_id = 0: + *
      + *
    • version_format: <field><field> + *
    • field_format: <field_id><field_format> + *
    • Fields: + *
        + *
      • TraceId: (field_id = 0, len = 16, default = "0000000000000000") - + * 16-byte array representing the trace_id. + *
      • SpanId: (field_id = 1, len = 8, default = "00000000") - 8-byte array + * representing the span_id. + *
      • TraceOptions: (field_id = 2, len = 1, default = "0") - 1-byte array + * representing the trace_options. + *
      + *
    • Fields MUST be encoded using the field id order (smaller to higher). + *
    • Valid value example: + *
        + *
      • {0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97, + * 98, 99, 100, 101, 102, 103, 104, 2, 1} + *
      • version_id = 0; + *
      • trace_id = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79} + *
      • span_id = {97, 98, 99, 100, 101, 102, 103, 104}; + *
      • trace_options = {1}; + *
      + *
    + *
+ */ +public final class BinaryFormatImpl extends BinaryFormat { + private static final byte VERSION_ID = 0; + private static final int VERSION_ID_OFFSET = 0; + // The version_id/field_id size in bytes. + private static final byte ID_SIZE = 1; + private static final byte TRACE_ID_FIELD_ID = 0; + private static final int TRACE_ID_FIELD_ID_OFFSET = VERSION_ID_OFFSET + ID_SIZE; + private static final int TRACE_ID_OFFSET = TRACE_ID_FIELD_ID_OFFSET + ID_SIZE; + private static final byte SPAN_ID_FIELD_ID = 1; + private static final int SPAN_ID_FIELD_ID_OFFSET = TRACE_ID_OFFSET + TraceId.SIZE; + private static final int SPAN_ID_OFFSET = SPAN_ID_FIELD_ID_OFFSET + ID_SIZE; + private static final byte TRACE_OPTION_FIELD_ID = 2; + private static final int TRACE_OPTION_FIELD_ID_OFFSET = SPAN_ID_OFFSET + SpanId.SIZE; + private static final int TRACE_OPTIONS_OFFSET = TRACE_OPTION_FIELD_ID_OFFSET + ID_SIZE; + private static final int FORMAT_LENGTH = + 4 * ID_SIZE + TraceId.SIZE + SpanId.SIZE + TraceOptions.SIZE; + + @Override + public byte[] toBinaryValue(SpanContext spanContext) { + checkNotNull(spanContext, "spanContext"); + byte[] bytes = new byte[FORMAT_LENGTH]; + bytes[VERSION_ID_OFFSET] = VERSION_ID; + bytes[TRACE_ID_FIELD_ID_OFFSET] = TRACE_ID_FIELD_ID; + spanContext.getTraceId().copyBytesTo(bytes, TRACE_ID_OFFSET); + bytes[SPAN_ID_FIELD_ID_OFFSET] = SPAN_ID_FIELD_ID; + spanContext.getSpanId().copyBytesTo(bytes, SPAN_ID_OFFSET); + bytes[TRACE_OPTION_FIELD_ID_OFFSET] = TRACE_OPTION_FIELD_ID; + spanContext.getTraceOptions().copyBytesTo(bytes, TRACE_OPTIONS_OFFSET); + return bytes; + } + + @Override + public SpanContext fromBinaryValue(byte[] bytes) throws ParseException { + checkNotNull(bytes, "bytes"); + if (bytes.length == 0 || bytes[0] != VERSION_ID) { + throw new ParseException("Unsupported version.", 0); + } + TraceId traceId = TraceId.INVALID; + SpanId spanId = SpanId.INVALID; + TraceOptions traceOptions = TraceOptions.DEFAULT; + int pos = 1; + try { + if (bytes.length > pos && bytes[pos] == TRACE_ID_FIELD_ID) { + traceId = TraceId.fromBytes(bytes, pos + ID_SIZE); + pos += ID_SIZE + TraceId.SIZE; + } + if (bytes.length > pos && bytes[pos] == SPAN_ID_FIELD_ID) { + spanId = SpanId.fromBytes(bytes, pos + ID_SIZE); + pos += ID_SIZE + SpanId.SIZE; + } + if (bytes.length > pos && bytes[pos] == TRACE_OPTION_FIELD_ID) { + traceOptions = TraceOptions.fromBytes(bytes, pos + ID_SIZE); + } + return SpanContext.create(traceId, spanId, traceOptions); + } catch (IndexOutOfBoundsException e) { + throw new ParseException("Invalid input: " + e.toString(), pos); + } + } +} diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/PropagationComponentImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/PropagationComponentImpl.java new file mode 100644 index 00000000..b090ab76 --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/PropagationComponentImpl.java @@ -0,0 +1,27 @@ +/* + * Copyright 2017, Google Inc. + * 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.implcore.trace.propagation; + +import io.opencensus.trace.propagation.BinaryFormat; +import io.opencensus.trace.propagation.PropagationComponent; + +/** Implementation of the {@link PropagationComponent}. */ +public class PropagationComponentImpl extends PropagationComponent { + private final BinaryFormat binaryFormat = new BinaryFormatImpl(); + + @Override + public BinaryFormat getBinaryFormat() { + return binaryFormat; + } +} -- cgit v1.2.3