diff --git a/money-api/src/main/java/com/comcast/money/api/Resource.java b/money-api/src/main/java/com/comcast/money/api/Resource.java new file mode 100644 index 00000000..226fad32 --- /dev/null +++ b/money-api/src/main/java/com/comcast/money/api/Resource.java @@ -0,0 +1,45 @@ +/* + * 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; + +/** + * Represents a resource, which capture identifying information about the entities + * for which traces are reported. + */ +public interface Resource { + /** + * @return the current application name + */ + String applicationName(); + + /** + * @return the host name or ip + */ + String hostName(); + + /** + * @return the tracing library + */ + InstrumentationLibrary library(); + + /** + * @return a map of attributes that describe the resource + */ + Attributes attributes(); +} 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..118efb9b 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 @@ -30,7 +30,7 @@ public interface SpanInfo { /** - * @return a map of all of the notes that were recorded on the span. Implementers should enforce + * @return a map of all the notes that were recorded on the span. Implementers should enforce * that the map returned is a copy of the notes */ Map> notes(); @@ -49,6 +49,11 @@ default List links() { return Collections.emptyList(); } + /** + * @return the resource for this span + */ + Resource resource(); + /** * @return the time in milliseconds when this span was started */ 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 90% 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..c6e29886 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 @@ -17,12 +17,11 @@ 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-core/src/main/scala/com/comcast/money/core/CoreResource.scala b/money-core/src/main/scala/com/comcast/money/core/CoreResource.scala new file mode 100644 index 00000000..4367df4b --- /dev/null +++ b/money-core/src/main/scala/com/comcast/money/core/CoreResource.scala @@ -0,0 +1,26 @@ +/* + * 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.{ InstrumentationLibrary, Resource } +import io.opentelemetry.api.common.Attributes + +private[core] case class CoreResource( + applicationName: String, + hostName: String, + library: InstrumentationLibrary = InstrumentationLibrary.UNKNOWN, + attributes: Attributes = Attributes.empty()) extends Resource {} 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 953fcc88..92e920d2 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 @@ -38,12 +38,12 @@ import scala.collection.mutable.ListBuffer * @param handler The [[SpanHandler]] responsible for processing the span once it is stopped */ private[core] case class CoreSpan( + resource: Resource, id: SpanId, var name: String, kind: SpanKind = SpanKind.INTERNAL, links: List[SpanInfo.Link] = Nil, startTimeNanos: Long = SystemClock.now, - library: InstrumentationLibrary = Money.InstrumentationLibrary, clock: Clock = SystemClock, handler: SpanHandler = DisabledSpanHandler) extends Span { @@ -109,10 +109,10 @@ private[core] case class CoreSpan( override def info(): SpanInfo = CoreSpanInfo( + resource = resource, id = id, name = name, kind = kind, - library = library, startTimeNanos = startTimeNanos, endTimeNanos = endTimeNanos, durationNanos = calculateDurationNanos, 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..c1dd7f8f 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, Resource, Span, SpanBuilder, SpanHandler, SpanId, SpanInfo } import com.comcast.money.core.samplers.{ DropResult, RecordResult, Sampler } import io.opentelemetry.api.common.{ AttributeKey, Attributes } import io.opentelemetry.context.Context @@ -28,13 +28,13 @@ import java.util.Optional import scala.collection.JavaConverters._ private[core] class CoreSpanBuilder( + resource: Resource, spanId: Option[SpanId], var parentSpan: Option[Span], spanName: String, clock: Clock, handler: SpanHandler, - sampler: Sampler, - library: InstrumentationLibrary) extends SpanBuilder { + sampler: Sampler) extends SpanBuilder { var sticky: Boolean = true var spanKind: SpanKind = SpanKind.INTERNAL @@ -112,12 +112,12 @@ private[core] class CoreSpanBuilder( } private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long): Span = CoreSpan( + resource = resource, id = id, name = name, startTimeNanos = startTimeNanos, kind = kind, links = links, - library = library, clock = clock, handler = handler) @@ -131,7 +131,7 @@ private[core] class CoreSpanBuilder( } sampler.shouldSample(spanId, parentSpanId, spanName) match { - case DropResult => UnrecordedSpan(spanId, spanName) + case DropResult => UnrecordedSpan(spanId, spanName, resource) case RecordResult(sample, notes) => val traceFlags = if (sample) TraceFlags.getSampled else TraceFlags.getDefault 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..4ac0220b 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 @@ -16,7 +16,7 @@ package com.comcast.money.core -import com.comcast.money.api.{ InstrumentationLibrary, Span, SpanBuilder, SpanFactory, SpanHandler, SpanId } +import com.comcast.money.api.{ InstrumentationLibrary, Resource, Span, SpanBuilder, SpanFactory, SpanHandler, SpanId } import com.comcast.money.core.formatters.Formatter import com.comcast.money.core.internal.SpanContext import com.comcast.money.core.samplers.Sampler @@ -27,7 +27,7 @@ private[core] final case class CoreSpanFactory( handler: SpanHandler, formatter: Formatter, sampler: Sampler, - library: InstrumentationLibrary) extends SpanFactory { + resource: Resource) extends SpanFactory { override def spanBuilder(spanName: String): SpanBuilder = spanBuilder(spanName, None, spanContext.current) @@ -53,5 +53,5 @@ private[core] final case class CoreSpanFactory( clock = clock, handler = handler, sampler = sampler, - library = library) + resource = resource) } 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..3161051a 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,7 +17,8 @@ package com.comcast.money.core import java.util.Collections -import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo } +import com.comcast.money.api.{ InstrumentationLibrary, Note, Resource, SpanId, SpanInfo } +import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.{ Span, SpanKind, StatusCode } private[core] case class CoreSpanInfo( @@ -32,6 +33,9 @@ private[core] case class CoreSpanInfo( 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(), - library: InstrumentationLibrary = Money.InstrumentationLibrary, - appName: String = Money.Environment.applicationName, - host: String = Money.Environment.hostName) extends SpanInfo \ No newline at end of file + override val resource: Resource) extends SpanInfo { + + override def library(): InstrumentationLibrary = resource.library() + override def appName(): String = resource.applicationName() + override def host(): String = resource.hostName() +} \ No newline at end of file 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..8eab2122 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 @@ -41,8 +41,8 @@ case class Money( asyncNotifier: AsyncNotifier = new AsyncNotifier(Seq())) object Money { - - val InstrumentationLibrary = new InstrumentationLibrary("money-core", "0.10.0") + private lazy val MoneyVersion: String = getClass.getPackage.getImplementationVersion + lazy val InstrumentationLibrary = new InstrumentationLibrary("money-core", MoneyVersion) lazy val Environment: Money = apply(ConfigFactory.load().getConfig("money")) def apply(conf: Config): Money = { @@ -56,7 +56,8 @@ object Money { configureContextFilters(conf) val formatter = configureFormatter(conf) val sampler = configureSampler(conf) - val factory: SpanFactory = CoreSpanFactory(SpanLocal, clock, handler, formatter, sampler, Money.InstrumentationLibrary) + val resource = ResourceFactory.create(applicationName, hostName, InstrumentationLibrary, conf) + val factory: SpanFactory = CoreSpanFactory(SpanLocal, clock, handler, formatter, sampler, resource) val tracer = new Tracer { override val spanFactory: SpanFactory = factory } 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 deleted file mode 100644 index e8c69e1f..00000000 --- a/money-core/src/main/scala/com/comcast/money/core/MoneyTracerProvider.scala +++ /dev/null @@ -1,41 +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.{ InstrumentationLibrary, SpanFactory } -import io.opentelemetry.api.trace -import io.opentelemetry.api.trace.TracerProvider - -import scala.collection.concurrent.TrieMap - -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, { - tracer.spanFactory match { - case csf: CoreSpanFactory => new Tracer { - override val spanFactory: SpanFactory = csf.copy(library = library) - } - case _ => tracer - } - }) - } -} diff --git a/money-core/src/main/scala/com/comcast/money/core/ResourceFactory.scala b/money-core/src/main/scala/com/comcast/money/core/ResourceFactory.scala new file mode 100644 index 00000000..ec874933 --- /dev/null +++ b/money-core/src/main/scala/com/comcast/money/core/ResourceFactory.scala @@ -0,0 +1,56 @@ +/* + * 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.{ InstrumentationLibrary, Resource } +import com.typesafe.config.Config +import io.opentelemetry.api.common.{ AttributeKey, Attributes } +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes + +import scala.collection.JavaConverters._ + +private[core] object ResourceFactory { + def create( + applicationName: String, + hostName: String, + library: InstrumentationLibrary, + conf: Config): Resource = { + + val attributesBuilder = Attributes.builder() + .put(ResourceAttributes.SERVICE_NAME, applicationName) + .put(ResourceAttributes.TELEMETRY_SDK_LANGUAGE, "scala") + .put(ResourceAttributes.TELEMETRY_SDK_NAME, library.name()) + .put(ResourceAttributes.TELEMETRY_SDK_VERSION, library.version()) + .put(ResourceAttributes.HOST_NAME, hostName) + + val ResourceKey = "resource" + if (conf.hasPath(ResourceKey)) { + val resourceConf = conf.getConfig(ResourceKey) + for (entry <- resourceConf.entrySet().asScala) { + val key = entry.getKey + val value = resourceConf.getString(key) + attributesBuilder.put(AttributeKey.stringKey(key), value) + } + } + + CoreResource( + applicationName = applicationName, + hostName = hostName, + library = library, + attributes = attributesBuilder.build()) + } +} 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..dd27cfd6 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 -import com.comcast.money.api.{ MoneyTracer, Note, Span, SpanBuilder, SpanFactory } +import java.io.Closeable +import com.comcast.money.api.{ Note, Span, SpanBuilder, SpanFactory } import com.comcast.money.core.internal.{ SpanContext, SpanLocal } import io.opentelemetry.context.Scope -import io.opentelemetry.api.trace.{ StatusCode, Span => OtelSpan } +import io.opentelemetry.api.trace.StatusCode /** * 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 e2a04283..5b579223 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 @@ -19,19 +19,23 @@ package com.comcast.money.core import java.lang import java.time.Instant import java.util.concurrent.TimeUnit - -import com.comcast.money.api.{ Note, Span, SpanId, SpanInfo } +import com.comcast.money.api.{ Note, Resource, Span, SpanId, SpanInfo } import io.opentelemetry.api.common.{ AttributeKey, Attributes } import io.opentelemetry.context.Scope import io.opentelemetry.api.trace.{ SpanContext, StatusCode } private[core] final case class UnrecordedSpan( spanId: SpanId, - var name: String) extends Span { + var name: String, + resource: Resource) extends Span { private var scopes: List[Scope] = Nil - override def info(): SpanInfo = CoreSpanInfo(spanId, name) + override def info(): SpanInfo = CoreSpanInfo( + id = spanId, + name = name, + resource = resource) + override def getSpanContext: SpanContext = spanId.toSpanContext override def attachScope(scope: Scope): Span = { 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..e430afed 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 @@ -18,7 +18,7 @@ package com.comcast.money.core import java.time.Instant import java.util.concurrent.TimeUnit -import com.comcast.money.api.{ IdGenerator, InstrumentationLibrary, Note, Span, SpanHandler, SpanId, SpanInfo } +import com.comcast.money.api.{ IdGenerator, InstrumentationLibrary, Note, Resource, Span, SpanHandler, SpanId, SpanInfo } import com.comcast.money.core.samplers.{ Sampler, SamplerResult } import io.opentelemetry.api.common.{ AttributeKey, Attributes } import io.opentelemetry.api.trace.{ SpanContext, SpanKind, TraceFlags, TraceState, Span => OtelSpan } @@ -37,16 +37,22 @@ import java.util.Optional class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { val clock = SystemClock - val library = InstrumentationLibrary.UNKNOWN + var resource = new Resource { + override def applicationName(): String = "test" + override def hostName(): String = "test" + override def library(): InstrumentationLibrary = InstrumentationLibrary.UNKNOWN + override def attributes(): Attributes = Attributes.empty() + } "CoreSpanBuilder" should { "create a span" in { val handler = mock[SpanHandler] val sampler = mock[Sampler] + val resource = mock[Resource] 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(resource, None, None, "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { name shouldBe "test" span @@ -65,7 +71,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(resource, Some(spanId), None, "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { id shouldBe spanId name shouldBe "test" @@ -89,7 +95,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { 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 underTest = new CoreSpanBuilder(resource, None, Some(parentSpan), "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { id.traceId shouldBe parentSpanId.traceId id.parentId shouldBe parentSpanId.selfId @@ -114,7 +120,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { 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(resource, None, None, "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { id.traceId shouldBe parentSpanId.traceId id.parentId shouldBe parentSpanId.selfId @@ -141,7 +147,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { 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(resource, None, None, "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { id.traceId shouldBe parentSpanId.traceId id.parentId shouldBe parentSpanId.selfId @@ -169,7 +175,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar { 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(resource, None, None, "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { id.traceId shouldBe parentSpanId.traceId id.parentId shouldBe parentSpanId.selfId @@ -194,7 +200,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(resource, None, Some(parentSpan), "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = { name shouldBe "test" span @@ -213,7 +219,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(resource, None, None, "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span } @@ -260,7 +266,7 @@ 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 underTest = new CoreSpanBuilder(resource, None, Some(parentSpan), "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span } @@ -292,7 +298,7 @@ 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 underTest = new CoreSpanBuilder(resource, None, Some(parentSpan), "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span } @@ -309,7 +315,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(resource, None, None, "test", clock, handler, sampler) val result = underTest .setSpanKind(SpanKind.SERVER) @@ -323,7 +329,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(resource, None, None, "test", clock, handler, sampler) val result = underTest .setSpanKind(SpanKind.SERVER) @@ -337,7 +343,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(resource, None, None, "test", clock, handler, sampler) val result = underTest.startSpan() @@ -349,7 +355,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(resource, None, None, "test", clock, handler, sampler) val result = underTest.startSpan() @@ -364,7 +370,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(resource, None, None, "test", clock, handler, sampler) { override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span } @@ -383,7 +389,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(resource, None, None, "test", clock, handler, sampler) val result = underTest .setStartTimestamp(12345789L, TimeUnit.NANOSECONDS) @@ -397,7 +403,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(resource, None, None, "test", clock, handler, sampler) val instant = Instant.now val result = underTest @@ -413,7 +419,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(resource, None, None, "test", clock, handler, sampler) 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..c1e7af22 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 @@ -21,6 +21,7 @@ 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.samplers.{ AlwaysOffSampler, AlwaysOnSampler, RecordResult, Sampler, SamplerResult } +import io.opentelemetry.api.common.{ AttributeKey, Attributes } import io.opentelemetry.api.trace.TraceFlags import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec @@ -32,14 +33,14 @@ class CoreSpanFactorySpec extends AnyWordSpec with Matchers with MockitoSugar wi val handler = mock[SpanHandler] val formatter = MoneyTraceFormatter val sampler = AlwaysOnSampler - val instrumentationLibrary = new InstrumentationLibrary("test", "0.0.1") - val underTest = CoreSpanFactory(context, clock, handler, formatter, sampler, instrumentationLibrary) + val underTest = CoreSpanFactory(context, clock, handler, formatter, sampler, resource) "CoreSpanFactory" should { "create a new span" in { val result = underTest.newSpan("foo").asInstanceOf[CoreSpan] result.info.name shouldBe "foo" + result.info.resource shouldBe resource result.handler shouldBe handler } 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 index ca49c270..d43f928a 100644 --- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanInfoSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/CoreSpanInfoSpec.scala @@ -16,7 +16,8 @@ package com.comcast.money.core -import com.comcast.money.api.SpanId +import com.comcast.money.api.{ InstrumentationLibrary, SpanId } +import io.opentelemetry.api.common.Attributes import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec @@ -25,13 +26,15 @@ class CoreSpanInfoSpec extends AnyWordSpec with Matchers { "CoreSpanInfo" should { "have acceptable default values" in { val spanId = SpanId.createNew() - val underTest = CoreSpanInfo(spanId, "test") + val library = new InstrumentationLibrary("test", "0.0.1") + val resource = CoreResource("appName", "hostName", library, Attributes.empty()) + val underTest = CoreSpanInfo(spanId, "test", resource = resource) 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.appName shouldBe "appName" + underTest.host shouldBe "hostName" + underTest.library shouldBe library underTest.notes shouldBe empty underTest.success shouldBe null underTest.durationMicros 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 5396bbcd..ca3a029f 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 @@ -35,7 +35,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS "CoreSpan" should { "contain the start time stamp" in { - val underTest = CoreSpan(SpanId.createNew(), "test", startTimeNanos = 1000000000) + val underTest = CoreSpan(resource, SpanId.createNew(), "test", startTimeNanos = 1000000000) val info = underTest.info info.startTimeNanos shouldBe 1000000000 @@ -44,7 +44,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "record a timer" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.startTimer("foo") underTest.stopTimer("foo") @@ -53,7 +53,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "record a note" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.record(testLongNote) @@ -61,7 +61,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set a String attribute" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setAttribute("foo", "bar") @@ -72,7 +72,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set a long attribute" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setAttribute("foo", 200L) @@ -83,7 +83,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set a double attribute" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setAttribute("foo", 2.2) @@ -94,7 +94,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set a boolean attribute" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setAttribute("foo", true) @@ -105,7 +105,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set a attribute with an attribute key" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setAttribute(AttributeKey.stringKey("foo"), "bar") @@ -116,7 +116,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set a attribute with a long attribute key and an int" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") val i: Int = 123 underTest.setAttribute(AttributeKey.longKey("foo"), i) @@ -128,7 +128,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "add an event with name" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.addEvent("event") @@ -141,7 +141,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "add an event with name and timestamp" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.addEvent("event", 100L, TimeUnit.NANOSECONDS) @@ -154,7 +154,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "add an event with name and instant" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") val instant = Instant.now underTest.addEvent("event", instant) @@ -168,7 +168,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "add an event with name and attributes" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.addEvent("event", Attributes.of(AttributeKey.stringKey("foo"), "bar")) @@ -181,7 +181,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "add an event with name, attributes and timestamp" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.addEvent("event", Attributes.of(AttributeKey.stringKey("foo"), "bar"), 100L, TimeUnit.NANOSECONDS) @@ -195,7 +195,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "add an event with name, attributes and instant" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") val instant = Instant.now underTest.addEvent("event", Attributes.of(AttributeKey.stringKey("foo"), "bar"), instant) @@ -210,7 +210,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "record an exception" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") val exception = new RuntimeException("BOOM") underTest.recordException(exception) @@ -227,7 +227,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "record an exception with attributes" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") val exception = new RuntimeException("BOOM") underTest.recordException(exception, Attributes.of(AttributeKey.stringKey("foo"), "bar")) @@ -245,7 +245,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set the status to OK" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setStatus(StatusCode.OK) @@ -257,7 +257,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set the status to ERROR" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setStatus(StatusCode.ERROR) @@ -269,7 +269,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set the status to OK and stop with false result" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setStatus(StatusCode.OK) @@ -281,7 +281,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set the status to ERROR and stop with true result" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setStatus(StatusCode.ERROR) @@ -293,7 +293,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "set the status with description" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.setStatus(StatusCode.OK, "description") @@ -301,7 +301,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "update the span name" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.name shouldBe "test" underTest.info.name shouldBe "test" @@ -314,7 +314,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS } "gets isRecording" in { - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.isRecording shouldBe true @@ -325,7 +325,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS "gets SpanContext" in { val spanId = SpanId.createRemote("01234567-890A-BCDE-F012-34567890ABCD", 81985529216486895L, 81985529216486895L, TraceFlags.getSampled, TraceState.getDefault) - val underTest = CoreSpan(spanId, "test") + val underTest = CoreSpan(resource, spanId, "test") val context = underTest.getSpanContext @@ -336,7 +336,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS "set the endTimeMillis and endTimeMicros when stopped" in { val clock = mock[Clock] when(clock.now).thenReturn(3000000000L) - val underTest = CoreSpan(SpanId.createNew(), "test", startTimeNanos = 1000000000, clock = clock) + val underTest = CoreSpan(resource, SpanId.createNew(), "test", startTimeNanos = 1000000000, clock = clock) underTest.stop(true) @@ -352,7 +352,7 @@ 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 underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) + val underTest = CoreSpan(resource, SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) underTest.record(testLongNote) underTest.stop(true) @@ -372,7 +372,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 underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) + val underTest = CoreSpan(resource, SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) underTest.record(testLongNote) underTest.close() @@ -393,7 +393,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS val scope1 = mock[Scope] val scope2 = mock[Scope] - val underTest = CoreSpan(SpanId.createNew(), "test") + val underTest = CoreSpan(resource, SpanId.createNew(), "test") underTest.attachScope(scope1) underTest.attachScope(scope2) @@ -407,7 +407,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 underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) + val underTest = CoreSpan(resource, SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) underTest.record(testLongNote) underTest.end() @@ -427,7 +427,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 underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) + val underTest = CoreSpan(resource, SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) underTest.record(testLongNote) underTest.end(1L, TimeUnit.MILLISECONDS) @@ -447,7 +447,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 underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) + val underTest = CoreSpan(resource, SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now) val instant = Instant.now underTest.record(testLongNote) diff --git a/money-core/src/test/scala/com/comcast/money/core/MoneyTraceProviderSpec.scala b/money-core/src/test/scala/com/comcast/money/core/MoneyTraceProviderSpec.scala deleted file mode 100644 index 1571e3d8..00000000 --- a/money-core/src/test/scala/com/comcast/money/core/MoneyTraceProviderSpec.scala +++ /dev/null @@ -1,73 +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.{ InstrumentationLibrary, SpanFactory } -import com.comcast.money.core.internal.SpanLocal -import com.comcast.money.core.samplers.AlwaysOnSampler -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpec - -class MoneyTraceProviderSpec extends AnyWordSpec with Matchers { - - "MoneyTraceProvider" should { - "wrap an existing Tracer with a decorated SpanFactory" in { - val factory = new CoreSpanFactory(SpanLocal, SystemClock, DisabledSpanHandler, DisabledFormatter, AlwaysOnSampler, Money.InstrumentationLibrary) - val tracer = new Tracer { - override val spanFactory: SpanFactory = factory - } - - val underTest = MoneyTracerProvider(tracer) - - val result = underTest.get("test") - result shouldBe a[Tracer] - result should not be tracer - - val libraryTracer = result.asInstanceOf[Tracer] - val span = libraryTracer.startSpan("test") - val info = span.info - info.library shouldBe new InstrumentationLibrary("test") - } - - "returns the same Tracer for equivalent InstrumentationLibraries" in { - val factory = new CoreSpanFactory(SpanLocal, SystemClock, DisabledSpanHandler, DisabledFormatter, AlwaysOnSampler, Money.InstrumentationLibrary) - val tracer = new Tracer { - override val spanFactory: SpanFactory = factory - } - - val underTest = MoneyTracerProvider(tracer) - - val result = underTest.get("test", "0.0.1") - val other = underTest.get("test", "0.0.1") - other shouldBe result - } - - "return different Tracers for different InstrumentationLibraries" in { - val factory = new CoreSpanFactory(SpanLocal, SystemClock, DisabledSpanHandler, DisabledFormatter, AlwaysOnSampler, Money.InstrumentationLibrary) - val tracer = new Tracer { - override val spanFactory: SpanFactory = factory - } - - val underTest = MoneyTracerProvider(tracer) - - val result = underTest.get("test", "0.0.1") - val other = underTest.get("test", "0.0.2") - other shouldBe a[Tracer] - other should not be result - } - } -} diff --git a/money-core/src/test/scala/com/comcast/money/core/formatters/OtelFormatterSpec.scala b/money-core/src/test/scala/com/comcast/money/core/formatters/OtelFormatterSpec.scala index 1954a5e1..75fde94c 100644 --- a/money-core/src/test/scala/com/comcast/money/core/formatters/OtelFormatterSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/formatters/OtelFormatterSpec.scala @@ -16,7 +16,7 @@ package com.comcast.money.core.formatters -import com.comcast.money.api.SpanId +import com.comcast.money.api.{ Resource, SpanId } import com.comcast.money.core.{ CoreSpan, DisabledSpanHandler } import io.opentelemetry.context.Context import io.opentelemetry.context.propagation.{ TextMapGetter, TextMapPropagator, TextMapSetter } @@ -59,8 +59,9 @@ class OtelFormatterSpec extends AnyWordSpec with MockitoSugar with Matchers { } "wraps extract" in { + val resource = mock[Resource] val spanId = SpanId.createNew() - val span = CoreSpan(id = spanId, name = "test", handler = DisabledSpanHandler) + val span = CoreSpan(resource = resource, id = spanId, name = "test", handler = DisabledSpanHandler) val context = span.storeInContext(Context.root) val headers = Seq("A", "B") val getter = mock[String => String] 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..4a142eba 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 @@ -17,7 +17,7 @@ package com.comcast.money.core.handlers import com.comcast.money.api.{ Note, SpanId } -import com.comcast.money.core.CoreSpanInfo +import com.comcast.money.core.{ CoreResource, CoreSpanInfo } import com.typesafe.config.ConfigFactory import io.opentelemetry.api.trace.StatusCode @@ -36,27 +36,24 @@ class SpanLogFormatterSpec extends AnyWordSpec with Matchers { } """) val spanLogFormatter = SpanLogFormatter(emitterConf) - val spanId = SpanId.createNew() val sampleData = CoreSpanInfo( + resource = CoreResource("unknown", "host"), id = spanId, startTimeNanos = 1000000L, endTimeNanos = 26000000L, durationNanos = 35000000L, 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 withNull = CoreSpanInfo( + resource = CoreResource("unknown", "host"), id = spanId, startTimeNanos = 1000000L, endTimeNanos = 26000000L, durationNanos = 35000000L, name = "key", - appName = "unknown", - host = "host", notes = Map[String, Note[_]]("empty" -> Note.of("empty", null)).asJava, status = StatusCode.OK) 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..d73b989c 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 @@ -17,9 +17,8 @@ package com.comcast.money.core.handlers import java.util.Collections - -import com.comcast.money.api.{ Note, SpanHandler, SpanId, SpanInfo } -import com.comcast.money.core.{ Clock, CoreSpan, CoreSpanInfo, SystemClock } +import com.comcast.money.api.{ Note, Resource, SpanHandler, SpanId, SpanInfo } +import com.comcast.money.core.{ Clock, CoreResource, CoreSpan, CoreSpanInfo, SystemClock } import com.typesafe.config.Config import io.opentelemetry.api.trace.{ StatusCode, TraceFlags, TraceState, Span => OtelSpan } @@ -42,33 +41,35 @@ trait TestData { val clock: Clock = SystemClock + val resource: Resource = CoreResource( + applicationName = "test", + hostName = "localhost") + val testSpanInfo = CoreSpanInfo( + resource = resource, id = SpanId.createNew(), startTimeNanos = clock.now, endTimeNanos = clock.now, durationNanos = 123456000L, 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 testSpanId = SpanId.createNew() - val testSpan = CoreSpan(testSpanId, "test-span") + val testSpan = CoreSpan(resource, testSpanId, "test-span") val childSpanId = testSpanId.createChild() - val childSpan = CoreSpan(childSpanId, "child-span") + val childSpan = CoreSpan(resource, childSpanId, "child-span") val fixedTestSpanId = SpanId.createRemote("5092ddfe-3701-4f84-b3d2-21f5501c0d28", 5176425846116696835L, 5176425846116696835L, TraceFlags.getSampled, TraceState.getDefault) val fixedTestSpanInfo = CoreSpanInfo( + resource = resource, id = fixedTestSpanId, startTimeNanos = 100000000L, endTimeNanos = 300000000L, durationNanos = 200000L, 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()) } diff --git a/money-core/src/test/scala/com/comcast/money/core/internal/SpanLocalSpec.scala b/money-core/src/test/scala/com/comcast/money/core/internal/SpanLocalSpec.scala index 5d8dc9e3..8e755984 100644 --- a/money-core/src/test/scala/com/comcast/money/core/internal/SpanLocalSpec.scala +++ b/money-core/src/test/scala/com/comcast/money/core/internal/SpanLocalSpec.scala @@ -52,14 +52,14 @@ class SpanLocalSpec extends AnyWordSpec SpanLocal.current shouldEqual Some(testSpan) } "add to the existing call stack" in { - val nested = testSpan.copy(SpanId.createNew()) + val nested = testSpan.copy(id = SpanId.createNew()) SpanLocal.push(testSpan) SpanLocal.push(nested) SpanLocal.current shouldEqual Some(nested) } "close the last added item from the call stack" in { - val nested = testSpan.copy(SpanId.createNew()) + val nested = testSpan.copy(id = SpanId.createNew()) SpanLocal.push(testSpan) val scope = SpanLocal.push(nested) diff --git a/money-kafka/src/test/scala/com/comcast/money/kafka/KafkaSpanHandlerSpec.scala b/money-kafka/src/test/scala/com/comcast/money/kafka/KafkaSpanHandlerSpec.scala index 521e2a7a..c2ce81e8 100644 --- a/money-kafka/src/test/scala/com/comcast/money/kafka/KafkaSpanHandlerSpec.scala +++ b/money-kafka/src/test/scala/com/comcast/money/kafka/KafkaSpanHandlerSpec.scala @@ -16,7 +16,7 @@ package com.comcast.money.kafka -import com.comcast.money.api.Note +import com.comcast.money.api.{ InstrumentationLibrary, Note, Resource } import com.comcast.money.api import com.typesafe.config.{ Config, ConfigFactory } import kafka.message.{ CompressionCodec, GZIPCompressionCodec } @@ -30,8 +30,8 @@ import org.scalatestplus.mockito.MockitoSugar import scala.collection.JavaConverters._ import java.{ util => ju } - import com.comcast.money.wire.TestSpanInfo +import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.StatusCode trait MockProducerMaker extends ProducerMaker { @@ -66,6 +66,12 @@ class KafkaSpanHandlerSpec extends AnyWordSpec val sampleData = TestSpanInfo( id = api.SpanId.createNew(), name = "key", + resource = new Resource { + override def applicationName(): String = "app" + override def hostName(): String = "host" + override def library(): InstrumentationLibrary = new InstrumentationLibrary("test", "0.0.1") + override def attributes(): Attributes = Attributes.empty() + }, appName = "app", host = "host", startTimeNanos = 1000000L, 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 323f7b30..0dd643b9 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 @@ -35,6 +35,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 = convertResource(info.resource()) override def getSpanContext: SpanContext = spanContext override def getParentSpanContext: SpanContext = parentSpanContext @@ -45,7 +46,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 @@ -65,6 +66,9 @@ private[otel] class MoneyReadableSpanData(info: SpanInfo) extends ReadableSpan w id.parentSpanId().toSpanContext } + private def convertResource(resource: com.comcast.money.api.Resource): Resource = + Resource.create(resource.attributes()) + private def convertLibraryInfo(library: InstrumentationLibrary): InstrumentationLibraryInfo = if (library != null) { InstrumentationLibraryInfo.create(library.name, library.version) 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..2b222916 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,17 +20,20 @@ import java.util.List; import java.util.Map; +import io.opentelemetry.api.common.Attributes; 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.Resource; import com.comcast.money.api.SpanId; import com.comcast.money.api.SpanInfo; public class TestSpanInfo implements SpanInfo { private final SpanId spanId; + private final InstrumentationLibrary instrumentationLibrary = new InstrumentationLibrary("test", "0.0.1"); public TestSpanInfo(SpanId spanId) { this.spanId = spanId; @@ -68,7 +71,7 @@ public SpanKind kind() { @Override public InstrumentationLibrary library() { - return new InstrumentationLibrary("test", "0.0.1"); + return instrumentationLibrary; } @Override @@ -100,4 +103,29 @@ public String appName() { public String host() { return "host"; } + + @Override + public Resource resource() { + return new Resource() { + @Override + public String applicationName() { + return "appName"; + } + + @Override + public String hostName() { + return "host"; + } + + @Override + public InstrumentationLibrary library() { + return instrumentationLibrary; + } + + @Override + public Attributes attributes() { + return Attributes.empty(); + } + }; + } } \ No newline at end of file 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 0735e28c..e7dadc97 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 @@ -47,7 +47,7 @@ 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.getResource shouldBe Resource.create(Attributes.of(AttributeKey.stringKey("foo"), "bar")) underTest.getLatencyNanos shouldBe 2000000L underTest.getStatus shouldBe StatusData.create(StatusCode.OK, "description") underTest.getTotalAttributeCount shouldBe 1 @@ -72,7 +72,7 @@ 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.getResource shouldBe Resource.create(Attributes.of(AttributeKey.stringKey("foo"), "bar")) underTest.getLatencyNanos shouldBe 2000000L underTest.getStatus shouldBe StatusData.create(StatusCode.OK, "description") underTest.getTotalAttributeCount shouldBe 1 @@ -99,6 +99,12 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers { 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 resource(): com.comcast.money.api.Resource = new com.comcast.money.api.Resource { + override def applicationName(): String = "app" + override def hostName(): String = "host" + override def library(): InstrumentationLibrary = new InstrumentationLibrary("test", "0.0.1") + override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar") + } } val event = new SpanInfo.Event { 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..27e3b807 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 @@ -17,11 +17,13 @@ package com.comcast.money.spring; import com.comcast.money.api.InstrumentationLibrary; +import com.comcast.money.api.Resource; import com.comcast.money.api.Span; import com.comcast.money.api.SpanId; import com.comcast.money.api.SpanInfo; import com.comcast.money.core.CoreSpanInfo; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; @@ -50,6 +52,28 @@ public class MoneyClientHttpInterceptorSpec { @Before public void setUp() { Span span = mock(Span.class); + InstrumentationLibrary library = new InstrumentationLibrary("test", "0.0.1"); + Resource testResource = new Resource() { + @Override + public String applicationName() { + return "testAppName"; + } + + @Override + public String hostName() { + return "testHost"; + } + + @Override + public InstrumentationLibrary library() { + return library; + } + + @Override + public Attributes attributes() { + return Attributes.empty(); + } + }; SpanInfo testSpanInfo = new CoreSpanInfo( id, "testName", @@ -62,9 +86,7 @@ public void setUp() { Collections.emptyMap(), Collections.emptyList(), Collections.emptyList(), - new InstrumentationLibrary("test", "0.0.1"), - "testAppName", - "testHost"); + testResource); when(span.info()).thenReturn(testSpanInfo); when(span.storeInContext(any())).thenCallRealMethod(); 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..8a4bfae6 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,12 +21,13 @@ 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, Resource, SpanId, SpanInfo } import com.comcast.money.core._ import com.comcast.money.wire.avro import com.comcast.money.wire.avro.NoteType import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.databind.{ DeserializationFeature, ObjectMapper } +import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.{ Span, SpanKind, StatusCode, TraceFlags, TraceState } import org.apache.avro.Schema import org.apache.avro.io.{ DecoderFactory, EncoderFactory } @@ -151,6 +152,12 @@ trait SpanWireConverters { override def library(): InstrumentationLibrary = toInstrumentationLibrary(from) override def appName(): String = from.getAppName override def host(): String = from.getHost + override def resource(): Resource = new Resource { + override def applicationName(): String = from.getAppName + override def hostName(): String = from.getHost + override def library(): InstrumentationLibrary = toInstrumentationLibrary(from) + override def attributes(): Attributes = Attributes.empty() + } } } } diff --git a/money-wire/src/test/scala/com/comcast/money/wire/AvroConversionSpec.scala b/money-wire/src/test/scala/com/comcast/money/wire/AvroConversionSpec.scala index e90376c3..64163be1 100644 --- a/money-wire/src/test/scala/com/comcast/money/wire/AvroConversionSpec.scala +++ b/money-wire/src/test/scala/com/comcast/money/wire/AvroConversionSpec.scala @@ -16,8 +16,9 @@ package com.comcast.money.wire -import com.comcast.money.api.{ Note, SpanInfo } +import com.comcast.money.api.{ InstrumentationLibrary, Note, Resource, SpanInfo } import com.comcast.money.core.formatters.FormatterUtils.randomRemoteSpanId +import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.StatusCode import org.scalatest.Inspectors import org.scalatest.matchers.should.Matchers @@ -32,6 +33,12 @@ class AvroConversionSpec extends AnyWordSpec with Matchers with Inspectors { "Avro Conversion" should { "roundtrip" in { val orig = TestSpanInfo( + resource = new Resource { + override def applicationName(): String = "app" + override def hostName(): String = "host" + override def library(): InstrumentationLibrary = InstrumentationLibrary.UNKNOWN + override def attributes(): Attributes = Attributes.empty() + }, id = randomRemoteSpanId(), name = "key", appName = "app", diff --git a/money-wire/src/test/scala/com/comcast/money/wire/JsonConversionSpec.scala b/money-wire/src/test/scala/com/comcast/money/wire/JsonConversionSpec.scala index de3af2f3..b109aacc 100644 --- a/money-wire/src/test/scala/com/comcast/money/wire/JsonConversionSpec.scala +++ b/money-wire/src/test/scala/com/comcast/money/wire/JsonConversionSpec.scala @@ -16,8 +16,9 @@ package com.comcast.money.wire -import com.comcast.money.api.{ Note, SpanInfo } +import com.comcast.money.api.{ InstrumentationLibrary, Note, Resource, SpanInfo } import com.comcast.money.core.formatters.FormatterUtils.randomRemoteSpanId +import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.StatusCode import org.scalatest.Inspectors import org.scalatest.matchers.should.Matchers @@ -30,6 +31,12 @@ class JsonConversionSpec extends AnyWordSpec with Matchers with Inspectors { import scala.collection.JavaConverters._ val orig = TestSpanInfo( + resource = new Resource { + override def applicationName(): String = "app" + override def hostName(): String = "host" + override def library(): InstrumentationLibrary = InstrumentationLibrary.UNKNOWN + override def attributes(): Attributes = Attributes.empty() + }, id = randomRemoteSpanId(), name = "key", appName = "app", 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..a2b80e58 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 @@ -17,11 +17,12 @@ package com.comcast.money.wire import java.util.Collections -import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo } +import com.comcast.money.api.{ InstrumentationLibrary, Note, Resource, SpanId, SpanInfo } import com.comcast.money.core.Money import io.opentelemetry.api.trace.{ Span, SpanKind, StatusCode } case class TestSpanInfo( + resource: Resource, id: SpanId, name: String, kind: SpanKind = SpanKind.INTERNAL,