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..7c26614e 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,16 +32,11 @@ */ 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); + void end(boolean result); @Override Span setAttribute(String key, String value); 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..bef93067 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 { @@ -38,14 +35,14 @@ public interface SpanInfo { /** * @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(); } @@ -165,45 +162,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-core/src/main/scala/com/comcast/money/core/CoreEvent.scala b/money-core/src/main/scala/com/comcast/money/core/CoreEventInfo.scala similarity index 92% rename from money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala rename to money-core/src/main/scala/com/comcast/money/core/CoreEventInfo.scala index d714fa4a..6a614963 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreEventInfo.scala @@ -17,15 +17,15 @@ 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 -private[core] case class CoreEvent( +private[core] case class CoreEventInfo( 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/CoreLinkInfo.scala similarity index 82% rename from money-core/src/main/scala/com/comcast/money/core/CoreLink.scala rename to money-core/src/main/scala/com/comcast/money/core/CoreLinkInfo.scala index 4a9d50fa..15860d86 100644 --- a/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala +++ b/money-core/src/main/scala/com/comcast/money/core/CoreLinkInfo.scala @@ -16,10 +16,10 @@ package com.comcast.money.core -import com.comcast.money.api.SpanInfo +import com.comcast.money.api.{ SpanInfo, LinkInfo } import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.SpanContext -private[core] final case class CoreLink( +private[core] final case class CoreLinkInfo( 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..a301b479 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 @@ -22,11 +22,12 @@ 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.trace.{ SpanContext, SpanKind, StatusCode } import io.opentelemetry.api.common.{ AttributeKey, Attributes } import io.opentelemetry.context.Scope import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import java.util.concurrent.atomic.AtomicBoolean import scala.collection.mutable.ListBuffer /** @@ -40,7 +41,7 @@ private[core] case class CoreSpan( 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, @@ -51,40 +52,39 @@ private[core] case class CoreSpan( private var description: String = _ // use concurrent maps + private val ended = new AtomicBoolean(false) 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 end(): Unit = end(clock.now, StatusCode.UNSET) + override def end(endTimeStamp: Long, unit: TimeUnit): Unit = end(unit.toNanos(endTimeStamp), StatusCode.UNSET) + override def end(result: Boolean): Unit = end(clock.now, if (result) StatusCode.OK else StatusCode.ERROR) - 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 end(endTimeNanos: Long, status: StatusCode): Unit = + if (ended.compareAndSet(false, true)) { + this.endTimeNanos = endTimeNanos - private def stop(endTimeNanos: Long, status: StatusCode): Unit = { - this.endTimeNanos = endTimeNanos + // process any hanging timers + val openTimers = timers.keys + openTimers.foreach(stopTimer) - // process any hanging timers - val openTimers = timers.keys - openTimers.foreach(stopTimer) + scopes.foreach { + _.close() + } + scopes = Nil - 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 + } - 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()) } - handler.handle(info()) - } - override def stopTimer(timerKey: String): Unit = timers.remove(timerKey) foreach { timerStartInstant => @@ -121,7 +121,7 @@ private[core] case class CoreSpan( events = events.asJava, links = links.asJava) - override def close(): Unit = stop() + override def close(): Unit = end() 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)) @@ -144,7 +144,7 @@ private[core] case class CoreSpan( } private def addEventInternal(eventName: String, eventAttributes: Attributes, timestampNanos: scala.Long, exception: Throwable = null): Span = { - events += CoreEvent(eventName, eventAttributes, timestampNanos, exception) + events += CoreEventInfo(eventName, eventAttributes, timestampNanos, exception) this } @@ -165,9 +165,6 @@ 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 getSpanContext: SpanContext = id.toSpanContext override def isRecording: Boolean = startTimeNanos > 0 && endTimeNanos <= 0 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..e348d671 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 @@ -18,7 +18,7 @@ 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.{ InstrumentationLibrary, Note, Span, SpanBuilder, SpanHandler, SpanId, SpanInfo, LinkInfo } import com.comcast.money.core.samplers.{ DropResult, RecordResult, Sampler } import io.opentelemetry.api.common.{ AttributeKey, Attributes } import io.opentelemetry.context.Context @@ -40,7 +40,7 @@ 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) @@ -75,7 +75,7 @@ private[core] class CoreSpanBuilder( override def addLink(spanContext: SpanContext): SpanBuilder = addLink(spanContext, Attributes.empty) override def addLink(spanContext: SpanContext, attributes: Attributes): SpanBuilder = { - links = CoreLink(spanContext, attributes) :: links + links = CoreLinkInfo(spanContext, attributes) :: links this } 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..9bf07164 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,8 +17,8 @@ package com.comcast.money.core import java.util.Collections -import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo } -import io.opentelemetry.api.trace.{ Span, SpanKind, StatusCode } +import com.comcast.money.api.{ InstrumentationLibrary, Note, EventInfo, SpanId, SpanInfo, LinkInfo } +import io.opentelemetry.api.trace.{ SpanKind, StatusCode } private[core] case class CoreSpanInfo( id: SpanId, @@ -30,8 +30,8 @@ private[core] case class CoreSpanInfo( 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 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..a3cdfe8d 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 @@ -134,9 +134,7 @@ object DisabledSpanBuilder extends SpanBuilder { object DisabledSpan extends Span { - override def stop(): Unit = () - - override def stop(result: java.lang.Boolean): Unit = () + override def end(result: Boolean): Unit = () override def stopTimer(timerKey: String): Unit = () diff --git a/money-core/src/main/scala/com/comcast/money/core/Money.scala b/money-core/src/main/scala/com/comcast/money/core/Money.scala index 22227355..45176944 100644 --- a/money-core/src/main/scala/com/comcast/money/core/Money.scala +++ b/money-core/src/main/scala/com/comcast/money/core/Money.scala @@ -42,7 +42,7 @@ case class Money( object Money { - val InstrumentationLibrary = new InstrumentationLibrary("money-core", "0.10.0") + val InstrumentationLibrary = new InstrumentationLibrary("money-core", "0.18.0") lazy val Environment: Money = apply(ConfigFactory.load().getConfig("money")) def apply(conf: Config): Money = { 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..67b05bda 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 @@ -46,10 +46,9 @@ 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() + override def end(result: Boolean): Unit = close() + override def end(): Unit = close() + override def end(endTimeStamp: Long, unit: TimeUnit): Unit = close() override def record(note: Note[_]): Span = this override def startTimer(timerKey: String): Scope = () => () 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..8a752f9b 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() result } case Success(result) => - span.stop(true) + span.end() 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/CoreSpanSpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala index 6c7a41dd..8aa98803 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) } @@ -306,7 +306,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS underTest.isRecording shouldBe true - underTest.stop() + underTest.end() underTest.isRecording shouldBe false } @@ -326,7 +326,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 +339,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 +359,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 +386,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 +394,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 +414,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 +434,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/logging/MethodTracerSpec.scala b/money-core/src/test/scala/com/comcast/money/core/logging/MethodTracerSpec.scala index edc5739a..1030c122 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() 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() 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..1a65a517 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.{ SpanInfo, LinkInfo } 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..fb2cd8b1 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,13 +17,14 @@ package com.comcast.money.otel.handlers import java.util -import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo } +import com.comcast.money.api.{ InstrumentationLibrary, Note, EventInfo, SpanId, SpanInfo, LinkInfo } import io.opentelemetry.api.common.{ Attributes, AttributesBuilder } import io.opentelemetry.sdk.common.InstrumentationLibraryInfo import io.opentelemetry.sdk.resources.Resource import io.opentelemetry.sdk.trace.ReadableSpan import io.opentelemetry.sdk.trace.data.{ EventData, LinkData, SpanData, StatusData } -import io.opentelemetry.api.trace.{ SpanContext, SpanKind, TraceState, Span => OtelSpan } +import io.opentelemetry.api.trace.{ SpanContext, SpanKind } +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes.{ HOST_NAME, SERVICE_NAME, TELEMETRY_SDK_LANGUAGE, TELEMETRY_SDK_NAME, TELEMETRY_SDK_VERSION } import scala.collection.JavaConverters._ @@ -35,6 +36,7 @@ private[otel] class MoneyReadableSpanData(info: SpanInfo) extends ReadableSpan w private lazy val attributes = convertAttributes(info.notes) private lazy val events = convertEvents(info.events) private lazy val links = convertLinks(info.links) + private lazy val resource = createResource(info) override def getSpanContext: SpanContext = spanContext override def getParentSpanContext: SpanContext = parentSpanContext @@ -45,7 +47,7 @@ private[otel] class MoneyReadableSpanData(info: SpanInfo) extends ReadableSpan w override def getLatencyNanos: Long = info.durationNanos override def getTraceId: String = id.traceIdAsHex override def getSpanId: String = id.selfIdAsHex - override def getResource: Resource = Resource.getDefault + override def getResource: Resource = resource override def getKind: SpanKind = info.kind override def getStartEpochNanos: Long = info.startTimeNanos override def getLinks: util.List[LinkData] = links @@ -71,6 +73,15 @@ private[otel] class MoneyReadableSpanData(info: SpanInfo) extends ReadableSpan w InstrumentationLibraryInfo.empty } + private def createResource(info: SpanInfo): Resource = + Resource.create(Attributes.builder() + .put(TELEMETRY_SDK_NAME, "money") + .put(TELEMETRY_SDK_LANGUAGE, "java") + .put(TELEMETRY_SDK_VERSION, "0.18.0") + .put(SERVICE_NAME, info.appName) + .put(HOST_NAME, info.host) + .build()) + private def appendNoteToBuilder[T](builder: AttributesBuilder, note: Note[T]): AttributesBuilder = builder.put(note.key, note.value) @@ -81,14 +92,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..098c1d7d 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,12 +20,12 @@ 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.InstrumentationLibrary; import com.comcast.money.api.Note; +import com.comcast.money.api.EventInfo; import com.comcast.money.api.SpanId; import com.comcast.money.api.SpanInfo; @@ -42,7 +42,7 @@ public Map> notes() { } @Override - public List events() { + public List events() { return Collections.emptyList(); } 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..517fa055 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, SpanInfo, LinkInfo } 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..0c1f1c4f 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,11 +18,12 @@ 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.{ IdGenerator, InstrumentationLibrary, Note, EventInfo, SpanId, SpanInfo, LinkInfo } import io.opentelemetry.api.common.{ AttributeKey, Attributes } import io.opentelemetry.sdk.resources.Resource import io.opentelemetry.api.trace.{ Span, SpanContext, SpanKind, StatusCode, TraceFlags, TraceState } import io.opentelemetry.sdk.trace.data.StatusData +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes.{ HOST_NAME, SERVICE_NAME, TELEMETRY_SDK_LANGUAGE, TELEMETRY_SDK_NAME, TELEMETRY_SDK_VERSION } import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec @@ -47,7 +48,6 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers { underTest.hasEnded shouldBe true underTest.getLinks.asScala should contain(MoneyLink(link)) underTest.getTotalRecordedLinks shouldBe 0 - underTest.getResource shouldBe Resource.getDefault underTest.getLatencyNanos shouldBe 2000000L underTest.getStatus shouldBe StatusData.create(StatusCode.OK, "description") underTest.getTotalAttributeCount shouldBe 1 @@ -55,6 +55,12 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers { underTest.getTotalRecordedEvents shouldBe 1 underTest.getEvents.asScala should contain(MoneyEvent(event)) underTest.toSpanData shouldBe underTest + val resource = underTest.getResource + resource.getAttributes.get(TELEMETRY_SDK_NAME) shouldBe "money" + resource.getAttributes.get(TELEMETRY_SDK_LANGUAGE) shouldBe "java" + resource.getAttributes.get(TELEMETRY_SDK_VERSION) shouldBe "0.18.0" + resource.getAttributes.get(SERVICE_NAME) shouldBe "app" + resource.getAttributes.get(HOST_NAME) shouldBe "host" } "wrap child Money SpanInfo" in { @@ -71,7 +77,6 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers { underTest.hasEnded shouldBe true underTest.getLinks.asScala should contain(MoneyLink(link)) underTest.getTotalRecordedLinks shouldBe 0 - underTest.getResource shouldBe Resource.getDefault underTest.getLatencyNanos shouldBe 2000000L underTest.getStatus shouldBe StatusData.create(StatusCode.OK, "description") underTest.getTotalAttributeCount shouldBe 1 @@ -80,6 +85,12 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers { underTest.getEvents.asScala should contain(MoneyEvent(event)) underTest.getLinks.asScala should contain(MoneyLink(link)) underTest.toSpanData shouldBe underTest + val resource = underTest.getResource + resource.getAttributes.get(TELEMETRY_SDK_NAME) shouldBe "money" + resource.getAttributes.get(TELEMETRY_SDK_LANGUAGE) shouldBe "java" + resource.getAttributes.get(TELEMETRY_SDK_VERSION) shouldBe "0.18.0" + resource.getAttributes.get(SERVICE_NAME) shouldBe "app" + resource.getAttributes.get(HOST_NAME) shouldBe "host" } } @@ -95,11 +106,11 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers { override def description(): String = "description" 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 +118,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/TracedMethodInterceptorSpec.java b/money-spring/src/test/java/com/comcast/money/spring/TracedMethodInterceptorSpec.java index aa805d88..95030343 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(); 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/scala/com/comcast/money/wire/SpanConverters.scala b/money-wire/src/main/scala/com/comcast/money/wire/SpanConverters.scala index b430894b..5f5ca72b 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.{ InstrumentationLibrary, Note, EventInfo, SpanId, SpanInfo } import com.comcast.money.core._ import com.comcast.money.wire.avro import com.comcast.money.wire.avro.NoteType @@ -139,7 +139,7 @@ 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 endTimeNanos(): Long = startTimeNanos + durationNanos override def status(): StatusCode = if (from.getSuccess) StatusCode.OK else StatusCode.ERROR diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 771ae3a8..91d73597 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -9,9 +9,9 @@ object Dependencies { val jodaV = "2.9.9" val json4sV = "3.6.10" val typesafeConfigV = "1.3.3" - val openTelemetryV = "0.17.0" - val openTelemetryInstV = "0.17.0" - val openTelemetrySemConvV = "0.17.0-alpha" + val openTelemetryV = "1.0.0" + val openTelemetryInstV = "1.0.0" + val openTelemetrySemConvV = "1.0.0-alpha" val akka = "com.typesafe.akka" %% "akka-actor" % akkaV val akkaStream = "com.typesafe.akka" %% "akka-stream" % akkaV