From f3efd82cafc1a40214acc9b289ff75ccede262c7 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Fri, 19 Feb 2021 16:57:29 -0500 Subject: [PATCH 1/5] Reworking API for 1.0 and Java port --- .../java/com/comcast/money/api/EventInfo.java | 44 ++++++++ .../com/comcast/money/api/IdGenerator.java | 2 +- .../money/api/InstrumentationLibrary.java | 2 +- .../java/com/comcast/money/api/LinkInfo.java | 38 +++++++ .../main/java/com/comcast/money/api/Note.java | 2 +- .../main/java/com/comcast/money/api/Span.java | 78 ++++++++++---- .../com/comcast/money/api/SpanBuilder.java | 38 ++++--- .../java/com/comcast/money/api/SpanInfo.java | 77 +++----------- .../api/{MoneyTracer.java => Tracer.java} | 5 +- .../com/comcast/money/api/TracerProvider.java | 31 ++++++ .../com/comcast/money/core/CoreEvent.scala | 4 +- .../com/comcast/money/core/CoreLink.scala | 4 +- .../com/comcast/money/core/CoreSpan.scala | 90 ++++++---------- .../comcast/money/core/CoreSpanBuilder.scala | 39 +++---- .../comcast/money/core/CoreSpanFactory.scala | 11 +- .../com/comcast/money/core/CoreSpanInfo.scala | 10 +- .../com/comcast/money/core/Disabled.scala | 17 +-- .../money/core/MoneyTracerProvider.scala | 12 +-- .../scala/com/comcast/money/core/Tracer.scala | 11 +- .../comcast/money/core/UnrecordedSpan.scala | 3 - .../money/core/logging/MethodTracer.scala | 8 +- .../money/core/CoreSpanBuilderSpec.scala | 100 +++++------------- .../money/core/CoreSpanFactorySpec.scala | 4 +- .../com/comcast/money/core/CoreSpanSpec.scala | 28 +++-- .../core/handlers/SpanLogFormatterSpec.scala | 2 + .../money/core/handlers/TestData.scala | 2 + .../money/core/logging/MethodTracerSpec.scala | 22 ++-- .../money/otel/handlers/MoneyEvent.scala | 4 +- .../money/otel/handlers/MoneyLink.scala | 4 +- .../otel/handlers/MoneyReadableSpanData.scala | 6 +- .../money/otel/handlers/TestSpanInfo.java | 14 ++- .../money/otel/handlers/MoneyEventSpec.scala | 4 +- .../money/otel/handlers/MoneyLinkSpec.scala | 4 +- .../handlers/MoneyReadableSpanDataSpec.scala | 12 ++- .../MoneyClientHttpInterceptorSpec.java | 2 + .../spring/TracedMethodInterceptorSpec.java | 6 +- money-wire/src/main/avro/money.avsc | 5 + .../comcast/money/wire/SpanConverters.scala | 7 +- .../com/comcast/money/wire/TestSpanInfo.scala | 6 +- 39 files changed, 393 insertions(+), 365 deletions(-) create mode 100644 money-api/src/main/java/com/comcast/money/api/EventInfo.java create mode 100644 money-api/src/main/java/com/comcast/money/api/LinkInfo.java rename money-api/src/main/java/com/comcast/money/api/{MoneyTracer.java => Tracer.java} (85%) create mode 100644 money-api/src/main/java/com/comcast/money/api/TracerProvider.java diff --git a/money-api/src/main/java/com/comcast/money/api/EventInfo.java b/money-api/src/main/java/com/comcast/money/api/EventInfo.java new file mode 100644 index 00000000..da1f1f24 --- /dev/null +++ b/money-api/src/main/java/com/comcast/money/api/EventInfo.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012 Comcast Cable Communications Management, LLC + * + * 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 com.comcast.money.api; + +import io.opentelemetry.api.common.Attributes; + +/** + * An event that was recorded on a {@link Span}. + */ +public interface EventInfo { + /** + * @return the name of the event + */ + String name(); + + /** + * @return the attributes recorded on the event + */ + Attributes attributes(); + + /** + * @return the timestamp of when the event occurred in nanoseconds since the epoch + */ + long timestamp(); + + /** + * @return an exception if one was recorded with the event; otherwise {@code null} + */ + Throwable exception(); +} diff --git a/money-api/src/main/java/com/comcast/money/api/IdGenerator.java b/money-api/src/main/java/com/comcast/money/api/IdGenerator.java index 36c7691b..10beb79e 100644 --- a/money-api/src/main/java/com/comcast/money/api/IdGenerator.java +++ b/money-api/src/main/java/com/comcast/money/api/IdGenerator.java @@ -22,7 +22,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class IdGenerator { +public final class IdGenerator { private IdGenerator() { } public static final String INVALID_TRACE_ID = "00000000-0000-0000-0000-000000000000"; diff --git a/money-api/src/main/java/com/comcast/money/api/InstrumentationLibrary.java b/money-api/src/main/java/com/comcast/money/api/InstrumentationLibrary.java index 1847c95d..a97687c7 100644 --- a/money-api/src/main/java/com/comcast/money/api/InstrumentationLibrary.java +++ b/money-api/src/main/java/com/comcast/money/api/InstrumentationLibrary.java @@ -18,7 +18,7 @@ import java.util.Objects; -public class InstrumentationLibrary { +public final class InstrumentationLibrary { public static final InstrumentationLibrary UNKNOWN = new InstrumentationLibrary("unknown"); private final String name; diff --git a/money-api/src/main/java/com/comcast/money/api/LinkInfo.java b/money-api/src/main/java/com/comcast/money/api/LinkInfo.java new file mode 100644 index 00000000..207109a0 --- /dev/null +++ b/money-api/src/main/java/com/comcast/money/api/LinkInfo.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Comcast Cable Communications Management, LLC + * + * 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 com.comcast.money.api; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; + +/** + * A reference to another {@link Span} by span context. + *

+ * Can be used to associate multiple traces as a part of a batch operation. + */ +public interface LinkInfo { + /** + * @return the context of the linked span + */ + SpanContext spanContext(); + + /** + * @return the attributes associated with the link between the spans + */ + Attributes attributes(); +} diff --git a/money-api/src/main/java/com/comcast/money/api/Note.java b/money-api/src/main/java/com/comcast/money/api/Note.java index f357d45a..6412a0be 100644 --- a/money-api/src/main/java/com/comcast/money/api/Note.java +++ b/money-api/src/main/java/com/comcast/money/api/Note.java @@ -26,7 +26,7 @@ * * @param The type of Note. This is currently limited to Long, String, Boolean and Double */ -public class Note { +public final class Note { private final AttributeKey key; private final T value; diff --git a/money-api/src/main/java/com/comcast/money/api/Span.java b/money-api/src/main/java/com/comcast/money/api/Span.java index db35ff91..a1239e64 100644 --- a/money-api/src/main/java/com/comcast/money/api/Span.java +++ b/money-api/src/main/java/com/comcast/money/api/Span.java @@ -32,43 +32,50 @@ */ public interface Span extends io.opentelemetry.api.trace.Span, Scope { - /** - * Stops the span asserts a successful result - */ - void stop(); - - /** - * Ends a span, moving it to a Stopped state - * @param result The result of the span (success or failure) - */ - void stop(Boolean result); - @Override - Span setAttribute(String key, String value); + default Span setAttribute(String key, String value) { + return record(Note.of(key, value)); + } @Override - Span setAttribute(String key, long value); + default Span setAttribute(String key, long value) { + return record(Note.of(key, value)); + } @Override - Span setAttribute(String key, double value); + default Span setAttribute(String key, double value) { + return record(Note.of(key, value)); + } @Override - Span setAttribute(String key, boolean value); + default Span setAttribute(String key, boolean value) { + return record(Note.of(key, value)); + } @Override - Span setAttribute(AttributeKey key, T value); + default Span setAttribute(AttributeKey key, T value) { + return record(Note.of(key, value)); + } @Override - Span setAttribute(AttributeKey key, int value); + default Span setAttribute(AttributeKey key, int value) { + return setAttribute(key, (long) value); + } @Override - Span addEvent(String name); + default Span addEvent(String name) { + return addEvent(name, Attributes.empty()); + } @Override - Span addEvent(String name, long timestamp, TimeUnit unit); + default Span addEvent(String name, long timestamp, TimeUnit unit) { + return addEvent(name, Attributes.empty(), timestamp, unit); + } @Override - Span addEvent(String name, Instant timestamp); + default Span addEvent(String name, Instant timestamp) { + return addEvent(name, Attributes.empty(), timestamp); + } @Override Span addEvent(String name, Attributes attributes); @@ -77,16 +84,26 @@ public interface Span extends io.opentelemetry.api.trace.Span, Scope { Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit); @Override - Span addEvent(String name, Attributes attributes, Instant timestamp); + default Span addEvent(String name, Attributes attributes, Instant timestamp) { + if (timestamp != null) { + return addEvent(name, attributes, TimeUnit.SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano(), TimeUnit.NANOSECONDS); + } else { + return addEvent(name, attributes); + } + } @Override - Span setStatus(StatusCode canonicalCode); + default Span setStatus(StatusCode canonicalCode) { + return setStatus(canonicalCode, null); + } @Override Span setStatus(StatusCode canonicalCode, String description); @Override - Span recordException(Throwable exception); + default Span recordException(Throwable exception) { + return recordException(exception, Attributes.empty()); + } @Override Span recordException(Throwable exception, Attributes additionalAttributes); @@ -117,8 +134,23 @@ public interface Span extends io.opentelemetry.api.trace.Span, Scope { */ Span attachScope(Scope scope); + /** + * Ends the span + * @param result The result of the span (success or failure) + */ + default void end(boolean result) { + setStatus(result ? StatusCode.OK : StatusCode.ERROR).end(); + } + /** * @return The current state of the Span */ SpanInfo info(); + + /** + * @return The {@link SpanId} of the Span + */ + default SpanId id() { + return info().id(); + } } diff --git a/money-api/src/main/java/com/comcast/money/api/SpanBuilder.java b/money-api/src/main/java/com/comcast/money/api/SpanBuilder.java index 9a247310..8219d225 100644 --- a/money-api/src/main/java/com/comcast/money/api/SpanBuilder.java +++ b/money-api/src/main/java/com/comcast/money/api/SpanBuilder.java @@ -34,16 +34,6 @@ public interface SpanBuilder extends io.opentelemetry.api.trace.SpanBuilder { @Override SpanBuilder setParent(Context context); - /** - * Sets the parent span to the specified span - */ - SpanBuilder setParent(Span span); - - /** - * Sets the parent span to the specified span - */ - SpanBuilder setParent(Optional span); - /** * {@inheritDoc} */ @@ -59,7 +49,9 @@ public interface SpanBuilder extends io.opentelemetry.api.trace.SpanBuilder { * {@inheritDoc} */ @Override - SpanBuilder addLink(SpanContext spanContext); + default SpanBuilder addLink(SpanContext spanContext) { + return addLink(spanContext, Attributes.empty()); + } /** * {@inheritDoc} @@ -71,31 +63,41 @@ public interface SpanBuilder extends io.opentelemetry.api.trace.SpanBuilder { * {@inheritDoc} */ @Override - SpanBuilder setAttribute(String key, String value); + default SpanBuilder setAttribute(String key, String value) { + return record(Note.of(key, value)); + } /** * {@inheritDoc} */ @Override - SpanBuilder setAttribute(String key, long value); + default SpanBuilder setAttribute(String key, long value) { + return record(Note.of(key, value)); + } /** * {@inheritDoc} */ @Override - SpanBuilder setAttribute(String key, double value); + default SpanBuilder setAttribute(String key, double value) { + return record(Note.of(key, value)); + } /** * {@inheritDoc} */ @Override - SpanBuilder setAttribute(String key, boolean value); + default SpanBuilder setAttribute(String key, boolean value) { + return record(Note.of(key, value)); + } /** * {@inheritDoc} */ @Override - SpanBuilder setAttribute(AttributeKey key, T value); + default SpanBuilder setAttribute(AttributeKey key, T value) { + return record(Note.of(key, value)); + } /** * Records the note on the created span @@ -118,7 +120,9 @@ public interface SpanBuilder extends io.opentelemetry.api.trace.SpanBuilder { * {@inheritDoc} */ @Override - SpanBuilder setStartTimestamp(Instant startTimestamp); + default SpanBuilder setStartTimestamp(Instant startTimestamp) { + return setStartTimestamp(TimeUnit.SECONDS.toNanos(startTimestamp.getEpochSecond()) + startTimestamp.getNano(), TimeUnit.NANOSECONDS); + } /** * {@inheritDoc} diff --git a/money-api/src/main/java/com/comcast/money/api/SpanInfo.java b/money-api/src/main/java/com/comcast/money/api/SpanInfo.java index 896ce152..99faa8a1 100644 --- a/money-api/src/main/java/com/comcast/money/api/SpanInfo.java +++ b/money-api/src/main/java/com/comcast/money/api/SpanInfo.java @@ -21,11 +21,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.api.trace.Span; public interface SpanInfo { @@ -33,19 +30,21 @@ public interface SpanInfo { * @return a map of all of the notes that were recorded on the span. Implementers should enforce * that the map returned is a copy of the notes */ - Map> notes(); + default Map> notes() { + return Collections.emptyMap(); + } /** * @return a list of all of the events that were recorded on the span. */ - default List events() { + default List events() { return Collections.emptyList(); } /** * @return a list of the spans linked to the span */ - default List links() { + default List links() { return Collections.emptyList(); } @@ -84,7 +83,12 @@ default long endTimeMicros() { } /** - * @return the time in nanoseconds when this span was stopped. + * @return {@code true} if the span has ended + */ + boolean hasEnded(); + + /** + * @return the time in nanoseconds when this span ended. */ long endTimeNanos(); @@ -94,27 +98,19 @@ default long endTimeMicros() { StatusCode status(); /** - * @return the result of the span. Will return null if the span was never stopped. + * @return the result of the span. Will return {@code null} if the span has not ended.. */ default Boolean success() { - StatusCode status = status(); - if (status != null && endTimeNanos() > 0L) { - switch (status) { - case OK: - return true; - case ERROR: - return false; - } + if (hasEnded()) { + return status() != StatusCode.ERROR; } return null; } /** - * @return {@code true} if the span has been started but not yet stopped; otherwise, {@code false}. + * @return {@code true} if the span is recording notes or events */ - default boolean isRecording() { - return startTimeNanos() > 0L && endTimeNanos() <= 0L; - } + boolean isRecording(); /** * @return the kind of the span, e.g. if it wraps a server or client request. @@ -165,45 +161,4 @@ default long durationMicros() { */ String host(); - /** - * An event that was recorded on a {@link com.comcast.money.api.Span}. - */ - interface Event { - /** - * @return the name of the event - */ - String name(); - - /** - * @return the attributes recorded on the event - */ - Attributes attributes(); - - /** - * @return the timestamp of when the event occurred in nanoseconds since the epoch - */ - long timestamp(); - - /** - * @return an exception if one was recorded with the event; otherwise {@code null} - */ - Throwable exception(); - } - - /** - * A reference to another {@link Span} by span context. - * - * Can be used to associate multiple traces as a part of a batch operation. - */ - interface Link { - /** - * @return the context of the linked span - */ - SpanContext spanContext(); - - /** - * @return the attributes associated with the link between the spans - */ - Attributes attributes(); - } } diff --git a/money-api/src/main/java/com/comcast/money/api/MoneyTracer.java b/money-api/src/main/java/com/comcast/money/api/Tracer.java similarity index 85% rename from money-api/src/main/java/com/comcast/money/api/MoneyTracer.java rename to money-api/src/main/java/com/comcast/money/api/Tracer.java index c31fef4e..3c72d991 100644 --- a/money-api/src/main/java/com/comcast/money/api/MoneyTracer.java +++ b/money-api/src/main/java/com/comcast/money/api/Tracer.java @@ -16,13 +16,10 @@ package com.comcast.money.api; -import io.opentelemetry.api.trace.SpanBuilder; -import io.opentelemetry.api.trace.Tracer; - /** * OpenTelemetry compatible API to be used for tracing */ -public interface MoneyTracer extends Tracer { +public interface Tracer extends io.opentelemetry.api.trace.Tracer { /** * {@inheritDoc} diff --git a/money-api/src/main/java/com/comcast/money/api/TracerProvider.java b/money-api/src/main/java/com/comcast/money/api/TracerProvider.java new file mode 100644 index 00000000..f5547956 --- /dev/null +++ b/money-api/src/main/java/com/comcast/money/api/TracerProvider.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012 Comcast Cable Communications Management, LLC + * + * 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 com.comcast.money.api; + +public interface TracerProvider extends io.opentelemetry.api.trace.TracerProvider { + @Override + default Tracer get(String instrumentationName) { + return get(new InstrumentationLibrary(instrumentationName, null)); + } + + @Override + default Tracer get(String instrumentationName, String instrumentationVersion) { + return get(new InstrumentationLibrary(instrumentationName, instrumentationVersion)); + } + + Tracer get(InstrumentationLibrary instrumentationLibrary); +} diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala b/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala index d714fa4a..a67cb0b4 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala @@ -17,7 +17,7 @@ package com.comcast.money.core import java.io.{ PrintWriter, StringWriter } -import com.comcast.money.api.SpanInfo +import com.comcast.money.api.{ EventInfo, SpanInfo } import io.opentelemetry.api.common.Attributes import io.opentelemetry.semconv.trace.attributes.SemanticAttributes @@ -25,7 +25,7 @@ private[core] case class CoreEvent( name: String, eventAttributes: Attributes, timestamp: Long, - exception: Throwable) extends SpanInfo.Event { + exception: Throwable) extends EventInfo { lazy val attributes: Attributes = initializeAttributes() diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala b/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala index 4a9d50fa..ec715e1c 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala @@ -16,10 +16,10 @@ package com.comcast.money.core -import com.comcast.money.api.SpanInfo +import com.comcast.money.api.{ LinkInfo, SpanInfo } import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.SpanContext private[core] final case class CoreLink( spanContext: SpanContext, - attributes: Attributes = Attributes.empty()) extends SpanInfo.Link + attributes: Attributes = Attributes.empty()) extends LinkInfo diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala index bf11eef4..b598998b 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala @@ -16,17 +16,16 @@ package com.comcast.money.core -import java.time.Instant -import java.util.concurrent.TimeUnit import com.comcast.money.api._ - -import scala.collection.JavaConverters._ -import scala.collection.concurrent.TrieMap -import io.opentelemetry.api.trace.{ SpanContext, SpanKind, StatusCode, Span => OtelSpan } -import io.opentelemetry.api.common.{ AttributeKey, Attributes } +import io.opentelemetry.api.common.Attributes +import io.opentelemetry.api.trace.{ SpanContext, SpanKind, StatusCode } import io.opentelemetry.context.Scope import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean +import scala.collection.JavaConverters._ +import scala.collection.concurrent.TrieMap import scala.collection.mutable.ListBuffer /** @@ -37,15 +36,17 @@ import scala.collection.mutable.ListBuffer * @param handler The [[SpanHandler]] responsible for processing the span once it is stopped */ private[core] case class CoreSpan( - id: SpanId, + override val id: SpanId, var name: String, kind: SpanKind = SpanKind.INTERNAL, - links: List[SpanInfo.Link] = Nil, + links: List[LinkInfo] = Nil, startTimeNanos: Long = SystemClock.now, library: InstrumentationLibrary = Money.InstrumentationLibrary, clock: Clock = SystemClock, handler: SpanHandler = DisabledSpanHandler) extends Span { + private val ended: AtomicBoolean = new AtomicBoolean() + private var endTimeNanos: Long = 0 private var status: StatusCode = StatusCode.UNSET private var description: String = _ @@ -53,38 +54,9 @@ private[core] case class CoreSpan( // use concurrent maps private val timers = new TrieMap[String, Long]() private val noted = new TrieMap[String, Note[_]]() - private val events = new ListBuffer[SpanInfo.Event]() + private val events = new ListBuffer[EventInfo]() private var scopes: List[Scope] = Nil - override def stop(): Unit = stop(clock.now, StatusCode.UNSET) - - override def stop(result: java.lang.Boolean): Unit = - if (result == null) { - stop(clock.now, StatusCode.UNSET) - } else { - stop(clock.now, if (result) StatusCode.OK else StatusCode.ERROR) - } - - private def stop(endTimeNanos: Long, status: StatusCode): Unit = { - this.endTimeNanos = endTimeNanos - - // process any hanging timers - val openTimers = timers.keys - openTimers.foreach(stopTimer) - - scopes.foreach { _.close() } - scopes = Nil - - this.status = (this.status, status) match { - case (StatusCode.UNSET, StatusCode.UNSET) => StatusCode.OK - case (StatusCode.UNSET, other) => other - case (other, StatusCode.UNSET) => other - case (_, other) => other - } - - handler.handle(info()) - } - override def stopTimer(timerKey: String): Unit = timers.remove(timerKey) foreach { timerStartInstant => @@ -114,6 +86,7 @@ private[core] case class CoreSpan( library = library, startTimeNanos = startTimeNanos, endTimeNanos = endTimeNanos, + hasEnded = ended.get(), durationNanos = calculateDurationNanos, status = status, description = description, @@ -121,28 +94,12 @@ private[core] case class CoreSpan( events = events.asJava, links = links.asJava) - override def close(): Unit = stop() - - override def setAttribute(attributeName: String, value: String): Span = record(Note.of(attributeName, value)) - override def setAttribute(attributeName: String, value: scala.Long): Span = record(Note.of(attributeName, value)) - override def setAttribute(attributeName: String, value: Double): Span = record(Note.of(attributeName, value)) - override def setAttribute(attributeName: String, value: Boolean): Span = record(Note.of(attributeName, value)) - override def setAttribute[T](key: AttributeKey[T], value: T): Span = record(Note.of(key, value)) + override def close(): Unit = end() override def addEvent(eventName: String): Span = addEventInternal(eventName, Attributes.empty(), clock.now) - override def addEvent(eventName: String, timestamp: Instant): Span = addEventInternal(eventName, Attributes.empty(), timestamp) - override def addEvent(eventName: String, timestamp: scala.Long, unit: TimeUnit): Span = addEventInternal(eventName, Attributes.empty(), unit.toNanos(timestamp)) override def addEvent(eventName: String, eventAttributes: Attributes): Span = addEventInternal(eventName, eventAttributes, clock.now) - override def addEvent(eventName: String, eventAttributes: Attributes, timestamp: Instant): Span = addEventInternal(eventName, eventAttributes, timestamp) override def addEvent(eventName: String, eventAttributes: Attributes, timestamp: scala.Long, unit: TimeUnit): Span = addEventInternal(eventName, eventAttributes, unit.toNanos(timestamp)) - private def addEventInternal(eventName: String, eventAttributes: Attributes, timestamp: Instant): Span = { - val timestampNanos = if (timestamp != null) - TimeUnit.SECONDS.toNanos(timestamp.getEpochSecond) + timestamp.getNano - else clock.now - addEventInternal(eventName, eventAttributes, timestampNanos) - } - private def addEventInternal(eventName: String, eventAttributes: Attributes, timestampNanos: scala.Long, exception: Throwable = null): Span = { events += CoreEvent(eventName, eventAttributes, timestampNanos, exception) this @@ -165,12 +122,27 @@ private[core] case class CoreSpan( this } - override def end(): Unit = stop() - override def `end`(endTimeStamp: Long, unit: TimeUnit): Unit = stop(unit.toNanos(endTimeStamp), StatusCode.UNSET) + override def end(): Unit = end(clock.now, TimeUnit.NANOSECONDS) + + override def end(timestamp: Long, unit: TimeUnit): Unit = + if (ended.compareAndSet(false, true)) { + this.endTimeNanos = unit.toNanos(timestamp) + + // process any hanging timers + val openTimers = timers.keys + openTimers.foreach(stopTimer) + + scopes.foreach { + _.close() + } + scopes = Nil + + handler.handle(info()) + } override def getSpanContext: SpanContext = id.toSpanContext - override def isRecording: Boolean = startTimeNanos > 0 && endTimeNanos <= 0 + override def isRecording: Boolean = true private def calculateDurationNanos: Long = if (endTimeNanos <= 0L && startTimeNanos <= 0L) diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala index 0935bc3a..94e5ab48 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala @@ -16,20 +16,21 @@ package com.comcast.money.core -import java.time.Instant -import java.util.concurrent.TimeUnit -import com.comcast.money.api.{ InstrumentationLibrary, Note, Span, SpanBuilder, SpanHandler, SpanId, SpanInfo } +import com.comcast.money.api._ +import com.comcast.money.core.internal.{ SpanContext, SpanLocal } import com.comcast.money.core.samplers.{ DropResult, RecordResult, Sampler } import io.opentelemetry.api.common.{ AttributeKey, Attributes } +import io.opentelemetry.api.trace.{ SpanKind, TraceFlags, SpanContext => OtelSpanContext } import io.opentelemetry.context.Context -import io.opentelemetry.api.trace.{ SpanContext, SpanKind, TraceFlags, Span => OtelSpan } -import java.util.Optional +import java.time.Instant +import java.util.concurrent.TimeUnit import scala.collection.JavaConverters._ private[core] class CoreSpanBuilder( spanId: Option[SpanId], - var parentSpan: Option[Span], + var parentContext: Context = Context.current(), + spanContext: SpanContext = SpanLocal, spanName: String, clock: Clock, handler: SpanHandler, @@ -40,25 +41,10 @@ private[core] class CoreSpanBuilder( var spanKind: SpanKind = SpanKind.INTERNAL var startTimeNanos: Long = 0L var notes: List[Note[_]] = List() - var links: List[SpanInfo.Link] = List() + var links: List[LinkInfo] = List() override def setParent(context: Context): SpanBuilder = { - parentSpan = Option(context) - .flatMap { ctx => Option(OtelSpan.fromContextOrNull(ctx)) } - .flatMap { - case span: Span => Some(span) - case _ => None - } - this - } - - override def setParent(span: Span): SpanBuilder = { - parentSpan = Option(span) - this - } - - override def setParent(span: Optional[Span]): SpanBuilder = { - parentSpan = if (span.isPresent) Some(span.get) else None + parentContext = context this } @@ -68,13 +54,13 @@ private[core] class CoreSpanBuilder( } override def setNoParent(): SpanBuilder = { - parentSpan = None + parentContext = Context.root this } - override def addLink(spanContext: SpanContext): SpanBuilder = addLink(spanContext, Attributes.empty) + override def addLink(spanContext: OtelSpanContext): SpanBuilder = addLink(spanContext, Attributes.empty) - override def addLink(spanContext: SpanContext, attributes: Attributes): SpanBuilder = { + override def addLink(spanContext: OtelSpanContext, attributes: Attributes): SpanBuilder = { links = CoreLink(spanContext, attributes) :: links this } @@ -122,6 +108,7 @@ private[core] class CoreSpanBuilder( handler = handler) override def startSpan(): Span = { + val parentSpan = spanContext.fromContext(parentContext) val parentSpanId = parentSpan.map { _.info.id } val spanId = (this.spanId, parentSpanId) match { diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpanFactory.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpanFactory.scala index 2160f516..b63601ed 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreSpanFactory.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpanFactory.scala @@ -20,6 +20,7 @@ import com.comcast.money.api.{ InstrumentationLibrary, Span, SpanBuilder, SpanFa import com.comcast.money.core.formatters.Formatter import com.comcast.money.core.internal.SpanContext import com.comcast.money.core.samplers.Sampler +import io.opentelemetry.context.Context private[core] final case class CoreSpanFactory( spanContext: SpanContext, @@ -45,13 +46,19 @@ private[core] final case class CoreSpanFactory( .setSticky(true) .startSpan() - private[core] def spanBuilder(spanName: String, spanId: Option[SpanId] = None, parentSpan: Option[Span] = spanContext.current): SpanBuilder = + private[core] def spanBuilder(spanName: String, spanId: Option[SpanId] = None, parentSpan: Option[Span] = spanContext.current): SpanBuilder = { + val parentContext = parentSpan match { + case Some(s) => Context.current().`with`(s) + case None => Context.root() + } new CoreSpanBuilder( spanId = spanId, - parentSpan = parentSpan, + parentContext = parentContext, + spanContext = spanContext, spanName = spanName, clock = clock, handler = handler, sampler = sampler, library = library) + } } diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala index 1a4759a2..19a2ce7f 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala @@ -17,21 +17,23 @@ package com.comcast.money.core import java.util.Collections -import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo } +import com.comcast.money.api.{ EventInfo, InstrumentationLibrary, LinkInfo, Note, SpanId, SpanInfo } import io.opentelemetry.api.trace.{ Span, SpanKind, StatusCode } private[core] case class CoreSpanInfo( id: SpanId, name: String, kind: SpanKind = SpanKind.INTERNAL, + override val isRecording: Boolean = true, startTimeNanos: Long = 0L, endTimeNanos: Long = 0L, + override val hasEnded: Boolean = false, durationNanos: Long = 0L, status: StatusCode = StatusCode.UNSET, description: String = "", - notes: java.util.Map[String, Note[_]] = Collections.emptyMap(), - override val events: java.util.List[SpanInfo.Event] = Collections.emptyList(), - override val links: java.util.List[SpanInfo.Link] = Collections.emptyList(), + override val notes: java.util.Map[String, Note[_]] = Collections.emptyMap(), + override val events: java.util.List[EventInfo] = Collections.emptyList(), + override val links: java.util.List[LinkInfo] = Collections.emptyList(), library: InstrumentationLibrary = Money.InstrumentationLibrary, appName: String = Money.Environment.applicationName, host: String = Money.Environment.hostName) extends SpanInfo \ No newline at end of file diff --git a/money-core/src/main/scala/com/comcast/money/core/Disabled.scala b/money-core/src/main/scala/com/comcast/money/core/Disabled.scala index a774cfdb..50e64b60 100644 --- a/money-core/src/main/scala/com/comcast/money/core/Disabled.scala +++ b/money-core/src/main/scala/com/comcast/money/core/Disabled.scala @@ -16,16 +16,15 @@ package com.comcast.money.core -import java.time.Instant -import java.util.concurrent.TimeUnit import com.comcast.money.api._ import com.comcast.money.core.context.ContextStorageFilter +import com.comcast.money.core.formatters.Formatter import io.opentelemetry.api.common.{ AttributeKey, Attributes } +import io.opentelemetry.api.trace.{ SpanContext, SpanKind, StatusCode } import io.opentelemetry.context.{ Context, ContextStorage, Scope } -import io.opentelemetry.api.trace.{ SpanContext, SpanKind, StatusCode, Span => OtelSpan } -import com.comcast.money.core.formatters.Formatter -import java.util.Optional +import java.time.Instant +import java.util.concurrent.TimeUnit // $COVERAGE-OFF$ object DisabledSpanHandler extends SpanHandler { @@ -99,10 +98,6 @@ object DisabledSpanFactory extends SpanFactory { object DisabledSpanBuilder extends SpanBuilder { override def setParent(context: Context): SpanBuilder = this - override def setParent(span: Span): SpanBuilder = this - - override def setParent(span: Optional[Span]): SpanBuilder = this - override def setSticky(sticky: Boolean): SpanBuilder = this override def setNoParent(): SpanBuilder = this @@ -134,10 +129,6 @@ object DisabledSpanBuilder extends SpanBuilder { object DisabledSpan extends Span { - override def stop(): Unit = () - - override def stop(result: java.lang.Boolean): Unit = () - override def stopTimer(timerKey: String): Unit = () override def record(note: Note[_]): Span = this diff --git a/money-core/src/main/scala/com/comcast/money/core/MoneyTracerProvider.scala b/money-core/src/main/scala/com/comcast/money/core/MoneyTracerProvider.scala index e8c69e1f..544052c8 100644 --- a/money-core/src/main/scala/com/comcast/money/core/MoneyTracerProvider.scala +++ b/money-core/src/main/scala/com/comcast/money/core/MoneyTracerProvider.scala @@ -16,9 +16,7 @@ package com.comcast.money.core -import com.comcast.money.api.{ InstrumentationLibrary, SpanFactory } -import io.opentelemetry.api.trace -import io.opentelemetry.api.trace.TracerProvider +import com.comcast.money.api.{ InstrumentationLibrary, SpanFactory, Tracer => ApiTracer, TracerProvider } import scala.collection.concurrent.TrieMap @@ -26,13 +24,11 @@ final case class MoneyTracerProvider(tracer: Tracer) extends TracerProvider { private val tracers = new TrieMap[InstrumentationLibrary, Tracer]() - override def get(instrumentationName: String): trace.Tracer = get(instrumentationName, null) - override def get(instrumentationName: String, instrumentationVersion: String): trace.Tracer = { - val library = new InstrumentationLibrary(instrumentationName, instrumentationVersion) - tracers.getOrElseUpdate(library, { + override def get(instrumentationLibrary: InstrumentationLibrary): ApiTracer = { + tracers.getOrElseUpdate(instrumentationLibrary, { tracer.spanFactory match { case csf: CoreSpanFactory => new Tracer { - override val spanFactory: SpanFactory = csf.copy(library = library) + override val spanFactory: SpanFactory = csf.copy(library = instrumentationLibrary) } case _ => tracer } diff --git a/money-core/src/main/scala/com/comcast/money/core/Tracer.scala b/money-core/src/main/scala/com/comcast/money/core/Tracer.scala index 7b11abcd..6cb80aff 100644 --- a/money-core/src/main/scala/com/comcast/money/core/Tracer.scala +++ b/money-core/src/main/scala/com/comcast/money/core/Tracer.scala @@ -16,17 +16,18 @@ package com.comcast.money.core -import java.io.Closeable - -import com.comcast.money.api.{ MoneyTracer, Note, Span, SpanBuilder, SpanFactory } +import com.comcast.money.api +import com.comcast.money.api.{ Note, Span, SpanBuilder, SpanFactory } import com.comcast.money.core.internal.{ SpanContext, SpanLocal } +import io.opentelemetry.api.trace.StatusCode import io.opentelemetry.context.Scope -import io.opentelemetry.api.trace.{ StatusCode, Span => OtelSpan } + +import java.io.Closeable /** * Primary API to be used for tracing */ -trait Tracer extends MoneyTracer with Closeable { +trait Tracer extends api.Tracer with Closeable { val spanFactory: SpanFactory diff --git a/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala b/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala index 6cf849ae..5fd17049 100644 --- a/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala +++ b/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala @@ -16,7 +16,6 @@ package com.comcast.money.core -import java.lang import java.time.Instant import java.util.concurrent.TimeUnit @@ -46,8 +45,6 @@ private[core] final case class UnrecordedSpan( override def isRecording: Boolean = false // $COVERAGE-OFF$ - override def stop(): Unit = close() - override def stop(result: lang.Boolean): Unit = close() override def `end`(): Unit = close() override def `end`(endTimeStamp: Long, unit: TimeUnit): Unit = close() diff --git a/money-core/src/main/scala/com/comcast/money/core/logging/MethodTracer.scala b/money-core/src/main/scala/com/comcast/money/core/logging/MethodTracer.scala index 67982741..2a6938c1 100644 --- a/money-core/src/main/scala/com/comcast/money/core/logging/MethodTracer.scala +++ b/money-core/src/main/scala/com/comcast/money/core/logging/MethodTracer.scala @@ -54,16 +54,16 @@ trait MethodTracer extends Reflections with TraceLogging { case Some(future) => future case None => - span.stop(true) + span.end(true) result } case Success(result) => - span.stop(true) + span.end(true) result case Failure(exception) => logException(exception) span.recordException(exception) - span.stop(exceptionMatches(exception, annotation.ignoredExceptions())) + span.end(exceptionMatches(exception, annotation.ignoredExceptions())) throw exception } } finally { @@ -119,7 +119,7 @@ trait MethodTracer extends Reflections with TraceLogging { } // stop the captured span with the success/failure flag - span.stop(result) + span.end(result) } finally { // reset the current thread context scope.close() diff --git a/money-core/src/test/scala/com/comcast/money/core/CoreSpanBuilderSpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanBuilderSpec.scala index b53a1a61..ecb0dace 100644 --- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanBuilderSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/CoreSpanBuilderSpec.scala @@ -32,6 +32,7 @@ import org.scalatestplus.mockito.MockitoSugar import scala.collection.JavaConverters._ import InstantImplicits._ +import com.comcast.money.core.internal.SpanLocal import java.util.Optional @@ -46,7 +47,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val span = mock[Span] when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) { + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { name shouldBe "test" span @@ -65,7 +66,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val span = mock[Span] when(sampler.shouldSample(argEq(spanId), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(Some(spanId), None, "test", clock, handler, sampler, library) { + val underTest = new CoreSpanBuilder(Some(spanId), Context.root(), SpanLocal, "test", clock, handler, sampler, library) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { id shouldBe spanId name shouldBe "test" @@ -85,11 +86,13 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val parentSpanInfo = mock[SpanInfo] val parentSpan = mock[Span] val span = mock[Span] + when(parentSpan.storeInContext(any())).thenCallRealMethod() when(parentSpan.info).thenReturn(parentSpanInfo) when(parentSpanInfo.id).thenReturn(parentSpanId) when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, Some(parentSpan), "test", clock, handler, sampler, library) { + val parentContext = Context.root.`with`(parentSpan) + val underTest = new CoreSpanBuilder(None, parentContext, SpanLocal, "test", clock, handler, sampler, library) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { id.traceId shouldBe parentSpanId.traceId id.parentId shouldBe parentSpanId.selfId @@ -111,10 +114,11 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val parentSpan = mock[Span] val span = mock[Span] when(parentSpan.info).thenReturn(parentSpanInfo) + when(parentSpan.storeInContext(any())).thenCallRealMethod() when(parentSpanInfo.id).thenReturn(parentSpanId) when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) { + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { id.traceId shouldBe parentSpanId.traceId id.parentId shouldBe parentSpanId.selfId @@ -124,65 +128,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { } val result = underTest - .setParent(parentSpan) - .startSpan() - result shouldBe span - } - - "create a child span with an explicit parent span wrapped in an Option" in { - val handler = mock[SpanHandler] - val sampler = mock[Sampler] - - val parentSpanId = SpanId.createNew() - val parentSpanInfo = mock[SpanInfo] - val parentSpan = mock[Span] - val span = mock[Span] - when(parentSpan.info).thenReturn(parentSpanInfo) - when(parentSpanInfo.id).thenReturn(parentSpanId) - when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) { - override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { - id.traceId shouldBe parentSpanId.traceId - id.parentId shouldBe parentSpanId.selfId - name shouldBe "test" - span - } - } - - val result = underTest - .setParent(Optional.of(parentSpan)) - .startSpan() - - result shouldBe span - } - - "create a child span with an explicit parent span wrapped in an Context" in { - val handler = mock[SpanHandler] - val sampler = mock[Sampler] - - val parentSpanId = SpanId.createNew() - val parentSpanInfo = mock[SpanInfo] - val parentSpan = mock[Span] - val span = mock[Span] - when(parentSpan.info).thenReturn(parentSpanInfo) - when(parentSpanInfo.id).thenReturn(parentSpanId) - when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) { - override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { - id.traceId shouldBe parentSpanId.traceId - id.parentId shouldBe parentSpanId.selfId - name shouldBe "test" - span - } - } - - when(parentSpan.storeInContext(Context.root())).thenCallRealMethod() - val context = Context.root.`with`(parentSpan) - - val result = underTest - .setParent(context) + .setParent(Context.root().`with`(parentSpan)) .startSpan() result shouldBe span } @@ -194,7 +140,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val parentSpan = mock[Span] when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, Some(parentSpan), "test", clock, handler, sampler, library) { + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { name shouldBe "test" span @@ -213,7 +159,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val span = mock[Span] when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) { + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span } @@ -253,6 +199,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val parentSpan = mock[Span] val span = mock[Span] when(parentSpan.info).thenReturn(parentSpanInfo) + when(parentSpan.storeInContext(any())).thenCallRealMethod() when(parentSpanInfo.id).thenReturn(parentSpanId) val notes: Map[String, Note[_]] = Map( "test" -> Note.of("some", "note", true), @@ -260,7 +207,8 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { when(parentSpanInfo.notes).thenReturn(notes.asJava) when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, Some(parentSpan), "test", clock, handler, sampler, library) { + val parentContext = Context.root.`with`(parentSpan) + val underTest = new CoreSpanBuilder(None, parentContext, SpanLocal, "test", clock, handler, sampler, library) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span } @@ -284,6 +232,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val parentSpanInfo = mock[SpanInfo] val parentSpan = mock[Span] val span = mock[Span] + when(parentSpan.storeInContext(any())).thenCallRealMethod() when(parentSpan.info).thenReturn(parentSpanInfo) when(parentSpanInfo.id).thenReturn(parentSpanId) val notes: Map[String, Note[_]] = Map( @@ -292,7 +241,8 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { when(parentSpanInfo.notes).thenReturn(notes.asJava) when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, Some(parentSpan), "test", clock, handler, sampler, library) { + val parentContext = Context.root.`with`(parentSpan) + val underTest = new CoreSpanBuilder(None, parentContext, SpanLocal, "test", clock, handler, sampler, library) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span } @@ -309,7 +259,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val sampler = mock[Sampler] when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) val result = underTest .setSpanKind(SpanKind.SERVER) @@ -323,7 +273,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val sampler = mock[Sampler] when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.Drop) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) val result = underTest .setSpanKind(SpanKind.SERVER) @@ -337,7 +287,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val sampler = mock[Sampler] when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) val result = underTest.startSpan() @@ -349,7 +299,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val sampler = mock[Sampler] when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.Record) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) val result = underTest.startSpan() @@ -364,7 +314,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { when(sampler.shouldSample(any(), argEq(None), argEq("test"))) .thenReturn(SamplerResult.RecordAndSample.withNote(Note.of("sampler", "note"))) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) { + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span } @@ -383,7 +333,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val sampler = mock[Sampler] when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) val result = underTest .setStartTimestamp(12345789L, TimeUnit.NANOSECONDS) @@ -397,7 +347,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val sampler = mock[Sampler] when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) val instant = Instant.now val result = underTest @@ -413,7 +363,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample) - val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) + val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) val linkedContext = SpanContext.create(IdGenerator.generateRandomTraceIdAsHex(), IdGenerator.generateRandomIdAsHex(), TraceFlags.getSampled, TraceState.getDefault) val attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar") diff --git a/money-core/src/test/scala/com/comcast/money/core/CoreSpanFactorySpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanFactorySpec.scala index e20bc006..fe70ff68 100644 --- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanFactorySpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/CoreSpanFactorySpec.scala @@ -19,7 +19,7 @@ package com.comcast.money.core import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanHandler, SpanId } import com.comcast.money.core.formatters.MoneyTraceFormatter import com.comcast.money.core.handlers.TestData -import com.comcast.money.core.internal.SpanContext +import com.comcast.money.core.internal.{ SpanContext, SpanLocal } import com.comcast.money.core.samplers.{ AlwaysOffSampler, AlwaysOnSampler, RecordResult, Sampler, SamplerResult } import io.opentelemetry.api.trace.TraceFlags import org.scalatest.matchers.should.Matchers @@ -28,7 +28,7 @@ import org.scalatestplus.mockito.MockitoSugar class CoreSpanFactorySpec extends AnyWordSpec with Matchers with MockitoSugar with TestData { - val context = mock[SpanContext] + val context = SpanLocal val handler = mock[SpanHandler] val formatter = MoneyTraceFormatter val sampler = AlwaysOnSampler diff --git a/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala index 6c7a41dd..016f7c8a 100644 --- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala @@ -239,7 +239,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS underTest.info.success shouldBe (null) - underTest.stop() + underTest.end() underTest.info.success shouldBe (true) } @@ -251,7 +251,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS underTest.info.success shouldBe (null) - underTest.stop() + underTest.end() underTest.info.success shouldBe (false) } @@ -263,7 +263,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS underTest.info.success shouldBe (null) - underTest.stop(false) + underTest.end(false) underTest.info.success shouldBe (false) } @@ -275,7 +275,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS underTest.info.success shouldBe (null) - underTest.stop(true) + underTest.end(true) underTest.info.success shouldBe (true) } @@ -305,10 +305,6 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS val underTest = CoreSpan(SpanId.createNew(), "test") underTest.isRecording shouldBe true - - underTest.stop() - - underTest.isRecording shouldBe false } "gets SpanContext" in { @@ -326,7 +322,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS when(clock.now).thenReturn(3000000000L) val underTest = CoreSpan(SpanId.createNew(), "test", startTimeNanos = 1000000000, clock = clock) - underTest.stop(true) + underTest.end(true) val state = underTest.info @@ -339,11 +335,11 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS "invoke the span handler when stopped" in { val handler = mock[SpanHandler] - val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo]) + val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo]) val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) underTest.record(testLongNote) - underTest.stop(true) + underTest.end(true) verify(handler).handle(handleCaptor.capture()) @@ -359,7 +355,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS "invoke the span handler when closed" in { val handler = mock[SpanHandler] - val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo]) + val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo]) val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) underTest.record(testLongNote) @@ -386,7 +382,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS underTest.attachScope(scope1) underTest.attachScope(scope2) - underTest.stop() + underTest.end() verify(scope1).close() verify(scope2).close() @@ -394,7 +390,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS "invoke the span handler when ended" in { val handler = mock[SpanHandler] - val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo]) + val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo]) val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) underTest.record(testLongNote) @@ -414,7 +410,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS "invoke the span handler when ended with timestamp" in { val handler = mock[SpanHandler] - val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo]) + val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo]) val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) underTest.record(testLongNote) @@ -434,7 +430,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS "invoke the span handler when ended with instant" in { val handler = mock[SpanHandler] - val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo]) + val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo]) val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) val instant = Instant.now diff --git a/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala b/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala index b425dbae..547f7b85 100644 --- a/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala @@ -43,6 +43,7 @@ class SpanLogFormatterSpec extends AnyWordSpec with Matchers { startTimeNanos = 1000000L, endTimeNanos = 26000000L, durationNanos = 35000000L, + hasEnded = true, name = "key", appName = "unknown", host = "host", @@ -54,6 +55,7 @@ class SpanLogFormatterSpec extends AnyWordSpec with Matchers { startTimeNanos = 1000000L, endTimeNanos = 26000000L, durationNanos = 35000000L, + hasEnded = true, name = "key", appName = "unknown", host = "host", diff --git a/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala b/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala index ed6338dd..8bb4780d 100644 --- a/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala +++ b/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala @@ -47,6 +47,7 @@ trait TestData { startTimeNanos = clock.now, endTimeNanos = clock.now, durationNanos = 123456000L, + hasEnded = true, status = StatusCode.OK, name = "test-span", appName = "test", @@ -65,6 +66,7 @@ trait TestData { startTimeNanos = 100000000L, endTimeNanos = 300000000L, durationNanos = 200000L, + hasEnded = true, status = StatusCode.OK, name = "test-span", appName = "test", diff --git a/money-core/src/test/scala/com/comcast/money/core/logging/MethodTracerSpec.scala b/money-core/src/test/scala/com/comcast/money/core/logging/MethodTracerSpec.scala index edc5739a..c72e395f 100644 --- a/money-core/src/test/scala/com/comcast/money/core/logging/MethodTracerSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/logging/MethodTracerSpec.scala @@ -80,7 +80,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with verify(mockSpanBuilder).startSpan() verify(mockSpan).storeInContext(any()) verify(mockContext).makeCurrent() - verify(mockSpan).stop(true) + verify(mockSpan).end(true) verify(mockScope).close() result shouldBe "result" @@ -107,7 +107,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with verify(mockSpanBuilder).startSpan() verify(mockSpan).storeInContext(any()) verify(mockContext).makeCurrent() - verify(mockSpan).stop(false) + verify(mockSpan).end(false) verify(mockScope).close() } "that throws an ignored exception" in { @@ -131,7 +131,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with verify(mockSpanBuilder).startSpan() verify(mockSpan).storeInContext(any()) verify(mockContext).makeCurrent() - verify(mockSpan).stop(true) + verify(mockSpan).end(true) verify(mockScope).close() } } @@ -157,14 +157,14 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with verify(mockSpanBuilder).startSpan() verify(mockSpan).storeInContext(any()) verify(mockContext).makeCurrent() - verify(mockSpan, never()).stop(any()) + verify(mockSpan, never()).end(any()) verify(mockScope).close() result shouldBe "result2" handler.callback(Success("result3")) - verify(mockSpan).stop(true) + verify(mockSpan).end(true) } "that complete exceptionally" in { val method = tracedAsyncMethod @@ -187,14 +187,14 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with verify(mockSpanBuilder).startSpan() verify(mockSpan).storeInContext(any()) verify(mockContext).makeCurrent() - verify(mockSpan, never()).stop(any()) + verify(mockSpan, never()).end(any()) verify(mockScope).close() result shouldBe "result2" handler.callback(Failure(new Exception)) - verify(mockSpan).stop(false) + verify(mockSpan).end(false) } "that complete exceptionally with ignored exception" in { val method = tracedAsyncMethodWithIgnoredExceptions @@ -217,14 +217,14 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with verify(mockSpanBuilder).startSpan() verify(mockSpan).storeInContext(any()) verify(mockContext).makeCurrent() - verify(mockSpan, never()).stop(any()) + verify(mockSpan, never()).end(any()) verify(mockScope).close() result shouldBe "result2" handler.callback(Failure(new IllegalArgumentException())) - verify(mockSpan).stop(true) + verify(mockSpan).end(true) } "with unhandled return value" in { val method = tracedAsyncMethod @@ -246,7 +246,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with verify(mockSpanBuilder).startSpan() verify(mockSpan).storeInContext(any()) verify(mockContext).makeCurrent() - verify(mockSpan).stop(true) + verify(mockSpan).end(true) verify(mockScope).close() result shouldBe "result" @@ -273,7 +273,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with verify(mockSpanBuilder).startSpan() verify(mockSpan).storeInContext(any()) verify(mockContext).makeCurrent() - verify(mockSpan).stop(false) + verify(mockSpan).end(false) verify(mockScope).close() } } diff --git a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala index 8577bc4f..b83dc969 100644 --- a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala +++ b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala @@ -16,11 +16,11 @@ package com.comcast.money.otel.handlers -import com.comcast.money.api.SpanInfo +import com.comcast.money.api.{ EventInfo, SpanInfo } import io.opentelemetry.api.common.Attributes import io.opentelemetry.sdk.trace.data.EventData -private[otel] case class MoneyEvent(event: SpanInfo.Event) extends EventData { +private[otel] case class MoneyEvent(event: EventInfo) extends EventData { override def getName: String = event.name override def getAttributes: Attributes = event.attributes override def getEpochNanos: Long = event.timestamp diff --git a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyLink.scala b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyLink.scala index feae7269..0e1d1a8f 100644 --- a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyLink.scala +++ b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyLink.scala @@ -16,12 +16,12 @@ package com.comcast.money.otel.handlers -import com.comcast.money.api.SpanInfo +import com.comcast.money.api.{ LinkInfo, SpanInfo } import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.SpanContext import io.opentelemetry.sdk.trace.data.LinkData -private[otel] case class MoneyLink(link: SpanInfo.Link) extends LinkData { +private[otel] case class MoneyLink(link: LinkInfo) extends LinkData { override def getSpanContext: SpanContext = link.spanContext override def getAttributes: Attributes = link.attributes override def getTotalAttributeCount: Int = link.attributes.size diff --git a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyReadableSpanData.scala b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyReadableSpanData.scala index d5c93925..a25582ae 100644 --- a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyReadableSpanData.scala +++ b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyReadableSpanData.scala @@ -17,7 +17,7 @@ package com.comcast.money.otel.handlers import java.util -import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo } +import com.comcast.money.api.{ EventInfo, InstrumentationLibrary, LinkInfo, Note, SpanId, SpanInfo } import io.opentelemetry.api.common.{ Attributes, AttributesBuilder } import io.opentelemetry.sdk.common.InstrumentationLibraryInfo import io.opentelemetry.sdk.resources.Resource @@ -81,14 +81,14 @@ private[otel] class MoneyReadableSpanData(info: SpanInfo) extends ReadableSpan w } .build() - private def convertEvents(events: util.List[SpanInfo.Event]): util.List[EventData] = + private def convertEvents(events: util.List[EventInfo]): util.List[EventData] = events.asScala .map({ event => MoneyEvent(event).asInstanceOf[EventData] }) .asJava - private def convertLinks(links: util.List[SpanInfo.Link]): util.List[LinkData] = + private def convertLinks(links: util.List[LinkInfo]): util.List[LinkData] = links.asScala .map({ link => MoneyLink(link).asInstanceOf[LinkData] diff --git a/money-otel-handler/src/test/java/com/comcast/money/otel/handlers/TestSpanInfo.java b/money-otel-handler/src/test/java/com/comcast/money/otel/handlers/TestSpanInfo.java index b2da5767..e5b6a3ef 100644 --- a/money-otel-handler/src/test/java/com/comcast/money/otel/handlers/TestSpanInfo.java +++ b/money-otel-handler/src/test/java/com/comcast/money/otel/handlers/TestSpanInfo.java @@ -20,10 +20,10 @@ import java.util.List; import java.util.Map; -import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; +import com.comcast.money.api.EventInfo; import com.comcast.money.api.InstrumentationLibrary; import com.comcast.money.api.Note; import com.comcast.money.api.SpanId; @@ -42,7 +42,7 @@ public Map> notes() { } @Override - public List events() { + public List events() { return Collections.emptyList(); } @@ -91,6 +91,16 @@ public long durationNanos() { return 0; } + @Override + public boolean hasEnded() { + return true; + } + + @Override + public boolean isRecording() { + return true; + } + @Override public String appName() { return "appName"; diff --git a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala index eeb2a1c2..021c3b43 100644 --- a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala +++ b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala @@ -16,14 +16,14 @@ package com.comcast.money.otel.handlers -import com.comcast.money.api.SpanInfo +import com.comcast.money.api.{ EventInfo, SpanInfo } import io.opentelemetry.api.common.{ AttributeKey, Attributes } import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec class MoneyEventSpec extends AnyWordSpec with Matchers { - val event = new SpanInfo.Event { + val event = new EventInfo { override def name(): String = "name" override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar") override def timestamp(): Long = 1234567890L diff --git a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyLinkSpec.scala b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyLinkSpec.scala index 4bf9533c..ff9f58eb 100644 --- a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyLinkSpec.scala +++ b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyLinkSpec.scala @@ -16,7 +16,7 @@ package com.comcast.money.otel.handlers -import com.comcast.money.api.{ IdGenerator, SpanInfo } +import com.comcast.money.api.{ IdGenerator, LinkInfo, SpanInfo } import io.opentelemetry.api.common.{ AttributeKey, Attributes } import io.opentelemetry.api.trace.{ SpanContext, TraceFlags, TraceState } import org.scalatest.matchers.should.Matchers @@ -25,7 +25,7 @@ import org.scalatest.wordspec.AnyWordSpec class MoneyLinkSpec extends AnyWordSpec with Matchers { val linkedContext = SpanContext.create(IdGenerator.generateRandomTraceIdAsHex(), IdGenerator.generateRandomIdAsHex(), TraceFlags.getSampled, TraceState.getDefault) - val link = new SpanInfo.Link { + val link = new LinkInfo { override def spanContext(): SpanContext = linkedContext override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar") } diff --git a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala index 21f0b8fa..dd749721 100644 --- a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala +++ b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala @@ -18,7 +18,7 @@ package com.comcast.money.otel.handlers import java.util import java.util.UUID -import com.comcast.money.api.{ IdGenerator, InstrumentationLibrary, Note, SpanId, SpanInfo } +import com.comcast.money.api.{ EventInfo, IdGenerator, InstrumentationLibrary, LinkInfo, Note, SpanId, SpanInfo } import io.opentelemetry.api.common.{ AttributeKey, Attributes } import io.opentelemetry.sdk.resources.Resource import io.opentelemetry.api.trace.{ Span, SpanContext, SpanKind, StatusCode, TraceFlags, TraceState } @@ -93,13 +93,15 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers { override def endTimeNanos(): Long = 3000000L override def status(): StatusCode = StatusCode.OK override def description(): String = "description" + override def hasEnded: Boolean = true + override def isRecording: Boolean = true override def durationNanos(): Long = 2000000L override def notes(): util.Map[String, Note[_]] = Map[String, Note[_]]("foo" -> Note.of("foo", "bar")).asJava - override def events(): util.List[SpanInfo.Event] = List(event).asJava - override def links(): util.List[SpanInfo.Link] = List(link).asJava + override def events(): util.List[EventInfo] = List(event).asJava + override def links(): util.List[LinkInfo] = List(link).asJava } - val event = new SpanInfo.Event { + val event = new EventInfo { override def name(): String = "event" override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar") override def timestamp(): Long = 1234567890L @@ -107,7 +109,7 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers { } val linkedContext = SpanContext.create(IdGenerator.generateRandomTraceIdAsHex(), IdGenerator.generateRandomIdAsHex(), TraceFlags.getSampled, TraceState.getDefault) - val link = new SpanInfo.Link { + val link = new LinkInfo { override def spanContext(): SpanContext = linkedContext override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar") } diff --git a/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java b/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java index 09d4a029..fe98b060 100644 --- a/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java +++ b/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java @@ -54,8 +54,10 @@ public void setUp() { id, "testName", SpanKind.INTERNAL, + true, 0L, 0L, + true, 0L, StatusCode.OK, "", diff --git a/money-spring/src/test/java/com/comcast/money/spring/TracedMethodInterceptorSpec.java b/money-spring/src/test/java/com/comcast/money/spring/TracedMethodInterceptorSpec.java index aa805d88..2cdd19e6 100644 --- a/money-spring/src/test/java/com/comcast/money/spring/TracedMethodInterceptorSpec.java +++ b/money-spring/src/test/java/com/comcast/money/spring/TracedMethodInterceptorSpec.java @@ -90,7 +90,7 @@ public void testTracing() throws Exception { verify(span).storeInContext(any()); verify(context).makeCurrent(); verify(springTracer).record("foo", "bar", false); - verify(span).stop(true); + verify(span).end(true); verify(scope).close(); } @@ -158,7 +158,7 @@ public void testTracingRecordsFailureOnException() throws Exception { verify(span).storeInContext(any()); verify(context).makeCurrent(); verify(springTracer).record("foo", "bar", false); - verify(span).stop(false); + verify(span).end(false); verify(scope).close(); } @@ -172,7 +172,7 @@ public void testTracingDoesNotTraceMethodsWithoutAnnotation() { @Test(expected = IllegalArgumentException.class) public void testTracingIgnoresException() { sampleTraceBean.doSomethingButIgnoreException(); - verify(span).stop(true); + verify(span).end(true); } @Configuration diff --git a/money-wire/src/main/avro/money.avsc b/money-wire/src/main/avro/money.avsc index c88e529a..fa30f169 100644 --- a/money-wire/src/main/avro/money.avsc +++ b/money-wire/src/main/avro/money.avsc @@ -26,6 +26,11 @@ "type": ["null","string"], "default": null }, + { + "name": "hasEnded", + "type": "boolean", + "default": false + }, { "name": "duration", "type": "long", diff --git a/money-wire/src/main/scala/com/comcast/money/wire/SpanConverters.scala b/money-wire/src/main/scala/com/comcast/money/wire/SpanConverters.scala index b430894b..036e6655 100644 --- a/money-wire/src/main/scala/com/comcast/money/wire/SpanConverters.scala +++ b/money-wire/src/main/scala/com/comcast/money/wire/SpanConverters.scala @@ -21,7 +21,7 @@ import java.util import java.util.Collections import java.util.concurrent.TimeUnit import com.comcast.money.api -import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo } +import com.comcast.money.api.{ EventInfo, InstrumentationLibrary, Note, SpanId, SpanInfo } import com.comcast.money.core._ import com.comcast.money.wire.avro import com.comcast.money.wire.avro.NoteType @@ -115,6 +115,7 @@ trait SpanWireConverters { span.host, span.library.name, span.library.version, + span.hasEnded, span.durationMicros, if (success == null) true else success, span.startTimeMillis, @@ -139,8 +140,10 @@ trait SpanWireConverters { new SpanInfo { override def notes(): util.Map[String, Note[_]] = toNotesMap(from.getNotes) - override def events(): util.List[SpanInfo.Event] = Collections.emptyList() + override def events(): util.List[EventInfo] = Collections.emptyList() override def startTimeNanos(): Long = TimeUnit.MILLISECONDS.toNanos(from.getStartTime) + override def hasEnded: Boolean = from.getHasEnded + override def isRecording: Boolean = true override def endTimeNanos(): Long = startTimeNanos + durationNanos override def status(): StatusCode = if (from.getSuccess) StatusCode.OK else StatusCode.ERROR override def kind(): SpanKind = SpanKind.INTERNAL diff --git a/money-wire/src/test/scala/com/comcast/money/wire/TestSpanInfo.scala b/money-wire/src/test/scala/com/comcast/money/wire/TestSpanInfo.scala index dac3bc74..64e4b3ce 100644 --- a/money-wire/src/test/scala/com/comcast/money/wire/TestSpanInfo.scala +++ b/money-wire/src/test/scala/com/comcast/money/wire/TestSpanInfo.scala @@ -28,9 +28,11 @@ case class TestSpanInfo( library: InstrumentationLibrary = new InstrumentationLibrary("test", "0.0.1"), startTimeNanos: Long = 0L, endTimeNanos: Long = 0L, + hasEnded: Boolean = true, + isRecording: Boolean = true, durationNanos: Long = 0L, status: StatusCode = StatusCode.UNSET, description: String = "", - notes: java.util.Map[String, Note[_]] = Collections.emptyMap(), + override val notes: java.util.Map[String, Note[_]] = Collections.emptyMap(), appName: String = Money.Environment.applicationName, - host: String = Money.Environment.hostName) extends SpanInfo + host: String = Money.Environment.hostName) extends SpanInfo \ No newline at end of file From 37f386ec3c7708a995001c27dd2a687b27d50827 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Fri, 19 Feb 2021 20:59:05 -0500 Subject: [PATCH 2/5] Add invalid and propagated spans and span helper methods --- .../com/comcast/money/api/InvalidSpan.java | 151 +++++++++++++++ .../com/comcast/money/api/PropagatedSpan.java | 175 ++++++++++++++++++ .../main/java/com/comcast/money/api/Span.java | 70 ++++++- .../com/comcast/money/core/Disabled.scala | 2 + .../comcast/money/core/UnrecordedSpan.scala | 1 + .../money/core/internal/SpanLocal.scala | 6 +- 6 files changed, 396 insertions(+), 9 deletions(-) create mode 100644 money-api/src/main/java/com/comcast/money/api/InvalidSpan.java create mode 100644 money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java diff --git a/money-api/src/main/java/com/comcast/money/api/InvalidSpan.java b/money-api/src/main/java/com/comcast/money/api/InvalidSpan.java new file mode 100644 index 00000000..09f1f3ca --- /dev/null +++ b/money-api/src/main/java/com/comcast/money/api/InvalidSpan.java @@ -0,0 +1,151 @@ +/* + * Copyright 2012 Comcast Cable Communications Management, LLC + * + * 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 com.comcast.money.api; + +import java.util.concurrent.TimeUnit; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Scope; + +enum InvalidSpan implements Span, SpanInfo { + INSTANCE; + + @Override + public Span addEvent(String name, Attributes attributes) { + return this; + } + + @Override + public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) { + return this; + } + + @Override + public Span setStatus(StatusCode canonicalCode, String description) { + return this; + } + + @Override + public Span recordException(Throwable exception, Attributes additionalAttributes) { + return this; + } + + @Override + public Span updateName(String name) { + return this; + } + + @Override + public void end() { } + + @Override + public void end(long timestamp, TimeUnit unit) { } + + @Override + public SpanContext getSpanContext() { + return SpanId.getInvalid().toSpanContext(); + } + + @Override + public long startTimeNanos() { + return 0L; + } + + @Override + public boolean hasEnded() { + return false; + } + + @Override + public long endTimeNanos() { + return 0L; + } + + @Override + public StatusCode status() { + return StatusCode.UNSET; + } + + @Override + public boolean isRecording() { + return false; + } + + @Override + public SpanKind kind() { + return SpanKind.INTERNAL; + } + + @Override + public String description() { + return null; + } + + @Override + public long durationNanos() { + return 0L; + } + + @Override + public InstrumentationLibrary library() { + return InstrumentationLibrary.UNKNOWN; + } + + @Override + public String appName() { + return null; + } + + @Override + public String host() { + return null; + } + + @Override + public Span record(Note note) { + return this; + } + + @Override + public Scope startTimer(String timerKey) { + return Scope.noop(); + } + + @Override + public void stopTimer(String timerKey) { } + + @Override + public Span attachScope(Scope scope) { + return this; + } + + @Override + public SpanInfo info() { + return this; + } + + @Override + public SpanId id() { + return SpanId.getInvalid(); + } + + @Override + public void close() { } +} diff --git a/money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java b/money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java new file mode 100644 index 00000000..3fa058f8 --- /dev/null +++ b/money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java @@ -0,0 +1,175 @@ +/* + * Copyright 2012 Comcast Cable Communications Management, LLC + * + * 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 com.comcast.money.api; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Scope; + +final class PropagatedSpan implements Span, SpanInfo { + private final SpanId id; + private final SpanKind kind; + private final String name; + private final List scopes = new ArrayList<>(); + + PropagatedSpan(SpanId id, String name, SpanKind kind) { + this.id = id; + this.kind = kind; + this.name = name; + } + + @Override + public Span addEvent(String name, Attributes attributes) { + return this; + } + + @Override + public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) { + return this; + } + + @Override + public Span setStatus(StatusCode canonicalCode, String description) { + return this; + } + + @Override + public Span recordException(Throwable exception, Attributes additionalAttributes) { + return this; + } + + @Override + public Span updateName(String name) { + return this; + } + + @Override + public Span record(Note note) { + return this; + } + + @Override + public Scope startTimer(String timerKey) { + return Scope.noop(); + } + + @Override + public void stopTimer(String timerKey) { } + + @Override + public Span attachScope(Scope scope) { + scopes.add(scope); + return this; + } + + @Override + public SpanInfo info() { + return this; + } + + @Override + public SpanId id() { + return id; + } + + @Override + public long startTimeNanos() { + return 0L; + } + + @Override + public boolean hasEnded() { + return false; + } + + @Override + public long endTimeNanos() { + return 0L; + } + + @Override + public StatusCode status() { + return StatusCode.UNSET; + } + + @Override + public SpanKind kind() { + return kind; + } + + @Override + public String description() { + return null; + } + + @Override + public String name() { + return name; + } + + @Override + public long durationNanos() { + return 0L; + } + + @Override + public InstrumentationLibrary library() { + return InstrumentationLibrary.UNKNOWN; + } + + @Override + public String appName() { + return null; + } + + @Override + public String host() { + return null; + } + + @Override + public void end() { } + + @Override + public void end(long timestamp, TimeUnit unit) { } + + @Override + public SpanContext getSpanContext() { + return id.toSpanContext(); + } + + @Override + public boolean isRecording() { + return false; + } + + @Override + public void close() { + for (Iterator iterator = scopes.iterator(); iterator.hasNext(); ) { + Scope scope = iterator.next(); + iterator.remove(); + scope.close(); + } + } +} diff --git a/money-api/src/main/java/com/comcast/money/api/Span.java b/money-api/src/main/java/com/comcast/money/api/Span.java index a1239e64..5c31eb7b 100644 --- a/money-api/src/main/java/com/comcast/money/api/Span.java +++ b/money-api/src/main/java/com/comcast/money/api/Span.java @@ -21,14 +21,15 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; /** * A Span is a container that represents a unit of work. It could be a long running operation or sequence of * statements in process, or a remote system call. - * - * A Span is immutable, all changes to the span result in a new Span being created. */ public interface Span extends io.opentelemetry.api.trace.Span, Scope { @@ -150,7 +151,68 @@ default void end(boolean result) { /** * @return The {@link SpanId} of the Span */ - default SpanId id() { - return info().id(); + SpanId id(); + + /** + * Returns the {@link io.opentelemetry.api.trace.Span} from the current {@link Context}, falling back to a default, no-op + * {@link io.opentelemetry.api.trace.Span} if there is no span in the current context. + */ + static Span current() { + return fromContext(Context.current()); + } + + /** + * Returns the {@link io.opentelemetry.api.trace.Span} from the specified {@link Context}, falling back to a default, no-op + * {@link io.opentelemetry.api.trace.Span} if there is no span in the context. + */ + static Span fromContext(Context context) { + Span span = fromContextOrNull(context); + return span != null ? span : getInvalid(); + } + + /** + * Returns the {@link io.opentelemetry.api.trace.Span} from the specified {@link Context}, or {@code null} if there is no + * span in the context. + */ + static Span fromContextOrNull(Context context) { + io.opentelemetry.api.trace.Span otelSpan = io.opentelemetry.api.trace.Span.fromContextOrNull(context); + if (otelSpan instanceof Span && otelSpan.getSpanContext().isValid()) { + return (Span) otelSpan; + } + return null; + } + + /** + * Returns an invalid {@link io.opentelemetry.api.trace.Span}. + */ + static Span getInvalid() { + return InvalidSpan.INSTANCE; + } + + /** + * Returns a non-recording {@link Span} that holds the provided {@link SpanId} but has no + * functionality. It will not be exported and all tracing operations are no-op, but it can be used + * to propagate a valid {@link SpanId} downstream. + */ + static Span wrap(SpanId id) { + return wrap(id, null, SpanKind.INTERNAL); + } + + /** + * Returns a non-recording {@link Span} that holds the provided {@link SpanId} but has no + * functionality. It will not be exported and all tracing operations are no-op, but it can be used + * to propagate a valid {@link SpanId} downstream. + */ + static Span wrap(SpanId id, String name) { + return wrap(id, name, SpanKind.INTERNAL); + } + + /** + * Returns a non-recording {@link Span} that holds the provided {@link SpanId} but has no + * functionality. It will not be exported and all tracing operations are no-op, but it can be used + * to propagate a valid {@link SpanId} downstream. + */ + static Span wrap(SpanId id, String name, SpanKind kind) { + return id != null && id.isValid() ? new PropagatedSpan(id, name, kind) : getInvalid(); } } diff --git a/money-core/src/main/scala/com/comcast/money/core/Disabled.scala b/money-core/src/main/scala/com/comcast/money/core/Disabled.scala index 50e64b60..6cc892b7 100644 --- a/money-core/src/main/scala/com/comcast/money/core/Disabled.scala +++ b/money-core/src/main/scala/com/comcast/money/core/Disabled.scala @@ -135,6 +135,8 @@ object DisabledSpan extends Span { override def startTimer(timerKey: String): Scope = () => () + override def id(): SpanId = SpanId.getInvalid + override def info(): SpanInfo = null override def attachScope(scope: Scope): Span = this diff --git a/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala b/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala index 5fd17049..421bcc59 100644 --- a/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala +++ b/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala @@ -30,6 +30,7 @@ private[core] final case class UnrecordedSpan( private var scopes: List[Scope] = Nil + override def id(): SpanId = spanId override def info(): SpanInfo = CoreSpanInfo(spanId, name) override def getSpanContext: SpanContext = spanId.toSpanContext diff --git a/money-core/src/main/scala/com/comcast/money/core/internal/SpanLocal.scala b/money-core/src/main/scala/com/comcast/money/core/internal/SpanLocal.scala index 7fc4d994..669c8735 100644 --- a/money-core/src/main/scala/com/comcast/money/core/internal/SpanLocal.scala +++ b/money-core/src/main/scala/com/comcast/money/core/internal/SpanLocal.scala @@ -18,7 +18,6 @@ package com.comcast.money.core.internal import com.comcast.money.api.Span import io.opentelemetry.context.{ Context, Scope } -import io.opentelemetry.api.trace.{ Span => OtelSpan } /** * Provides a context for storing SpanIds. Keeps a stack of trace ids so that we @@ -36,10 +35,7 @@ object SpanLocal extends SpanContext { def fromContext(context: Context): Option[Span] = if (context != null) { - Option(OtelSpan.fromContextOrNull(context)) match { - case Some(span: Span) => Some(span) - case _ => None - } + Option(Span.fromContextOrNull(context)) } else None def clear(): Unit = Context.root.makeCurrent() From 0d389f9618ef587b0aaabc7ec6a1ce2cf4fd9eb0 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Fri, 19 Feb 2021 22:06:12 -0500 Subject: [PATCH 3/5] Migrate simple case classes to Java --- .../com/comcast/money/api/InvalidSpan.java | 2 + .../com/comcast/money/api/PropagatedSpan.java | 62 +++++------ .../main/java/com/comcast/money/api/Span.java | 11 +- .../com/comcast/money/core/Clock.java} | 8 +- .../com/comcast/money/core/CoreEvent.java | 57 ++++++++++ .../money/core/CoreExceptionEvent.java | 102 ++++++++++++++++++ .../java/com/comcast/money/core/CoreLink.java | 64 +++++++++++ .../core/context/ContextStorageFilter.java} | 10 +- .../com/comcast/money/core/CoreEvent.scala | 54 ---------- .../com/comcast/money/core/CoreLink.scala | 25 ----- .../com/comcast/money/core/CoreSpan.scala | 17 ++- .../comcast/money/core/CoreSpanBuilder.scala | 24 +---- 12 files changed, 284 insertions(+), 152 deletions(-) rename money-core/src/main/{scala/com/comcast/money/core/Clock.scala => java/com/comcast/money/core/Clock.java} (87%) create mode 100644 money-core/src/main/java/com/comcast/money/core/CoreEvent.java create mode 100644 money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java create mode 100644 money-core/src/main/java/com/comcast/money/core/CoreLink.java rename money-core/src/main/{scala/com/comcast/money/core/context/ContextStorageFilter.scala => java/com/comcast/money/core/context/ContextStorageFilter.java} (70%) delete mode 100644 money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala delete mode 100644 money-core/src/main/scala/com/comcast/money/core/CoreLink.scala diff --git a/money-api/src/main/java/com/comcast/money/api/InvalidSpan.java b/money-api/src/main/java/com/comcast/money/api/InvalidSpan.java index 09f1f3ca..96fbae07 100644 --- a/money-api/src/main/java/com/comcast/money/api/InvalidSpan.java +++ b/money-api/src/main/java/com/comcast/money/api/InvalidSpan.java @@ -27,6 +27,7 @@ enum InvalidSpan implements Span, SpanInfo { INSTANCE; + // $COVERAGE-OFF$ @Override public Span addEvent(String name, Attributes attributes) { return this; @@ -148,4 +149,5 @@ public SpanId id() { @Override public void close() { } + // $COVERAGE-ON$ } diff --git a/money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java b/money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java index 3fa058f8..2eed87b2 100644 --- a/money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java +++ b/money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java @@ -40,59 +40,74 @@ final class PropagatedSpan implements Span, SpanInfo { } @Override - public Span addEvent(String name, Attributes attributes) { + public SpanInfo info() { return this; } @Override - public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) { - return this; + public SpanId id() { + return id; } @Override - public Span setStatus(StatusCode canonicalCode, String description) { - return this; + public SpanContext getSpanContext() { + return id.toSpanContext(); } @Override - public Span recordException(Throwable exception, Attributes additionalAttributes) { + public Span attachScope(Scope scope) { + scopes.add(scope); return this; } @Override - public Span updateName(String name) { + public void close() { + for (Iterator iterator = scopes.iterator(); iterator.hasNext(); ) { + Scope scope = iterator.next(); + iterator.remove(); + scope.close(); + } + } + + // $COVERAGE-OFF$ + @Override + public Span addEvent(String name, Attributes attributes) { return this; } @Override - public Span record(Note note) { + public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) { return this; } @Override - public Scope startTimer(String timerKey) { - return Scope.noop(); + public Span setStatus(StatusCode canonicalCode, String description) { + return this; } @Override - public void stopTimer(String timerKey) { } + public Span recordException(Throwable exception, Attributes additionalAttributes) { + return this; + } @Override - public Span attachScope(Scope scope) { - scopes.add(scope); + public Span updateName(String name) { return this; } @Override - public SpanInfo info() { + public Span record(Note note) { return this; } @Override - public SpanId id() { - return id; + public Scope startTimer(String timerKey) { + return Scope.noop(); } + @Override + public void stopTimer(String timerKey) { } + @Override public long startTimeNanos() { return 0L; @@ -154,22 +169,9 @@ public void end() { } @Override public void end(long timestamp, TimeUnit unit) { } - @Override - public SpanContext getSpanContext() { - return id.toSpanContext(); - } - @Override public boolean isRecording() { return false; } - - @Override - public void close() { - for (Iterator iterator = scopes.iterator(); iterator.hasNext(); ) { - Scope scope = iterator.next(); - iterator.remove(); - scope.close(); - } - } + // $COVERAGE-ON$ } diff --git a/money-api/src/main/java/com/comcast/money/api/Span.java b/money-api/src/main/java/com/comcast/money/api/Span.java index 5c31eb7b..467d1f25 100644 --- a/money-api/src/main/java/com/comcast/money/api/Span.java +++ b/money-api/src/main/java/com/comcast/money/api/Span.java @@ -175,9 +175,14 @@ static Span fromContext(Context context) { * span in the context. */ static Span fromContextOrNull(Context context) { - io.opentelemetry.api.trace.Span otelSpan = io.opentelemetry.api.trace.Span.fromContextOrNull(context); - if (otelSpan instanceof Span && otelSpan.getSpanContext().isValid()) { - return (Span) otelSpan; + if (context != null) { + io.opentelemetry.api.trace.Span otelSpan = io.opentelemetry.api.trace.Span.fromContextOrNull(context); + if (otelSpan instanceof Span) { + Span span = (Span) otelSpan; + if (span.info().id().isValid()) { + return (Span) otelSpan; + } + } } return null; } diff --git a/money-core/src/main/scala/com/comcast/money/core/Clock.scala b/money-core/src/main/java/com/comcast/money/core/Clock.java similarity index 87% rename from money-core/src/main/scala/com/comcast/money/core/Clock.scala rename to money-core/src/main/java/com/comcast/money/core/Clock.java index 525bb7df..5037cd98 100644 --- a/money-core/src/main/scala/com/comcast/money/core/Clock.scala +++ b/money-core/src/main/java/com/comcast/money/core/Clock.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.comcast.money.core +package com.comcast.money.core; -trait Clock { - def now: Long - def nanoTime: Long +public interface Clock { + long now(); + long nanoTime(); } diff --git a/money-core/src/main/java/com/comcast/money/core/CoreEvent.java b/money-core/src/main/java/com/comcast/money/core/CoreEvent.java new file mode 100644 index 00000000..3dacea33 --- /dev/null +++ b/money-core/src/main/java/com/comcast/money/core/CoreEvent.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012 Comcast Cable Communications Management, LLC + * + * 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 com.comcast.money.core; + +import io.opentelemetry.api.common.Attributes; + +import com.comcast.money.api.EventInfo; + +class CoreEvent implements EventInfo { + private final String name; + private final long timestampNanos; + private final Attributes attributes; + + public CoreEvent(String name, long timestampNanos) { + this(name, timestampNanos, Attributes.empty()); + } + + public CoreEvent(String name, long timestampNanos, Attributes attributes) { + this.name = name; + this.timestampNanos = timestampNanos; + this.attributes = attributes; + } + + @Override + public String name() { + return name; + } + + @Override + public Attributes attributes() { + return attributes; + } + + @Override + public long timestamp() { + return timestampNanos; + } + + @Override + public Throwable exception() { + return null; + } +} diff --git a/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java b/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java new file mode 100644 index 00000000..8422c127 --- /dev/null +++ b/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012 Comcast Cable Communications Management, LLC + * + * 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 com.comcast.money.core; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; + +import com.comcast.money.api.EventInfo; + +final class CoreExceptionEvent implements EventInfo { + private final Throwable exception; + private final long timestampNanos; + private final Attributes additionalAttributes; + private final Object lock = new Object(); + private transient Attributes attributes; + + public CoreExceptionEvent(Throwable exception, long timestampNanos) { + this(exception, timestampNanos, Attributes.empty()); + } + + public CoreExceptionEvent(Throwable exception, long timestampNanos, Attributes attributes) { + this.timestampNanos = timestampNanos; + this.exception = exception; + this.additionalAttributes = attributes; + } + + @Override + public String name() { + return SemanticAttributes.EXCEPTION_EVENT_NAME; + } + + @Override + public Attributes attributes() { + if (attributes != null) { + return attributes; + } + synchronized (lock) { + if (attributes != null) { + return attributes; + } + AttributesBuilder builder = additionalAttributes.toBuilder(); + builder.put(SemanticAttributes.EXCEPTION_TYPE, exception.getClass().getCanonicalName()); + String message = exception.getMessage(); + if (message != null && !message.isEmpty()) { + builder.put(SemanticAttributes.EXCEPTION_MESSAGE, message); + } + StringWriter writer = new StringWriter(); + exception.printStackTrace(new PrintWriter(writer)); + builder.put(SemanticAttributes.EXCEPTION_STACKTRACE, writer.toString()); + attributes = builder.build(); + return attributes; + } + } + + @Override + public long timestamp() { + return timestampNanos; + } + + @Override + public Throwable exception() { + return exception; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CoreExceptionEvent that = (CoreExceptionEvent) o; + + if (timestampNanos != that.timestampNanos) return false; + if (!exception.equals(that.exception)) return false; + return additionalAttributes.equals(that.additionalAttributes); + } + + @Override + public int hashCode() { + int result = exception.hashCode(); + result = 31 * result + (int) (timestampNanos ^ (timestampNanos >>> 32)); + result = 31 * result + additionalAttributes.hashCode(); + return result; + } +} diff --git a/money-core/src/main/java/com/comcast/money/core/CoreLink.java b/money-core/src/main/java/com/comcast/money/core/CoreLink.java new file mode 100644 index 00000000..bd35f9ac --- /dev/null +++ b/money-core/src/main/java/com/comcast/money/core/CoreLink.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012 Comcast Cable Communications Management, LLC + * + * 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 com.comcast.money.core; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanContext; + +import com.comcast.money.api.LinkInfo; + +final class CoreLink implements LinkInfo { + private final SpanContext spanContext; + private final Attributes attributes; + + public CoreLink(SpanContext spanContext) { + this(spanContext, Attributes.empty()); + } + + public CoreLink(SpanContext spanContext, Attributes attributes) { + this.spanContext = spanContext; + this.attributes = attributes; + } + + @Override + public SpanContext spanContext() { + return spanContext; + } + + @Override + public Attributes attributes() { + return attributes; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CoreLink coreLink = (CoreLink) o; + + if (!spanContext.equals(coreLink.spanContext)) return false; + return attributes.equals(coreLink.attributes); + } + + @Override + public int hashCode() { + int result = spanContext.hashCode(); + result = 31 * result + attributes.hashCode(); + return result; + } +} diff --git a/money-core/src/main/scala/com/comcast/money/core/context/ContextStorageFilter.scala b/money-core/src/main/java/com/comcast/money/core/context/ContextStorageFilter.java similarity index 70% rename from money-core/src/main/scala/com/comcast/money/core/context/ContextStorageFilter.scala rename to money-core/src/main/java/com/comcast/money/core/context/ContextStorageFilter.java index b2bdc97a..60c0700e 100644 --- a/money-core/src/main/scala/com/comcast/money/core/context/ContextStorageFilter.scala +++ b/money-core/src/main/java/com/comcast/money/core/context/ContextStorageFilter.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package com.comcast.money.core.context +package com.comcast.money.core.context; -import io.opentelemetry.context.{ Context, ContextStorage, Scope } +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.Scope; -trait ContextStorageFilter { - def attach(context: Context, storage: ContextStorage): Scope +public interface ContextStorageFilter { + Scope attach(Context context, ContextStorage storage); } diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala b/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala deleted file mode 100644 index a67cb0b4..00000000 --- a/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012 Comcast Cable Communications Management, LLC - * - * 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 com.comcast.money.core - -import java.io.{ PrintWriter, StringWriter } -import com.comcast.money.api.{ EventInfo, SpanInfo } -import io.opentelemetry.api.common.Attributes -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes - -private[core] case class CoreEvent( - name: String, - eventAttributes: Attributes, - timestamp: Long, - exception: Throwable) extends EventInfo { - - lazy val attributes: Attributes = initializeAttributes() - - private def initializeAttributes(): Attributes = { - if (exception != null) { - val attributeBuilder = if (eventAttributes != null) { - eventAttributes.toBuilder - } else { - Attributes.builder() - } - attributeBuilder.put(SemanticAttributes.EXCEPTION_TYPE, exception.getClass.getCanonicalName) - if (exception.getMessage != null) { - attributeBuilder.put(SemanticAttributes.EXCEPTION_MESSAGE, exception.getMessage) - } - val writer = new StringWriter - exception.printStackTrace(new PrintWriter(writer)) - attributeBuilder.put(SemanticAttributes.EXCEPTION_STACKTRACE, writer.toString) - attributeBuilder.build() - } else if (eventAttributes != null) { - eventAttributes - } else { - Attributes.empty() - } - } -} - diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala b/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala deleted file mode 100644 index ec715e1c..00000000 --- a/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2012 Comcast Cable Communications Management, LLC - * - * 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 com.comcast.money.core - -import com.comcast.money.api.{ LinkInfo, SpanInfo } -import io.opentelemetry.api.common.Attributes -import io.opentelemetry.api.trace.SpanContext - -private[core] final case class CoreLink( - spanContext: SpanContext, - attributes: Attributes = Attributes.empty()) extends LinkInfo diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala index b598998b..e249b088 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala @@ -20,7 +20,6 @@ import com.comcast.money.api._ import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.{ SpanContext, SpanKind, StatusCode } import io.opentelemetry.context.Scope -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -96,18 +95,16 @@ private[core] case class CoreSpan( override def close(): Unit = end() - override def addEvent(eventName: String): Span = addEventInternal(eventName, Attributes.empty(), clock.now) - override def addEvent(eventName: String, eventAttributes: Attributes): Span = addEventInternal(eventName, eventAttributes, clock.now) - override def addEvent(eventName: String, eventAttributes: Attributes, timestamp: scala.Long, unit: TimeUnit): Span = addEventInternal(eventName, eventAttributes, unit.toNanos(timestamp)) - - private def addEventInternal(eventName: String, eventAttributes: Attributes, timestampNanos: scala.Long, exception: Throwable = null): Span = { - events += CoreEvent(eventName, eventAttributes, timestampNanos, exception) + override def addEvent(eventName: String, eventAttributes: Attributes): Span = addEvent(eventName, eventAttributes, clock.now, TimeUnit.NANOSECONDS) + override def addEvent(eventName: String, eventAttributes: Attributes, timestamp: scala.Long, unit: TimeUnit): Span = { + events += new CoreEvent(eventName, unit.toNanos(timestamp), eventAttributes) this } - override def recordException(exception: Throwable): Span = recordException(exception, Attributes.empty()) - override def recordException(exception: Throwable, eventAttributes: Attributes): Span = - addEventInternal(SemanticAttributes.EXCEPTION_EVENT_NAME, eventAttributes, clock.now, exception) + override def recordException(exception: Throwable, eventAttributes: Attributes): Span = { + events += new CoreExceptionEvent(exception, clock.now, eventAttributes) + this + } override def setStatus(canonicalCode: StatusCode): Span = setStatus(canonicalCode, null) diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala index 94e5ab48..f55b423e 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala @@ -19,11 +19,10 @@ package com.comcast.money.core import com.comcast.money.api._ import com.comcast.money.core.internal.{ SpanContext, SpanLocal } import com.comcast.money.core.samplers.{ DropResult, RecordResult, Sampler } -import io.opentelemetry.api.common.{ AttributeKey, Attributes } +import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.{ SpanKind, TraceFlags, SpanContext => OtelSpanContext } import io.opentelemetry.context.Context -import java.time.Instant import java.util.concurrent.TimeUnit import scala.collection.JavaConverters._ @@ -58,23 +57,11 @@ private[core] class CoreSpanBuilder( this } - override def addLink(spanContext: OtelSpanContext): SpanBuilder = addLink(spanContext, Attributes.empty) - override def addLink(spanContext: OtelSpanContext, attributes: Attributes): SpanBuilder = { - links = CoreLink(spanContext, attributes) :: links + links = new CoreLink(spanContext, attributes) :: links this } - override def setAttribute(key: String, value: String): SpanBuilder = setAttribute[String](AttributeKey.stringKey(key), value) - - override def setAttribute(key: String, value: Long): SpanBuilder = setAttribute[java.lang.Long](AttributeKey.longKey(key), value) - - override def setAttribute(key: String, value: Double): SpanBuilder = setAttribute[java.lang.Double](AttributeKey.doubleKey(key), value) - - override def setAttribute(key: String, value: Boolean): SpanBuilder = setAttribute[java.lang.Boolean](AttributeKey.booleanKey(key), value) - - override def setAttribute[T](key: AttributeKey[T], value: T): SpanBuilder = record(Note.of(key, value)) - override def record(note: Note[_]): SpanBuilder = { notes = note :: notes this @@ -90,13 +77,6 @@ private[core] class CoreSpanBuilder( this } - override def setStartTimestamp(startTimestamp: Instant): SpanBuilder = { - this.startTimeNanos = if (startTimestamp != null) - TimeUnit.SECONDS.toNanos(startTimestamp.getEpochSecond) + startTimestamp.getNano - else 0L - this - } - private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long): Span = CoreSpan( id = id, name = name, From 4c896315e29d56c2960c47ff23abc6ac6ec90ce3 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Sat, 20 Feb 2021 14:03:15 -0500 Subject: [PATCH 4/5] Lombokify, convert CoreSpanInfo to Java (oof) --- build.sbt | 4 + lombok.config | 3 + .../java/com/comcast/money/api/EventInfo.java | 18 +- .../com/comcast/money/core/CoreEvent.java | 33 +- .../money/core/CoreExceptionEvent.java | 64 +- .../java/com/comcast/money/core/CoreLink.java | 45 +- .../com/comcast/money/core/CoreSpanInfo.java | 57 ++ .../com/comcast/money/core/CoreSpan.scala | 31 +- .../com/comcast/money/core/CoreSpanInfo.scala | 39 -- .../comcast/money/core/UnrecordedSpan.scala | 2 +- .../com/comcast/money/core/japi/JMoney.java | 621 ------------------ .../comcast/money/core/CoreSpanInfoSpec.scala | 44 -- .../com/comcast/money/core/CoreSpanSpec.scala | 14 +- .../core/handlers/MetricsHandlerSpec.scala | 3 +- .../core/handlers/SpanLogFormatterSpec.scala | 46 +- .../money/core/handlers/TestData.scala | 50 +- .../comcast/money/core/japi/JMoneySpec.scala | 362 ---------- .../money/otel/handlers/MoneyEvent.scala | 2 +- .../money/otel/handlers/MoneyEventSpec.scala | 4 +- .../handlers/MoneyReadableSpanDataSpec.scala | 2 +- .../MoneyClientHttpInterceptorSpec.java | 34 +- project/Dependencies.scala | 2 + project/plugins.sbt | 2 + 23 files changed, 212 insertions(+), 1270 deletions(-) create mode 100644 lombok.config create mode 100644 money-core/src/main/java/com/comcast/money/core/CoreSpanInfo.java delete mode 100644 money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala delete mode 100644 money-core/src/main/scala/com/comcast/money/core/japi/JMoney.java delete mode 100644 money-core/src/test/scala/com/comcast/money/core/CoreSpanInfoSpec.scala delete mode 100644 money-core/src/test/scala/com/comcast/money/core/japi/JMoneySpec.scala diff --git a/build.sbt b/build.sbt index 76883dba..5b651729 100644 --- a/build.sbt +++ b/build.sbt @@ -58,6 +58,7 @@ lazy val moneyCore = Seq( slf4j, log4jbinding, + lombok, metricsCore, openTelemetryApi, openTelemetrySemConv, @@ -330,6 +331,7 @@ def javaOnlyProjectSettings = projectSettings ++ Seq( ) def projectSettings = basicSettings ++ Seq( + compileOrder := CompileOrder.JavaThenScala, ScoverageKeys.coverageHighlighting := true, ScoverageKeys.coverageMinimum := 80, ScoverageKeys.coverageFailOnMinimum := true, @@ -366,6 +368,8 @@ def basicSettings = Defaults.itSettings ++ Seq( "-language:existentials", "-language:postfixOps", "-language:reflectiveCalls"), + // temporary to enable Lombok config to work in submodules + javaHome := sys.env.get("JAVA_HOME").map(file), javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), javacOptions in doc := Seq("-source", "1.8"), scalariformAutoformat := true, diff --git a/lombok.config b/lombok.config new file mode 100644 index 00000000..204c0d4a --- /dev/null +++ b/lombok.config @@ -0,0 +1,3 @@ +config.stopBubbling = true +lombok.accessors.chain=true +lombok.accessors.fluent=true \ No newline at end of file diff --git a/money-api/src/main/java/com/comcast/money/api/EventInfo.java b/money-api/src/main/java/com/comcast/money/api/EventInfo.java index da1f1f24..c040975f 100644 --- a/money-api/src/main/java/com/comcast/money/api/EventInfo.java +++ b/money-api/src/main/java/com/comcast/money/api/EventInfo.java @@ -16,6 +16,8 @@ package com.comcast.money.api; +import java.util.concurrent.TimeUnit; + import io.opentelemetry.api.common.Attributes; /** @@ -35,7 +37,21 @@ public interface EventInfo { /** * @return the timestamp of when the event occurred in nanoseconds since the epoch */ - long timestamp(); + long timestampNanos(); + + /** + * @return the timestamp of when the event occurred in microseconds since the epoch + */ + default long timestampMicros() { + return TimeUnit.NANOSECONDS.toMicros(timestampNanos()); + } + + /** + * @return the timestamp of when the event occurred in milliseconds since the epoch + */ + default long timestampMillis() { + return TimeUnit.NANOSECONDS.toMillis(timestampNanos()); + } /** * @return an exception if one was recorded with the event; otherwise {@code null} diff --git a/money-core/src/main/java/com/comcast/money/core/CoreEvent.java b/money-core/src/main/java/com/comcast/money/core/CoreEvent.java index 3dacea33..0c13bdf3 100644 --- a/money-core/src/main/java/com/comcast/money/core/CoreEvent.java +++ b/money-core/src/main/java/com/comcast/money/core/CoreEvent.java @@ -17,38 +17,15 @@ package com.comcast.money.core; import io.opentelemetry.api.common.Attributes; +import lombok.Value; import com.comcast.money.api.EventInfo; +@Value class CoreEvent implements EventInfo { - private final String name; - private final long timestampNanos; - private final Attributes attributes; - - public CoreEvent(String name, long timestampNanos) { - this(name, timestampNanos, Attributes.empty()); - } - - public CoreEvent(String name, long timestampNanos, Attributes attributes) { - this.name = name; - this.timestampNanos = timestampNanos; - this.attributes = attributes; - } - - @Override - public String name() { - return name; - } - - @Override - public Attributes attributes() { - return attributes; - } - - @Override - public long timestamp() { - return timestampNanos; - } + String name; + long timestampNanos; + Attributes attributes; @Override public Throwable exception() { diff --git a/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java b/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java index 8422c127..816710b3 100644 --- a/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java +++ b/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java @@ -22,23 +22,22 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import lombok.EqualsAndHashCode; +import lombok.Getter; import com.comcast.money.api.EventInfo; +@EqualsAndHashCode(exclude = {"additionalAttributes", "lock"}) final class CoreExceptionEvent implements EventInfo { - private final Throwable exception; - private final long timestampNanos; + @Getter private final Throwable exception; + @Getter private final long timestampNanos; private final Attributes additionalAttributes; private final Object lock = new Object(); private transient Attributes attributes; - public CoreExceptionEvent(Throwable exception, long timestampNanos) { - this(exception, timestampNanos, Attributes.empty()); - } - public CoreExceptionEvent(Throwable exception, long timestampNanos, Attributes attributes) { - this.timestampNanos = timestampNanos; this.exception = exception; + this.timestampNanos = timestampNanos; this.additionalAttributes = attributes; } @@ -56,47 +55,22 @@ public Attributes attributes() { if (attributes != null) { return attributes; } - AttributesBuilder builder = additionalAttributes.toBuilder(); - builder.put(SemanticAttributes.EXCEPTION_TYPE, exception.getClass().getCanonicalName()); - String message = exception.getMessage(); - if (message != null && !message.isEmpty()) { - builder.put(SemanticAttributes.EXCEPTION_MESSAGE, message); - } - StringWriter writer = new StringWriter(); - exception.printStackTrace(new PrintWriter(writer)); - builder.put(SemanticAttributes.EXCEPTION_STACKTRACE, writer.toString()); - attributes = builder.build(); + + attributes = mergeExceptionAttributes(additionalAttributes, exception); return attributes; } } - @Override - public long timestamp() { - return timestampNanos; - } - - @Override - public Throwable exception() { - return exception; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CoreExceptionEvent that = (CoreExceptionEvent) o; - - if (timestampNanos != that.timestampNanos) return false; - if (!exception.equals(that.exception)) return false; - return additionalAttributes.equals(that.additionalAttributes); - } - - @Override - public int hashCode() { - int result = exception.hashCode(); - result = 31 * result + (int) (timestampNanos ^ (timestampNanos >>> 32)); - result = 31 * result + additionalAttributes.hashCode(); - return result; + private Attributes mergeExceptionAttributes(Attributes attributes, Throwable exception) { + AttributesBuilder builder = additionalAttributes != null ? attributes.toBuilder() : Attributes.builder(); + builder.put(SemanticAttributes.EXCEPTION_TYPE, exception.getClass().getCanonicalName()); + String message = exception.getMessage(); + if (message != null && !message.isEmpty()) { + builder.put(SemanticAttributes.EXCEPTION_MESSAGE, message); + } + StringWriter writer = new StringWriter(); + exception.printStackTrace(new PrintWriter(writer)); + builder.put(SemanticAttributes.EXCEPTION_STACKTRACE, writer.toString()); + return builder.build(); } } diff --git a/money-core/src/main/java/com/comcast/money/core/CoreLink.java b/money-core/src/main/java/com/comcast/money/core/CoreLink.java index bd35f9ac..520505dd 100644 --- a/money-core/src/main/java/com/comcast/money/core/CoreLink.java +++ b/money-core/src/main/java/com/comcast/money/core/CoreLink.java @@ -18,47 +18,12 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanContext; +import lombok.Value; import com.comcast.money.api.LinkInfo; -final class CoreLink implements LinkInfo { - private final SpanContext spanContext; - private final Attributes attributes; - - public CoreLink(SpanContext spanContext) { - this(spanContext, Attributes.empty()); - } - - public CoreLink(SpanContext spanContext, Attributes attributes) { - this.spanContext = spanContext; - this.attributes = attributes; - } - - @Override - public SpanContext spanContext() { - return spanContext; - } - - @Override - public Attributes attributes() { - return attributes; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CoreLink coreLink = (CoreLink) o; - - if (!spanContext.equals(coreLink.spanContext)) return false; - return attributes.equals(coreLink.attributes); - } - - @Override - public int hashCode() { - int result = spanContext.hashCode(); - result = 31 * result + attributes.hashCode(); - return result; - } +@Value +class CoreLink implements LinkInfo { + SpanContext spanContext; + Attributes attributes; } diff --git a/money-core/src/main/java/com/comcast/money/core/CoreSpanInfo.java b/money-core/src/main/java/com/comcast/money/core/CoreSpanInfo.java new file mode 100644 index 00000000..15bfc9b4 --- /dev/null +++ b/money-core/src/main/java/com/comcast/money/core/CoreSpanInfo.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012 Comcast Cable Communications Management, LLC + * + * 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 com.comcast.money.core; + +import java.util.List; +import java.util.Map; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import lombok.Builder; +import lombok.Value; + +import com.comcast.money.api.EventInfo; +import com.comcast.money.api.InstrumentationLibrary; +import com.comcast.money.api.LinkInfo; +import com.comcast.money.api.Note; +import com.comcast.money.api.SpanId; +import com.comcast.money.api.SpanInfo; + +@Value +@Builder(toBuilder = true) +public class CoreSpanInfo implements SpanInfo { + String appName; + String host; + InstrumentationLibrary library; + SpanId id; + String name; + SpanKind kind; + List links; + long startTimeNanos; + long endTimeNanos; + boolean hasEnded; + long durationNanos; + StatusCode status; + String description; + Map> notes; + List events; + + @Override + public boolean isRecording() { + return true; + } +} diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala index e249b088..49d93742 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala @@ -78,20 +78,23 @@ private[core] case class CoreSpan( } override def info(): SpanInfo = - CoreSpanInfo( - id = id, - name = name, - kind = kind, - library = library, - startTimeNanos = startTimeNanos, - endTimeNanos = endTimeNanos, - hasEnded = ended.get(), - durationNanos = calculateDurationNanos, - status = status, - description = description, - notes = noted.toMap[String, Note[_]].asJava, - events = events.asJava, - links = links.asJava) + CoreSpanInfo.builder() + .appName(Money.Environment.applicationName) + .host(Money.Environment.hostName) + .library(library) + .id(id) + .name(name) + .kind(kind) + .links(links.asJava) + .startTimeNanos(startTimeNanos) + .endTimeNanos(endTimeNanos) + .hasEnded(ended.get()) + .durationNanos(calculateDurationNanos) + .status(status) + .description(description) + .notes(noted.toMap[String, Note[_]].asJava) + .events(events.asJava) + .build() override def close(): Unit = end() diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala deleted file mode 100644 index 19a2ce7f..00000000 --- a/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012 Comcast Cable Communications Management, LLC - * - * 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 com.comcast.money.core - -import java.util.Collections -import com.comcast.money.api.{ EventInfo, InstrumentationLibrary, LinkInfo, Note, SpanId, SpanInfo } -import io.opentelemetry.api.trace.{ Span, SpanKind, StatusCode } - -private[core] case class CoreSpanInfo( - id: SpanId, - name: String, - kind: SpanKind = SpanKind.INTERNAL, - override val isRecording: Boolean = true, - startTimeNanos: Long = 0L, - endTimeNanos: Long = 0L, - override val hasEnded: Boolean = false, - durationNanos: Long = 0L, - status: StatusCode = StatusCode.UNSET, - description: String = "", - override val notes: java.util.Map[String, Note[_]] = Collections.emptyMap(), - override val events: java.util.List[EventInfo] = Collections.emptyList(), - override val links: java.util.List[LinkInfo] = Collections.emptyList(), - library: InstrumentationLibrary = Money.InstrumentationLibrary, - appName: String = Money.Environment.applicationName, - host: String = Money.Environment.hostName) extends SpanInfo \ No newline at end of file diff --git a/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala b/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala index 421bcc59..de9eb3dd 100644 --- a/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala +++ b/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala @@ -31,7 +31,7 @@ private[core] final case class UnrecordedSpan( private var scopes: List[Scope] = Nil override def id(): SpanId = spanId - override def info(): SpanInfo = CoreSpanInfo(spanId, name) + override def info(): SpanInfo = CoreSpanInfo.builder().id(spanId).name(name).build() override def getSpanContext: SpanContext = spanId.toSpanContext override def attachScope(scope: Scope): Span = { diff --git a/money-core/src/main/scala/com/comcast/money/core/japi/JMoney.java b/money-core/src/main/scala/com/comcast/money/core/japi/JMoney.java deleted file mode 100644 index a03a79be..00000000 --- a/money-core/src/main/scala/com/comcast/money/core/japi/JMoney.java +++ /dev/null @@ -1,621 +0,0 @@ -/* - * Copyright 2012 Comcast Cable Communications Management, LLC - * - * 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 com.comcast.money.core.japi; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.context.Scope; - -import com.comcast.money.api.Note; -import com.comcast.money.api.Span; -import com.comcast.money.api.SpanBuilder; -import com.comcast.money.core.Money; -import com.comcast.money.core.Tracer; - -/** - * Java API for working directly with money from Java code - */ -public class JMoney { - private static Tracer tracer = Money.Environment().tracer(); - - private JMoney() { } - - public static boolean isEnabled() { - - return Money.Environment().enabled(); - } - - /** - * @return the {@link com.comcast.money.core.Tracer} to use for tracing - */ - public static Tracer tracer() { - return tracer; - } - - protected static void setTracer(Tracer tracer) { - if (tracer != null) { - JMoney.tracer = tracer; - } else { - JMoney.tracer = Money.Environment().tracer(); - } - } - - /** - * Records a note in the current trace span if one is present - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - */ - public static void record(String noteName, Double value) { - - tracer().record(noteName, value, false); - } - - /** - * Records a note in the current trace span if one is present - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - * @param propagate whether or not to propagate note to children spans - */ - public static void record(String noteName, Double value, boolean propagate) { - - tracer().record(noteName, value, propagate); - } - - /** - * Records a note in the current trace span if one is present - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - */ - public static void record(String noteName, String value) { - - tracer().record(noteName, value, false); - } - - /** - * Records a note in the current trace span if one is present - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - * @param propagate whether or not to propagate note to children spans - */ - public static void record(String noteName, String value, boolean propagate) { - - tracer().record(noteName, value, propagate); - } - - /** - * Records a note in the current trace span if one is present - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - */ - public static void record(String noteName, Long value) { - - tracer().record(noteName, value, false); - } - - /** - * Records a note in the current trace span if one is present - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - * @param propagate whether or not to propagate note to children spans - */ - public static void record(String noteName, Long value, boolean propagate) { - - tracer().record(noteName, value, propagate); - } - - /** - * Records a note in the current trace span if one is present - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - */ - public static void record(String noteName, boolean value) { - - tracer().record(noteName, value, false); - } - - /** - * Records a note in the current trace span if one is present - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - * @param propagate whether or not to propagate note to children spans - */ - public static void record(String noteName, boolean value, boolean propagate) { - - tracer().record(noteName, value, propagate); - } - - /** - * Records a note in the current trace span if one is present. If the Object value that is provided - * is null; or if it is a type other than String, Long, Double, or Boolean, then we record a - * String Note, and perform a "toString" on the value. - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - * @param propagate whether or not to propagate the note to child spans - */ - public static void record(String noteName, Object value, boolean propagate) { - - tracer().record(toNote(noteName, value, propagate)); - } - - /** - * Records a note in the current trace span if one is present. If the Object value that is provided - * is null; or if it is a type other than String, Long, Double, or Boolean, then we record a - * String Note, and perform a "toString" on the value. - * @param noteName The name of the note to record - * @param value The value to be recorded on the note - */ - public static void record(String noteName, Object value) { - - record(noteName, value, false); - } - - /** - * Records a note in the current trace span if one is present. - * @param key The name and type of the attribute to record - * @param value The value to be recorded on the note - */ - public static void record(AttributeKey key, T value) { - tracer().record(Note.of(key, value)); - } - - /** - * Records a note in the current trace span if one is present. - * @param key The name and type of the attribute to record - * @param value The value to be recorded on the note - * @param propagate whether or not to propagate note to children spans - */ - public static void record(AttributeKey key, T value, boolean propagate) { - tracer().record(Note.of(key, value, propagate)); - } - - /** - * Records a note in the current trace span if one is present - * - * This will store the current timestamp as the value of the note - * @param noteName The name of the note to record - */ - public static void record(String noteName) { - - tracer().time(noteName); - } - - /** - * Starts a timer that will be recorded on the current span - * - * You should call {@link Scope#close()} stop the timer. - * - * If you do not stop the timer, then the duration of the timer will be whenever - * the current Span is stopped - * @param noteName The name of the note that will be recorded for the timer - */ - public static Scope startTimer(String noteName) { - - return tracer().startTimer(noteName); - } - - /** - * Stops a timer that was previously started on the current span. - * - * This will add a note to the span with the name provided. The value will be - * the duration between the startTimer and stopTimer invocations. - * @param noteName The name of the timer to stop - * @deprecated Close the {@link Scope} returned by {@link JMoney#startTimer(String)} - */ - @Deprecated - public static void stopTimer(String noteName) { - - tracer().stopTimer(noteName); - } - - /** - * Starts a new span with the given name. - * - * If a span already exists, the new span will be a child span of the existing span. - * - * If no span exists, the new span will be a root span - * - * Use this method carefully, you must be certain to stop the span. If you do not stop the span, - * it will eventually timeout and no data will be recorded. Under heavy volume, this will - * add memory pressure to the process - * - * @param spanName The name of the span - * @return the created span - */ - public static Span startSpan(String spanName) { - - return tracer().startSpan(spanName); - } - - /** - * Creates a new {@link SpanBuilder} for configuring and creating a new span with the given name. - * - * If a span already exists, the new span will be a child span of the existing span. - * - * If no span exists, the new span will be a root span - * - * @param spanName The name of the span - * @return the {@link SpanBuilder} - */ - public static SpanBuilder spanBuilder(String spanName) { - - return tracer().spanBuilder(spanName); - } - - /** - * Stops the existing span if one is present. - * - * Once the span is stopped, the data for the span will be emitted - * @param success An indicator of whether or not the span was successful - * @deprecated Close the {@lin Scope} returned from {@link JMoney#startSpan(String)} - */ - @Deprecated - public static void stopSpan(boolean success) { - - tracer().stopSpan(success); - } - - /** - * Stops the existing span if one is present. Assumes a result of success - * @deprecated Close the {@lin Scope} returned from {@link JMoney#startSpan(String)} - */ - @Deprecated - public static void stopSpan() { - tracer().stopSpan(true); - } - - /** - * Creates a new span that can be used in Java that is closeable. Once the try - * block completes the span will automatically be stopped. - * - *

-     *     try (JMoney.TraceSpan span = JMoney.newSpan("some-name")) {
-     *         JMoney.record("some", 1);
-     *     }
-     * 
- *
-     *     try (JMoney.TraceSpan span = JMoney.newSpan("some-name")) {
-     *         try {
-     *             // do something here
-     *         } catch (Exception exception) {
-     *             span.fail();
-     *             // log exception here
-     *         }
-     *     }
-     * 
- * @param spanName The name of the span - * @return a {@link com.comcast.money.core.japi.JMoney.TraceSpan} that really can only be closed to stop the span - */ - public static TraceSpan newSpan(String spanName) { - - return newSpan(spanName, true); - } - - /** - * Creates a new span that can be used in Java that is closeable. Once the try - * block completes the span will automatically be stopped with the specified - * success indicator unless changed on the returned span. - * - *
-     *     try (JMoney.TraceSpan span = JMoney.newSpan("some-name", false)) {
-     *         JMoney.record("some, 1);
-     *         span.success();
-     *     }
-     * 
- * @param spanName The name of the span - * @param success An indicator of whether or not the span was successful by default - * @return a {@link com.comcast.money.core.japi.JMoney.TraceSpan} that really can only be closed to stop the span - */ - public static TraceSpan newSpan(String spanName, boolean success) { - startSpan(spanName); - return new TraceSpan(success); - } - - /** - * Starts a new timer that can be used in Java that is closeable. Once the try block - * completes the timer will automatically be stopped. - * - *
-     *     try (JMoney.TraceTimer timer = JMoney.newTimer("timer-name")) {
-     *         // do something here
-     *     }
-     * 
- * @param timerName The name of the timer - * @return a {@link com.comcast.money.core.japi.JMoney.TraceTimer} that really can only be closed to stop the timer - */ - public static TraceTimer newTimer(String timerName) { - - startTimer(timerName); - return new TraceTimer(timerName); - } - - /** - * Creates a span to measure the computation of a task which may throw an exception - * - *
-     *     JMoney.trace("span-name", () -> {
-     *         // do something here
-     *     });
-     * 
- * @param spanName The name of the span - * @param runnable The task to complete - * @param the type of checked exception - * @throws E if unable to complete the task - */ - public static void trace(String spanName, CheckedRunnable runnable) throws E { - try (TraceSpan span = newSpan(spanName, false)) { - runnable.run(); - span.success(); - } - } - - /** - * Creates a span to measure the computation of a task which may throw an exception - * - *
-     *     JMoney.trace("span-name", span -> {
-     *         try {
-     *             // do something here
-     *         } catch (Exception exception) {
-     *             span.fail();
-     *         }
-     *     });
-     * 
- * @param spanName The name of the span - * @param consumer The task to complete - * @param the type of checked exception - * @throws E if unable to complete the task - */ - public static void trace(String spanName, CheckedConsumer consumer) throws E { - try (TraceSpan span = newSpan(spanName)) { - try { - consumer.accept(span); - } catch (Exception exception) { - span.fail(); - @SuppressWarnings("unchecked") - E checked = (E) exception; - throw checked; - } - } - } - - /** - * Creates a span to measure the computation of a task which returns a result and may throw an exception - * - *
-     *     String value = JMoney.trace("span-name", () -> {
-     *         // do something here
-     *         return "Result";
-     *     });
-     * 
- * @param spanName The name of the span - * @param callable The task to compute the result - * @param the type of the result - * @param the type of checked exception - * @return the computed result of the task - * @throws E if unable to complete the task - */ - public static V trace(String spanName, CheckedCallable callable) throws E { - try (TraceSpan span = newSpan(spanName, false)) { - V result = callable.call(); - span.success(); - return result; - } - } - - /** - * Creates a span to measure the computation of a task which returns a result and may throw an exception - * - *
-     *     String value = JMoney.trace("span-name", span -> {
-     *         String result = calculateSomeResult();
-     *         if (result == null) {
-     *             span.fail();
-     *         }
-     *         return result;
-     *     });
-     * 
- * @param spanName The name of the span - * @param function The task to compute the result - * @param the type of the result - * @param the type of checked exception - * @return the computed result of the task - * @throws E if unable to complete the task - */ - public static V trace(String spanName, CheckedFunction function) throws E { - try (TraceSpan span = newSpan(spanName)) { - try { - return function.apply(span); - } catch (Exception exception) { - span.fail(); - @SuppressWarnings("unchecked") - E checked = (E) exception; - throw checked; - } - } - } - - /** - * Creates a timer to measure the duration of a task - * - *
-     *     JMoney.time("timer-name", () -> {
-     *         // do something here
-     *     });
-     * 
- * @param timerName The name of the timer - * @param runnable The task to be timed - * @param the type of checked exception - * @throws E if unable to complete the task - */ - public static void time(String timerName, CheckedRunnable runnable) throws E { - try (TraceTimer timer = newTimer(timerName)) { - runnable.run(); - } - } - - /** - * Creates a timer to measure the duration of a task that computes a result - * - *
-     *     String result = JMoney.time("timer-name", () -> {
-     *         // do something here
-     *         return "Result";
-     *     });
-     * 
- * @param timerName The name of the timer - * @param callable The task to be timed - * @param the type of the result - * @param the type of checked exception - * @return the computed result of the task - * @throws E if unable to complete the task - */ - public static V time(String timerName, CheckedCallable callable) throws E { - try (TraceTimer timer = newTimer(timerName)) { - return callable.call(); - } - } - - /** - * A {@link java.lang.AutoCloseable} that has a close method that will stop the current span - */ - public static class TraceSpan implements AutoCloseable { - - private boolean success; - private boolean stopped = false; - - public TraceSpan() { - this(true); - } - - public TraceSpan(boolean success) { - this.success = success; - } - - public void fail() { - success = false; - } - - public void success() { - success = true; - } - - @Override public void close() { - if (!stopped) { - stopped = true; - stopSpan(success); - } - } - } - - /** - * A {@link java.lang.AutoCloseable} that has a close method that will stop the timer - */ - public static class TraceTimer implements AutoCloseable { - - private final String noteName; - private boolean stopped = false; - - TraceTimer(String noteName) { - this.noteName = noteName; - } - - @Override - public void close() { - if (!stopped) { - stopped = true; - stopTimer(noteName); - } - } - } - - /** - * A task that returns a result and may throw an exception. - * @param the type of the result - * @param the type of checked exception - */ - @FunctionalInterface - public interface CheckedCallable { - /** - * Computes a result or throws a checked exception - * @return the computed result - * @throws E if unable to compute a result - */ - V call() throws E; - } - - /** - * A task that does not return a result and may throw an exception - * @param the type of checked exception - */ - @FunctionalInterface - public interface CheckedRunnable { - /** - * Runs a task or throws a checked exception - * @throws E if unable to complete the task - */ - void run() throws E; - } - - /** - * A task that accepts a single input, does not return a result and may throw an exception - * @param the type of the input to the task - * @param the type of checked exception - */ - @FunctionalInterface - public interface CheckedConsumer { - /** - * Runs a task with the accepted input - * @param value the input argument - * @throws E if unable to complete the task - */ - void accept(V value) throws E; - } - - /** - * A task that accepts a single input, returns a result and may throw an exception - * @param the type of the input to the task - * @param the type of the result - * @param the type of checked exception - */ - @FunctionalInterface - public interface CheckedFunction { - /** - * Runs a task with the accepted input and computes a result - * @param value the input argument - * @return the computed result - * @throws E if unable to complete the task - */ - R apply(V value) throws E; - } - - private static Note toNote(String name, Object value, boolean propagate) { - - if (value == null) { - return Note.of(name, null, propagate); - } else if (value instanceof Boolean) { - Boolean bool = (Boolean)value; - return Note.of(name, bool, propagate); - } else if (value instanceof Double) { - Double dbl = (Double)value; - return Note.of(name, dbl, propagate); - } else if (value instanceof Long) { - Long lng = (Long)value; - return Note.of(name, lng, propagate); - } else { - return Note.of(name, value.toString(), propagate); - } - } -} diff --git a/money-core/src/test/scala/com/comcast/money/core/CoreSpanInfoSpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanInfoSpec.scala deleted file mode 100644 index ca49c270..00000000 --- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanInfoSpec.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012 Comcast Cable Communications Management, LLC - * - * 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 com.comcast.money.core - -import com.comcast.money.api.SpanId -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpec - -class CoreSpanInfoSpec extends AnyWordSpec with Matchers { - - "CoreSpanInfo" should { - "have acceptable default values" in { - val spanId = SpanId.createNew() - val underTest = CoreSpanInfo(spanId, "test") - - underTest.id shouldBe spanId - underTest.name shouldBe "test" - underTest.appName shouldBe Money.Environment.applicationName - underTest.host shouldBe Money.Environment.hostName - underTest.library shouldBe Money.InstrumentationLibrary - underTest.notes shouldBe empty - underTest.success shouldBe null - underTest.durationMicros shouldBe 0L - underTest.startTimeMicros shouldBe 0L - underTest.startTimeMillis shouldBe 0L - underTest.endTimeMicros shouldBe 0L - underTest.endTimeMillis shouldBe 0L - } - } -} diff --git a/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala index 016f7c8a..4ea28fd0 100644 --- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala @@ -124,7 +124,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS val event = underTest.info.events.get(0) event.name shouldBe "event" event.attributes should have size 0 - event.timestamp should not be 0L + event.timestampNanos should not be 0L event.exception should be(null) } @@ -137,7 +137,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS val event = underTest.info.events.get(0) event.name shouldBe "event" event.attributes should have size 0 - event.timestamp shouldBe 100L + event.timestampNanos shouldBe 100L event.exception should be(null) } @@ -151,7 +151,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS val event = underTest.info.events.get(0) event.name shouldBe "event" event.attributes should have size 0 - event.timestamp shouldBe instant.toEpochNano + event.timestampNanos shouldBe instant.toEpochNano event.exception should be(null) } @@ -178,7 +178,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS event.name shouldBe "event" event.attributes should have size 1 event.attributes.get(AttributeKey.stringKey("foo")) shouldBe "bar" - event.timestamp shouldBe 100L + event.timestampNanos shouldBe 100L event.exception should be(null) } @@ -193,7 +193,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS event.name shouldBe "event" event.attributes should have size 1 event.attributes.get(AttributeKey.stringKey("foo")) shouldBe "bar" - event.timestamp shouldBe instant.toEpochNano + event.timestampNanos shouldBe instant.toEpochNano event.exception should be(null) } @@ -210,7 +210,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS event.attributes.get(SemanticAttributes.EXCEPTION_TYPE) shouldBe "java.lang.RuntimeException" event.attributes.get(SemanticAttributes.EXCEPTION_MESSAGE) shouldBe "BOOM" event.attributes.get(SemanticAttributes.EXCEPTION_STACKTRACE) should startWith("java.lang.RuntimeException: BOOM") - event.timestamp should not be 0 + event.timestampNanos should not be 0 event.exception shouldBe exception } @@ -228,7 +228,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS event.attributes.get(SemanticAttributes.EXCEPTION_MESSAGE) shouldBe "BOOM" event.attributes.get(SemanticAttributes.EXCEPTION_STACKTRACE) should startWith("java.lang.RuntimeException: BOOM") event.attributes.get(AttributeKey.stringKey("foo")) shouldBe "bar" - event.timestamp should not be 0 + event.timestampNanos should not be 0 event.exception shouldBe exception } diff --git a/money-core/src/test/scala/com/comcast/money/core/handlers/MetricsHandlerSpec.scala b/money-core/src/test/scala/com/comcast/money/core/handlers/MetricsHandlerSpec.scala index aab5681e..a40c4855 100644 --- a/money-core/src/test/scala/com/comcast/money/core/handlers/MetricsHandlerSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/handlers/MetricsHandlerSpec.scala @@ -17,6 +17,7 @@ package com.comcast.money.core.handlers import com.codahale.metrics.{ Histogram, Meter, MetricRegistry } +import com.comcast.money.core.CoreSpanInfo import com.typesafe.config.Config import io.opentelemetry.api.trace.StatusCode import org.mockito.Mockito._ @@ -61,7 +62,7 @@ class MetricsHandlerSpec extends AnyWordSpec with Matchers with MockitoSugar wit when(underTest.metricRegistry.histogram(anyString())).thenReturn(latencyMetric) when(underTest.metricRegistry.meter(anyString())).thenReturn(errorMetric) - underTest.handle(testSpanInfo.copy(status = StatusCode.ERROR)) + underTest.handle(testSpanInfo.toBuilder.status(StatusCode.ERROR).build()) verify(latencyMetric).update(testSpanInfo.durationMicros) verify(errorMetric).mark() diff --git a/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala b/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala index 547f7b85..bf133354 100644 --- a/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala @@ -38,29 +38,31 @@ class SpanLogFormatterSpec extends AnyWordSpec with Matchers { val spanLogFormatter = SpanLogFormatter(emitterConf) val spanId = SpanId.createNew() - val sampleData = CoreSpanInfo( - id = spanId, - startTimeNanos = 1000000L, - endTimeNanos = 26000000L, - durationNanos = 35000000L, - hasEnded = true, - name = "key", - appName = "unknown", - host = "host", - notes = Map[String, Note[_]]("bob" -> Note.of("bob", "craig"), "what" -> Note.of("what", 1L), "when" -> Note.of("when", 2L)).asJava, - status = StatusCode.OK) + val sampleData = CoreSpanInfo.builder() + .id(spanId) + .startTimeNanos(1000000L) + .endTimeNanos(26000000L) + .durationNanos(35000000L) + .hasEnded(true) + .name("key") + .appName("unknown") + .host("host") + .notes(Map[String, Note[_]]("bob" -> Note.of("bob", "craig"), "what" -> Note.of("what", 1L), "when" -> Note.of("when", 2L)).asJava) + .status(StatusCode.OK) + .build() - val withNull = CoreSpanInfo( - id = spanId, - startTimeNanos = 1000000L, - endTimeNanos = 26000000L, - durationNanos = 35000000L, - hasEnded = true, - name = "key", - appName = "unknown", - host = "host", - notes = Map[String, Note[_]]("empty" -> Note.of("empty", null)).asJava, - status = StatusCode.OK) + val withNull = CoreSpanInfo.builder() + .id(spanId) + .startTimeNanos(1000000L) + .endTimeNanos(26000000L) + .durationNanos(35000000L) + .hasEnded(true) + .name("key") + .appName("unknown") + .host("host") + .notes(Map[String, Note[_]]("empty" -> Note.of("empty", null)).asJava) + .status(StatusCode.OK) + .build() "A LogEmitter must" must { "have a correctly formatted message" in { diff --git a/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala b/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala index 8bb4780d..d82299ce 100644 --- a/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala +++ b/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala @@ -42,18 +42,19 @@ trait TestData { val clock: Clock = SystemClock - val testSpanInfo = CoreSpanInfo( - id = SpanId.createNew(), - startTimeNanos = clock.now, - endTimeNanos = clock.now, - durationNanos = 123456000L, - hasEnded = true, - status = StatusCode.OK, - name = "test-span", - appName = "test", - host = "localhost", - notes = Map[String, Note[_]]("str" -> testStringNote, "lng" -> testLongNote, "dbl" -> testDoubleNote, "bool" -> testBooleanNote).asJava, - events = Collections.emptyList()) + val testSpanInfo = CoreSpanInfo.builder() + .id(SpanId.createNew()) + .startTimeNanos(clock.now) + .endTimeNanos(clock.now) + .durationNanos(123456000L) + .hasEnded(true) + .status(StatusCode.OK) + .name("test-span") + .appName("test") + .host("localhost") + .notes(Map[String, Note[_]]("str" -> testStringNote, "lng" -> testLongNote, "dbl" -> testDoubleNote, "bool" -> testBooleanNote).asJava) + .events(Collections.emptyList()) + .build() val testSpanId = SpanId.createNew() val testSpan = CoreSpan(testSpanId, "test-span") @@ -61,16 +62,17 @@ trait TestData { val childSpan = CoreSpan(childSpanId, "child-span") val fixedTestSpanId = SpanId.createRemote("5092ddfe-3701-4f84-b3d2-21f5501c0d28", 5176425846116696835L, 5176425846116696835L, TraceFlags.getSampled, TraceState.getDefault) - val fixedTestSpanInfo = CoreSpanInfo( - id = fixedTestSpanId, - startTimeNanos = 100000000L, - endTimeNanos = 300000000L, - durationNanos = 200000L, - hasEnded = true, - status = StatusCode.OK, - name = "test-span", - appName = "test", - host = "localhost", - notes = Map[String, Note[_]]("str" -> testStringNote, "lng" -> testLongNote, "dbl" -> testDoubleNote, "bool" -> testBooleanNote).asJava, - events = Collections.emptyList()) + val fixedTestSpanInfo = CoreSpanInfo.builder() + .id(fixedTestSpanId) + .startTimeNanos(100000000L) + .endTimeNanos(300000000L) + .durationNanos(200000L) + .hasEnded(true) + .status(StatusCode.OK) + .name("test-span") + .appName("test") + .host("localhost") + .notes(Map[String, Note[_]]("str" -> testStringNote, "lng" -> testLongNote, "dbl" -> testDoubleNote, "bool" -> testBooleanNote).asJava) + .events(Collections.emptyList()) + .build() } diff --git a/money-core/src/test/scala/com/comcast/money/core/japi/JMoneySpec.scala b/money-core/src/test/scala/com/comcast/money/core/japi/JMoneySpec.scala deleted file mode 100644 index a27cb559..00000000 --- a/money-core/src/test/scala/com/comcast/money/core/japi/JMoneySpec.scala +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright 2012 Comcast Cable Communications Management, LLC - * - * 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 com.comcast.money.core.japi - -import com.comcast.money.api.{ Note, Span, SpanFactory, SpanId } -import com.comcast.money.core.handlers.TestData -import com.comcast.money.core.Tracer -import com.comcast.money.core.internal.SpanLocal -import com.comcast.money.core.japi.JMoney._ -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers._ -import org.mockito.Mockito._ -import org.scalatest._ -import org.scalatest.wordspec.AnyWordSpec -import org.scalatest.matchers.should.Matchers -import org.scalatestplus.mockito.MockitoSugar - -class JMoneySpec extends AnyWordSpec - with Matchers with MockitoSugar with TestData with BeforeAndAfterEach with OneInstancePerTest { - - val tracer = mock[Tracer] - - override def beforeEach() = { - SpanLocal.clear() - reset(tracer) - JMoney.setTracer(tracer) - } - - override def afterEach() = { - SpanLocal.clear() - JMoney.setTracer(null) - } - - "Java API" should { - "starts a span" in { - JMoney.startSpan("java-span") - verify(tracer).startSpan("java-span") - } - "stops a span" in { - JMoney.stopSpan(true) - verify(tracer).stopSpan(true) - } - "support closeable" in { - val span: TraceSpan = JMoney.newSpan("closeable") - span.close() - span.close() - - verify(tracer).startSpan("closeable") - verify(tracer, times(1)).stopSpan(true) - } - "support failed closeable" in { - val span: TraceSpan = JMoney.newSpan("closeable") - span.fail() - span.close() - span.close() - - verify(tracer).startSpan("closeable") - verify(tracer, times(1)).stopSpan(false) - } - "support closeable with failed default" in { - val span: TraceSpan = JMoney.newSpan("closeable", false) - span.close() - span.close() - - verify(tracer).startSpan("closeable") - verify(tracer, times(1)).stopSpan(false) - } - "support successful closeable with failed default" in { - val span: TraceSpan = JMoney.newSpan("closeable", false) - span.success() - span.close() - span.close() - - verify(tracer).startSpan("closeable") - verify(tracer, times(1)).stopSpan(true) - } - "support runnable lambda" in { - val runnable = mock[CheckedRunnable[Exception]] - JMoney.trace("runnable", runnable) - - verify(tracer).startSpan("runnable") - verify(runnable).run() - verify(tracer, times(1)).stopSpan(true) - } - "support throwing runnable lambda" in { - val runnable = mock[CheckedRunnable[Exception]] - doThrow(new Exception()).when(runnable).run() - - assertThrows[Exception] { - JMoney.trace("runnable", runnable) - } - - verify(tracer).startSpan("runnable") - verify(runnable).run() - verify(tracer, times(1)).stopSpan(false) - } - "support consumer lambda with span" in { - val consumer = mock[CheckedConsumer[TraceSpan, Exception]] - - JMoney.trace("consumer", consumer) - - verify(tracer).startSpan("consumer") - verify(consumer).accept(any()) - verify(tracer).stopSpan(true) - } - "support consumer lambda with failed span" in { - val consumer: CheckedConsumer[TraceSpan, Exception] = span => { span.fail() } - - JMoney.trace("consumer", consumer) - - verify(tracer).startSpan("consumer") - verify(tracer).stopSpan(false) - } - "support throwing consumer lambda" in { - val consumer = mock[CheckedConsumer[TraceSpan, Exception]] - doThrow(new Exception()).when(consumer).accept(any()) - - assertThrows[Exception] { - JMoney.trace("consumer", consumer) - } - - verify(tracer).startSpan("consumer") - verify(consumer).accept(any()) - verify(tracer).stopSpan(false) - } - "support callable lambda" in { - val callable = mock[CheckedCallable[String, Exception]] - when(callable.call()).thenReturn("Hello") - val result = JMoney.trace("callable", callable) - - result shouldBe "Hello" - - verify(tracer).startSpan("callable") - verify(callable).call() - verify(tracer).stopSpan(true) - } - "support throwing callable lambda" in { - val callable = mock[CheckedCallable[String, Exception]] - doThrow(new Exception()).when(callable).call() - assertThrows[Exception] { - JMoney.trace("callable", callable) - } - - verify(tracer).startSpan("callable") - verify(callable).call - verify(tracer).stopSpan(false) - } - "support function lambda with span" in { - val function = mock[CheckedFunction[TraceSpan, String, Exception]] - when(function.apply(any())).thenReturn("Hello") - - val result = JMoney.trace("function", function) - - result shouldBe "Hello" - - verify(tracer).startSpan("function") - verify(function).apply(any()) - verify(tracer).stopSpan(true) - } - "support function lambda with failed span" in { - val function: CheckedFunction[TraceSpan, String, Exception] = span => { - span.fail() - "Hello" - } - - val result = JMoney.trace("function", function) - - result shouldBe "Hello" - - verify(tracer).startSpan("function") - verify(tracer).stopSpan(false) - } - "support throwing function lambda" in { - val function = mock[CheckedFunction[TraceSpan, String, Exception]] - doThrow(new Exception()).when(function).apply(any()) - - assertThrows[Exception] { - JMoney.trace("function", function) - } - - verify(tracer).startSpan("function") - verify(function).apply(any()) - verify(tracer).stopSpan(false) - } - "start a timer" in { - JMoney.startTimer("the-timer") - verify(tracer).startTimer("the-timer") - } - "stop a timer" in { - JMoney.stopTimer("the-timer") - verify(tracer).stopTimer("the-timer") - } - "support closeable timer" in { - val timer: TraceTimer = JMoney.newTimer("the-timer") - timer.close() - timer.close() - - verify(tracer).startTimer("the-timer") - verify(tracer, times(1)).stopTimer("the-timer") - } - "support runnable timer lambda" in { - val runnable = mock[CheckedRunnable[Exception]] - - JMoney.time("the-timer", runnable) - - verify(tracer).startTimer("the-timer") - verify(runnable).run() - verify(tracer).stopTimer("the-timer") - } - "support throwing runnable timer lambda" in { - val runnable = mock[CheckedRunnable[Exception]] - doThrow(new Exception()).when(runnable).run() - - assertThrows[Exception] { - JMoney.time("the-timer", runnable) - } - - verify(tracer).startTimer("the-timer") - verify(runnable).run() - verify(tracer).stopTimer("the-timer") - } - "support callable timer lambda" in { - val callable = mock[CheckedCallable[String, Exception]] - when(callable.call()).thenReturn("Hello") - - val result = JMoney.time("the-timer", callable) - - result shouldBe "Hello" - - verify(tracer).startTimer("the-timer") - verify(callable).call() - verify(tracer).stopTimer("the-timer") - } - "support throwing callable timer lambda" in { - val callable = mock[CheckedCallable[String, Exception]] - doThrow(new Exception()).when(callable).call() - - assertThrows[Exception] { - JMoney.time("the-timer", callable) - } - - verify(tracer).startTimer("the-timer") - verify(callable).call() - verify(tracer).stopTimer("the-timer") - } - "record long values" in { - JMoney.record("the-long", java.lang.Long.valueOf(1L)) - verify(tracer).record("the-long", java.lang.Long.valueOf(1L), false) - } - "record propagate-able long values" in { - JMoney.record("the-long", java.lang.Long.valueOf(1L), true) - verify(tracer).record("the-long", java.lang.Long.valueOf(1L), true) - } - "record boolean values" in { - JMoney.record("the-bool", true) - verify(tracer).record("the-bool", true, false) - } - "record propagate-able boolean values" in { - JMoney.record("the-bool", true, true) - verify(tracer).record("the-bool", true, true) - } - "record double values" in { - JMoney.record("the-double", java.lang.Double.valueOf(3.14)) - verify(tracer).record("the-double", java.lang.Double.valueOf(3.14), false) - } - "record propagate-able double values" in { - JMoney.record("the-double", java.lang.Double.valueOf(3.14), true) - verify(tracer).record("the-double", java.lang.Double.valueOf(3.14), true) - } - "record string values" in { - JMoney.record("the-string", "yo") - verify(tracer).record("the-string", "yo", false) - } - "record propagate-able string values" in { - JMoney.record("the-string", "yo", true) - verify(tracer).record("the-string", "yo", true) - } - "record a timestamp" in { - JMoney.record("stamp-this") - verify(tracer).time("stamp-this") - } - "record a long as an Object" in { - val lng: java.lang.Long = 100L - val obj: AnyRef = lng - JMoney.record("long", obj) - - val captor = ArgumentCaptor.forClass(classOf[Note[_]]) - verify(tracer).record(captor.capture()) - val note = captor.getValue - note.name shouldBe "long" - note.value shouldBe 100L - } - "record a boolean as an Object" in { - val boo: java.lang.Boolean = true - val obj: AnyRef = boo - JMoney.record("bool", obj) - - val captor = ArgumentCaptor.forClass(classOf[Note[_]]) - verify(tracer).record(captor.capture()) - val note = captor.getValue - note.name shouldBe "bool" - note.value shouldBe true - } - "record a double as an Object" in { - val dbl: java.lang.Double = 3.14 - val obj: AnyRef = dbl - JMoney.record("double", obj) - - val captor = ArgumentCaptor.forClass(classOf[Note[_]]) - verify(tracer).record(captor.capture()) - val note = captor.getValue - note.name shouldBe "double" - note.value shouldBe 3.14 - } - "record a string as an Object" in { - val str: java.lang.String = "hello" - val obj: AnyRef = str - JMoney.record("string", obj) - - val captor = ArgumentCaptor.forClass(classOf[Note[_]]) - verify(tracer).record(captor.capture()) - val note = captor.getValue - note.name shouldBe "string" - note.value shouldBe "hello" - } - "record any object as an Object" in { - val lst = List(1) - JMoney.record("list", lst) - - val captor = ArgumentCaptor.forClass(classOf[Note[_]]) - verify(tracer).record(captor.capture()) - val note = captor.getValue - note.name shouldBe "list" - note.value shouldBe "List(1)" - } - "record null as a string Note with None" in { - val obj: AnyRef = null - JMoney.record("nill", obj) - - val captor = ArgumentCaptor.forClass(classOf[Note[_]]) - verify(tracer).record(captor.capture()) - val note = captor.getValue - val nil: AnyRef = null - note.name shouldBe "nill" - note.value shouldBe nil - } - } -} diff --git a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala index b83dc969..4caa8051 100644 --- a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala +++ b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala @@ -23,6 +23,6 @@ import io.opentelemetry.sdk.trace.data.EventData private[otel] case class MoneyEvent(event: EventInfo) extends EventData { override def getName: String = event.name override def getAttributes: Attributes = event.attributes - override def getEpochNanos: Long = event.timestamp + override def getEpochNanos: Long = event.timestampNanos override def getTotalAttributeCount: Int = event.attributes.size } diff --git a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala index 021c3b43..bac9e0b8 100644 --- a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala +++ b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala @@ -26,7 +26,7 @@ class MoneyEventSpec extends AnyWordSpec with Matchers { val event = new EventInfo { override def name(): String = "name" override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar") - override def timestamp(): Long = 1234567890L + override def timestampNanos(): Long = 1234567890L override def exception(): Throwable = null } @@ -35,7 +35,7 @@ class MoneyEventSpec extends AnyWordSpec with Matchers { val underTest = MoneyEvent(event) underTest.getName shouldBe event.name() - underTest.getEpochNanos shouldBe event.timestamp() + underTest.getEpochNanos shouldBe event.timestampNanos() underTest.getTotalAttributeCount shouldBe 1 underTest.getAttributes shouldBe Attributes.of(AttributeKey.stringKey("foo"), "bar") } diff --git a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala index dd749721..c7729377 100644 --- a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala +++ b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala @@ -104,7 +104,7 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers { val event = new EventInfo { override def name(): String = "event" override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar") - override def timestamp(): Long = 1234567890L + override def timestampNanos(): Long = 1234567890L override def exception(): Throwable = null } diff --git a/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java b/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java index fe98b060..95c18860 100644 --- a/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java +++ b/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java @@ -50,23 +50,23 @@ public class MoneyClientHttpInterceptorSpec { @Before public void setUp() { Span span = mock(Span.class); - SpanInfo testSpanInfo = new CoreSpanInfo( - id, - "testName", - SpanKind.INTERNAL, - true, - 0L, - 0L, - true, - 0L, - StatusCode.OK, - "", - Collections.emptyMap(), - Collections.emptyList(), - Collections.emptyList(), - new InstrumentationLibrary("test", "0.0.1"), - "testAppName", - "testHost"); + SpanInfo testSpanInfo = CoreSpanInfo.builder() + .id(id) + .name("testName") + .kind(SpanKind.INTERNAL) + .startTimeNanos(0L) + .endTimeNanos(0L) + .hasEnded(true) + .durationNanos(0L) + .status(StatusCode.OK) + .description("") + .notes(Collections.emptyMap()) + .links(Collections.emptyList()) + .events(Collections.emptyList()) + .library(new InstrumentationLibrary("test", "0.0.1")) + .appName("testAppName") + .host("testHost") + .build(); when(span.info()).thenReturn(testSpanInfo); when(span.storeInContext(any())).thenCallRealMethod(); diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 771ae3a8..c622b15c 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -13,6 +13,8 @@ object Dependencies { val openTelemetryInstV = "0.17.0" val openTelemetrySemConvV = "0.17.0-alpha" + val lombok = "org.projectlombok" % "lombok" % "1.18.16" % Provided + val akka = "com.typesafe.akka" %% "akka-actor" % akkaV val akkaStream = "com.typesafe.akka" %% "akka-stream" % akkaV val akkaLog = "com.typesafe.akka" %% "akka-slf4j" % akkaV diff --git a/project/plugins.sbt b/project/plugins.sbt index 00cab406..10b88712 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -20,3 +20,5 @@ addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.10.0-RC1") addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.3") + +addSbtPlugin("com.thoughtworks.sbt" % "delombokjavadoc" % "1.0.0") \ No newline at end of file From 9e4cbc1ee976c73bb1f3210b40acf4b4c0542bfa Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Mon, 22 Feb 2021 07:51:01 -0500 Subject: [PATCH 5/5] Bump Lombok, should no longer need workaround for sbt virtual files --- build.sbt | 2 -- project/Dependencies.scala | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 5b651729..fae63fba 100644 --- a/build.sbt +++ b/build.sbt @@ -368,8 +368,6 @@ def basicSettings = Defaults.itSettings ++ Seq( "-language:existentials", "-language:postfixOps", "-language:reflectiveCalls"), - // temporary to enable Lombok config to work in submodules - javaHome := sys.env.get("JAVA_HOME").map(file), javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), javacOptions in doc := Seq("-source", "1.8"), scalariformAutoformat := true, diff --git a/project/Dependencies.scala b/project/Dependencies.scala index c622b15c..9bc65530 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -13,7 +13,7 @@ object Dependencies { val openTelemetryInstV = "0.17.0" val openTelemetrySemConvV = "0.17.0-alpha" - val lombok = "org.projectlombok" % "lombok" % "1.18.16" % Provided + val lombok = "org.projectlombok" % "lombok" % "1.18.18" % Provided val akka = "com.typesafe.akka" %% "akka-actor" % akkaV val akkaStream = "com.typesafe.akka" %% "akka-stream" % akkaV