diff --git a/build.sbt b/build.sbt
index 76883dba..fae63fba 100644
--- a/build.sbt
+++ b/build.sbt
@@ -58,6 +58,7 @@ lazy val moneyCore =
Seq(
slf4j,
log4jbinding,
+ lombok,
metricsCore,
openTelemetryApi,
openTelemetrySemConv,
@@ -330,6 +331,7 @@ def javaOnlyProjectSettings = projectSettings ++ Seq(
)
def projectSettings = basicSettings ++ Seq(
+ compileOrder := CompileOrder.JavaThenScala,
ScoverageKeys.coverageHighlighting := true,
ScoverageKeys.coverageMinimum := 80,
ScoverageKeys.coverageFailOnMinimum := true,
diff --git a/lombok.config b/lombok.config
new file mode 100644
index 00000000..204c0d4a
--- /dev/null
+++ b/lombok.config
@@ -0,0 +1,3 @@
+config.stopBubbling = true
+lombok.accessors.chain=true
+lombok.accessors.fluent=true
\ No newline at end of file
diff --git a/money-api/src/main/java/com/comcast/money/api/EventInfo.java b/money-api/src/main/java/com/comcast/money/api/EventInfo.java
new file mode 100644
index 00000000..c040975f
--- /dev/null
+++ b/money-api/src/main/java/com/comcast/money/api/EventInfo.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 Comcast Cable Communications Management, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.comcast.money.api;
+
+import java.util.concurrent.TimeUnit;
+
+import io.opentelemetry.api.common.Attributes;
+
+/**
+ * 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 timestampNanos();
+
+ /**
+ * @return the timestamp of when the event occurred in microseconds since the epoch
+ */
+ default long timestampMicros() {
+ return TimeUnit.NANOSECONDS.toMicros(timestampNanos());
+ }
+
+ /**
+ * @return the timestamp of when the event occurred in milliseconds since the epoch
+ */
+ default long timestampMillis() {
+ return TimeUnit.NANOSECONDS.toMillis(timestampNanos());
+ }
+
+ /**
+ * @return an exception if one was recorded with the event; otherwise {@code null}
+ */
+ 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/InvalidSpan.java b/money-api/src/main/java/com/comcast/money/api/InvalidSpan.java
new file mode 100644
index 00000000..96fbae07
--- /dev/null
+++ b/money-api/src/main/java/com/comcast/money/api/InvalidSpan.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2012 Comcast Cable Communications Management, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.comcast.money.api;
+
+import java.util.concurrent.TimeUnit;
+
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.context.Scope;
+
+enum InvalidSpan implements Span, SpanInfo {
+ INSTANCE;
+
+ // $COVERAGE-OFF$
+ @Override
+ public Span addEvent(String name, Attributes attributes) {
+ return this;
+ }
+
+ @Override
+ public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) {
+ return this;
+ }
+
+ @Override
+ public Span setStatus(StatusCode canonicalCode, String description) {
+ return this;
+ }
+
+ @Override
+ public Span recordException(Throwable exception, Attributes additionalAttributes) {
+ return this;
+ }
+
+ @Override
+ public Span updateName(String name) {
+ return this;
+ }
+
+ @Override
+ public void end() { }
+
+ @Override
+ public void end(long timestamp, TimeUnit unit) { }
+
+ @Override
+ public SpanContext getSpanContext() {
+ return SpanId.getInvalid().toSpanContext();
+ }
+
+ @Override
+ public long startTimeNanos() {
+ return 0L;
+ }
+
+ @Override
+ public boolean hasEnded() {
+ return false;
+ }
+
+ @Override
+ public long endTimeNanos() {
+ return 0L;
+ }
+
+ @Override
+ public StatusCode status() {
+ return StatusCode.UNSET;
+ }
+
+ @Override
+ public boolean isRecording() {
+ return false;
+ }
+
+ @Override
+ public SpanKind kind() {
+ return SpanKind.INTERNAL;
+ }
+
+ @Override
+ public String description() {
+ return null;
+ }
+
+ @Override
+ public long durationNanos() {
+ return 0L;
+ }
+
+ @Override
+ public InstrumentationLibrary library() {
+ return InstrumentationLibrary.UNKNOWN;
+ }
+
+ @Override
+ public String appName() {
+ return null;
+ }
+
+ @Override
+ public String host() {
+ return null;
+ }
+
+ @Override
+ public Span record(Note> note) {
+ return this;
+ }
+
+ @Override
+ public Scope startTimer(String timerKey) {
+ return Scope.noop();
+ }
+
+ @Override
+ public void stopTimer(String timerKey) { }
+
+ @Override
+ public Span attachScope(Scope scope) {
+ return this;
+ }
+
+ @Override
+ public SpanInfo info() {
+ return this;
+ }
+
+ @Override
+ public SpanId id() {
+ return SpanId.getInvalid();
+ }
+
+ @Override
+ public void close() { }
+ // $COVERAGE-ON$
+}
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/PropagatedSpan.java b/money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java
new file mode 100644
index 00000000..2eed87b2
--- /dev/null
+++ b/money-api/src/main/java/com/comcast/money/api/PropagatedSpan.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2012 Comcast Cable Communications Management, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.comcast.money.api;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.context.Scope;
+
+final class PropagatedSpan implements Span, SpanInfo {
+ private final SpanId id;
+ private final SpanKind kind;
+ private final String name;
+ private final List scopes = new ArrayList<>();
+
+ PropagatedSpan(SpanId id, String name, SpanKind kind) {
+ this.id = id;
+ this.kind = kind;
+ this.name = name;
+ }
+
+ @Override
+ public SpanInfo info() {
+ return this;
+ }
+
+ @Override
+ public SpanId id() {
+ return id;
+ }
+
+ @Override
+ public SpanContext getSpanContext() {
+ return id.toSpanContext();
+ }
+
+ @Override
+ public Span attachScope(Scope scope) {
+ scopes.add(scope);
+ return this;
+ }
+
+ @Override
+ public void close() {
+ for (Iterator iterator = scopes.iterator(); iterator.hasNext(); ) {
+ Scope scope = iterator.next();
+ iterator.remove();
+ scope.close();
+ }
+ }
+
+ // $COVERAGE-OFF$
+ @Override
+ public Span addEvent(String name, Attributes attributes) {
+ return this;
+ }
+
+ @Override
+ public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) {
+ return this;
+ }
+
+ @Override
+ public Span setStatus(StatusCode canonicalCode, String description) {
+ return this;
+ }
+
+ @Override
+ public Span recordException(Throwable exception, Attributes additionalAttributes) {
+ return this;
+ }
+
+ @Override
+ public Span updateName(String name) {
+ return this;
+ }
+
+ @Override
+ public Span record(Note> note) {
+ return this;
+ }
+
+ @Override
+ public Scope startTimer(String timerKey) {
+ return Scope.noop();
+ }
+
+ @Override
+ public void stopTimer(String timerKey) { }
+
+ @Override
+ public long startTimeNanos() {
+ return 0L;
+ }
+
+ @Override
+ public boolean hasEnded() {
+ return false;
+ }
+
+ @Override
+ public long endTimeNanos() {
+ return 0L;
+ }
+
+ @Override
+ public StatusCode status() {
+ return StatusCode.UNSET;
+ }
+
+ @Override
+ public SpanKind kind() {
+ return kind;
+ }
+
+ @Override
+ public String description() {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public long durationNanos() {
+ return 0L;
+ }
+
+ @Override
+ public InstrumentationLibrary library() {
+ return InstrumentationLibrary.UNKNOWN;
+ }
+
+ @Override
+ public String appName() {
+ return null;
+ }
+
+ @Override
+ public String host() {
+ return null;
+ }
+
+ @Override
+ public void end() { }
+
+ @Override
+ public void end(long timestamp, TimeUnit unit) { }
+
+ @Override
+ public boolean isRecording() {
+ return false;
+ }
+ // $COVERAGE-ON$
+}
diff --git a/money-api/src/main/java/com/comcast/money/api/Span.java b/money-api/src/main/java/com/comcast/money/api/Span.java
index db35ff91..467d1f25 100644
--- a/money-api/src/main/java/com/comcast/money/api/Span.java
+++ b/money-api/src/main/java/com/comcast/money/api/Span.java
@@ -21,54 +21,62 @@
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
/**
* A Span is a container that represents a unit of work. It could be a long running operation or sequence of
* statements in process, or a remote system call.
- *
- * A Span is immutable, all changes to the span result in a new Span being created.
*/
public interface Span extends io.opentelemetry.api.trace.Span, Scope {
- /**
- * Stops the span asserts a successful result
- */
- void stop();
-
- /**
- * Ends a span, moving it to a Stopped state
- * @param result The result of the span (success or failure)
- */
- void stop(Boolean result);
-
@Override
- Span setAttribute(String key, String value);
+ default Span setAttribute(String key, String value) {
+ return record(Note.of(key, value));
+ }
@Override
- Span setAttribute(String key, long value);
+ default Span setAttribute(String key, long value) {
+ return record(Note.of(key, value));
+ }
@Override
- Span setAttribute(String key, double value);
+ default Span setAttribute(String key, double value) {
+ return record(Note.of(key, value));
+ }
@Override
- Span setAttribute(String key, boolean value);
+ default Span setAttribute(String key, boolean value) {
+ return record(Note.of(key, value));
+ }
@Override
- Span setAttribute(AttributeKey key, T value);
+ default Span setAttribute(AttributeKey key, T value) {
+ return record(Note.of(key, value));
+ }
@Override
- Span setAttribute(AttributeKey key, int value);
+ default Span setAttribute(AttributeKey key, int value) {
+ return setAttribute(key, (long) value);
+ }
@Override
- Span addEvent(String name);
+ default Span addEvent(String name) {
+ return addEvent(name, Attributes.empty());
+ }
@Override
- Span addEvent(String name, long timestamp, TimeUnit unit);
+ default Span addEvent(String name, long timestamp, TimeUnit unit) {
+ return addEvent(name, Attributes.empty(), timestamp, unit);
+ }
@Override
- Span addEvent(String name, Instant timestamp);
+ default Span addEvent(String name, Instant timestamp) {
+ return addEvent(name, Attributes.empty(), timestamp);
+ }
@Override
Span addEvent(String name, Attributes attributes);
@@ -77,16 +85,26 @@ public interface Span extends io.opentelemetry.api.trace.Span, Scope {
Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit);
@Override
- Span addEvent(String name, Attributes attributes, Instant timestamp);
+ default Span addEvent(String name, Attributes attributes, Instant timestamp) {
+ if (timestamp != null) {
+ return addEvent(name, attributes, TimeUnit.SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano(), TimeUnit.NANOSECONDS);
+ } else {
+ return addEvent(name, attributes);
+ }
+ }
@Override
- Span setStatus(StatusCode canonicalCode);
+ default Span setStatus(StatusCode canonicalCode) {
+ return setStatus(canonicalCode, null);
+ }
@Override
Span setStatus(StatusCode canonicalCode, String description);
@Override
- Span recordException(Throwable exception);
+ default Span recordException(Throwable exception) {
+ return recordException(exception, Attributes.empty());
+ }
@Override
Span recordException(Throwable exception, Attributes additionalAttributes);
@@ -117,8 +135,89 @@ public interface Span extends io.opentelemetry.api.trace.Span, Scope {
*/
Span attachScope(Scope scope);
+ /**
+ * Ends the span
+ * @param result The result of the span (success or failure)
+ */
+ default void end(boolean result) {
+ setStatus(result ? StatusCode.OK : StatusCode.ERROR).end();
+ }
+
/**
* @return The current state of the Span
*/
SpanInfo info();
+
+ /**
+ * @return The {@link SpanId} of the Span
+ */
+ SpanId id();
+
+ /**
+ * Returns the {@link io.opentelemetry.api.trace.Span} from the current {@link Context}, falling back to a default, no-op
+ * {@link io.opentelemetry.api.trace.Span} if there is no span in the current context.
+ */
+ static Span current() {
+ return fromContext(Context.current());
+ }
+
+ /**
+ * Returns the {@link io.opentelemetry.api.trace.Span} from the specified {@link Context}, falling back to a default, no-op
+ * {@link io.opentelemetry.api.trace.Span} if there is no span in the context.
+ */
+ static Span fromContext(Context context) {
+ Span span = fromContextOrNull(context);
+ return span != null ? span : getInvalid();
+ }
+
+ /**
+ * Returns the {@link io.opentelemetry.api.trace.Span} from the specified {@link Context}, or {@code null} if there is no
+ * span in the context.
+ */
+ static Span fromContextOrNull(Context context) {
+ if (context != null) {
+ io.opentelemetry.api.trace.Span otelSpan = io.opentelemetry.api.trace.Span.fromContextOrNull(context);
+ if (otelSpan instanceof Span) {
+ Span span = (Span) otelSpan;
+ if (span.info().id().isValid()) {
+ return (Span) otelSpan;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns an invalid {@link io.opentelemetry.api.trace.Span}.
+ */
+ static Span getInvalid() {
+ return InvalidSpan.INSTANCE;
+ }
+
+ /**
+ * Returns a non-recording {@link Span} that holds the provided {@link SpanId} but has no
+ * functionality. It will not be exported and all tracing operations are no-op, but it can be used
+ * to propagate a valid {@link SpanId} downstream.
+ */
+ static Span wrap(SpanId id) {
+ return wrap(id, null, SpanKind.INTERNAL);
+ }
+
+ /**
+ * Returns a non-recording {@link Span} that holds the provided {@link SpanId} but has no
+ * functionality. It will not be exported and all tracing operations are no-op, but it can be used
+ * to propagate a valid {@link SpanId} downstream.
+ */
+ static Span wrap(SpanId id, String name) {
+ return wrap(id, name, SpanKind.INTERNAL);
+ }
+
+ /**
+ * Returns a non-recording {@link Span} that holds the provided {@link SpanId} but has no
+ * functionality. It will not be exported and all tracing operations are no-op, but it can be used
+ * to propagate a valid {@link SpanId} downstream.
+ */
+ static Span wrap(SpanId id, String name, SpanKind kind) {
+ return id != null && id.isValid() ? new PropagatedSpan(id, name, kind) : getInvalid();
+ }
}
diff --git a/money-api/src/main/java/com/comcast/money/api/SpanBuilder.java b/money-api/src/main/java/com/comcast/money/api/SpanBuilder.java
index 9a247310..8219d225 100644
--- a/money-api/src/main/java/com/comcast/money/api/SpanBuilder.java
+++ b/money-api/src/main/java/com/comcast/money/api/SpanBuilder.java
@@ -34,16 +34,6 @@ public interface SpanBuilder extends io.opentelemetry.api.trace.SpanBuilder {
@Override
SpanBuilder setParent(Context context);
- /**
- * Sets the parent span to the specified span
- */
- SpanBuilder setParent(Span span);
-
- /**
- * Sets the parent span to the specified span
- */
- SpanBuilder setParent(Optional span);
-
/**
* {@inheritDoc}
*/
@@ -59,7 +49,9 @@ public interface SpanBuilder extends io.opentelemetry.api.trace.SpanBuilder {
* {@inheritDoc}
*/
@Override
- SpanBuilder addLink(SpanContext spanContext);
+ default SpanBuilder addLink(SpanContext spanContext) {
+ return addLink(spanContext, Attributes.empty());
+ }
/**
* {@inheritDoc}
@@ -71,31 +63,41 @@ public interface SpanBuilder extends io.opentelemetry.api.trace.SpanBuilder {
* {@inheritDoc}
*/
@Override
- SpanBuilder setAttribute(String key, String value);
+ default SpanBuilder setAttribute(String key, String value) {
+ return record(Note.of(key, value));
+ }
/**
* {@inheritDoc}
*/
@Override
- SpanBuilder setAttribute(String key, long value);
+ default SpanBuilder setAttribute(String key, long value) {
+ return record(Note.of(key, value));
+ }
/**
* {@inheritDoc}
*/
@Override
- SpanBuilder setAttribute(String key, double value);
+ default SpanBuilder setAttribute(String key, double value) {
+ return record(Note.of(key, value));
+ }
/**
* {@inheritDoc}
*/
@Override
- SpanBuilder setAttribute(String key, boolean value);
+ default SpanBuilder setAttribute(String key, boolean value) {
+ return record(Note.of(key, value));
+ }
/**
* {@inheritDoc}
*/
@Override
- SpanBuilder setAttribute(AttributeKey key, T value);
+ default SpanBuilder setAttribute(AttributeKey key, T value) {
+ return record(Note.of(key, value));
+ }
/**
* Records the note on the created span
@@ -118,7 +120,9 @@ public interface SpanBuilder extends io.opentelemetry.api.trace.SpanBuilder {
* {@inheritDoc}
*/
@Override
- SpanBuilder setStartTimestamp(Instant startTimestamp);
+ default SpanBuilder setStartTimestamp(Instant startTimestamp) {
+ return setStartTimestamp(TimeUnit.SECONDS.toNanos(startTimestamp.getEpochSecond()) + startTimestamp.getNano(), TimeUnit.NANOSECONDS);
+ }
/**
* {@inheritDoc}
diff --git a/money-api/src/main/java/com/comcast/money/api/SpanInfo.java b/money-api/src/main/java/com/comcast/money/api/SpanInfo.java
index 896ce152..99faa8a1 100644
--- a/money-api/src/main/java/com/comcast/money/api/SpanInfo.java
+++ b/money-api/src/main/java/com/comcast/money/api/SpanInfo.java
@@ -21,11 +21,8 @@
import java.util.Map;
import java.util.concurrent.TimeUnit;
-import io.opentelemetry.api.common.Attributes;
-import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
-import io.opentelemetry.api.trace.Span;
public interface SpanInfo {
@@ -33,19 +30,21 @@ public interface SpanInfo {
* @return a map of all of the notes that were recorded on the span. Implementers should enforce
* that the map returned is a copy of the notes
*/
- Map> notes();
+ default Map> notes() {
+ return Collections.emptyMap();
+ }
/**
* @return a list of all of the events that were recorded on the span.
*/
- default List events() {
+ default List events() {
return Collections.emptyList();
}
/**
* @return a list of the spans linked to the span
*/
- default List links() {
+ default List links() {
return Collections.emptyList();
}
@@ -84,7 +83,12 @@ default long endTimeMicros() {
}
/**
- * @return the time in nanoseconds when this span was stopped.
+ * @return {@code true} if the span has ended
+ */
+ boolean hasEnded();
+
+ /**
+ * @return the time in nanoseconds when this span ended.
*/
long endTimeNanos();
@@ -94,27 +98,19 @@ default long endTimeMicros() {
StatusCode status();
/**
- * @return the result of the span. Will return null if the span was never stopped.
+ * @return the result of the span. Will return {@code null} if the span has not ended..
*/
default Boolean success() {
- StatusCode status = status();
- if (status != null && endTimeNanos() > 0L) {
- switch (status) {
- case OK:
- return true;
- case ERROR:
- return false;
- }
+ if (hasEnded()) {
+ return status() != StatusCode.ERROR;
}
return null;
}
/**
- * @return {@code true} if the span has been started but not yet stopped; otherwise, {@code false}.
+ * @return {@code true} if the span is recording notes or events
*/
- default boolean isRecording() {
- return startTimeNanos() > 0L && endTimeNanos() <= 0L;
- }
+ boolean isRecording();
/**
* @return the kind of the span, e.g. if it wraps a server or client request.
@@ -165,45 +161,4 @@ default long durationMicros() {
*/
String host();
- /**
- * An event that was recorded on a {@link com.comcast.money.api.Span}.
- */
- interface Event {
- /**
- * @return the name of the event
- */
- String name();
-
- /**
- * @return the attributes recorded on the event
- */
- Attributes attributes();
-
- /**
- * @return the timestamp of when the event occurred in nanoseconds since the epoch
- */
- long timestamp();
-
- /**
- * @return an exception if one was recorded with the event; otherwise {@code null}
- */
- Throwable exception();
- }
-
- /**
- * A reference to another {@link Span} by span context.
- *
- * Can be used to associate multiple traces as a part of a batch operation.
- */
- interface Link {
- /**
- * @return the context of the linked span
- */
- SpanContext spanContext();
-
- /**
- * @return the attributes associated with the link between the spans
- */
- Attributes attributes();
- }
}
diff --git a/money-api/src/main/java/com/comcast/money/api/MoneyTracer.java b/money-api/src/main/java/com/comcast/money/api/Tracer.java
similarity index 85%
rename from money-api/src/main/java/com/comcast/money/api/MoneyTracer.java
rename to money-api/src/main/java/com/comcast/money/api/Tracer.java
index c31fef4e..3c72d991 100644
--- a/money-api/src/main/java/com/comcast/money/api/MoneyTracer.java
+++ b/money-api/src/main/java/com/comcast/money/api/Tracer.java
@@ -16,13 +16,10 @@
package com.comcast.money.api;
-import io.opentelemetry.api.trace.SpanBuilder;
-import io.opentelemetry.api.trace.Tracer;
-
/**
* OpenTelemetry compatible API to be used for tracing
*/
-public interface MoneyTracer extends Tracer {
+public interface Tracer extends io.opentelemetry.api.trace.Tracer {
/**
* {@inheritDoc}
diff --git a/money-api/src/main/java/com/comcast/money/api/TracerProvider.java b/money-api/src/main/java/com/comcast/money/api/TracerProvider.java
new file mode 100644
index 00000000..f5547956
--- /dev/null
+++ b/money-api/src/main/java/com/comcast/money/api/TracerProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012 Comcast Cable Communications Management, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.comcast.money.api;
+
+public interface TracerProvider extends io.opentelemetry.api.trace.TracerProvider {
+ @Override
+ default Tracer get(String instrumentationName) {
+ return get(new InstrumentationLibrary(instrumentationName, null));
+ }
+
+ @Override
+ default Tracer get(String instrumentationName, String instrumentationVersion) {
+ return get(new InstrumentationLibrary(instrumentationName, instrumentationVersion));
+ }
+
+ Tracer get(InstrumentationLibrary instrumentationLibrary);
+}
diff --git a/money-core/src/main/scala/com/comcast/money/core/Clock.scala b/money-core/src/main/java/com/comcast/money/core/Clock.java
similarity index 87%
rename from money-core/src/main/scala/com/comcast/money/core/Clock.scala
rename to money-core/src/main/java/com/comcast/money/core/Clock.java
index 525bb7df..5037cd98 100644
--- a/money-core/src/main/scala/com/comcast/money/core/Clock.scala
+++ b/money-core/src/main/java/com/comcast/money/core/Clock.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.comcast.money.core
+package com.comcast.money.core;
-trait Clock {
- def now: Long
- def nanoTime: Long
+public interface Clock {
+ long now();
+ long nanoTime();
}
diff --git a/money-core/src/main/java/com/comcast/money/core/CoreEvent.java b/money-core/src/main/java/com/comcast/money/core/CoreEvent.java
new file mode 100644
index 00000000..0c13bdf3
--- /dev/null
+++ b/money-core/src/main/java/com/comcast/money/core/CoreEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 Comcast Cable Communications Management, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.comcast.money.core;
+
+import io.opentelemetry.api.common.Attributes;
+import lombok.Value;
+
+import com.comcast.money.api.EventInfo;
+
+@Value
+class CoreEvent implements EventInfo {
+ String name;
+ long timestampNanos;
+ Attributes attributes;
+
+ @Override
+ public Throwable exception() {
+ return null;
+ }
+}
diff --git a/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java b/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java
new file mode 100644
index 00000000..816710b3
--- /dev/null
+++ b/money-core/src/main/java/com/comcast/money/core/CoreExceptionEvent.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 Comcast Cable Communications Management, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.comcast.money.core;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.common.AttributesBuilder;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+
+import com.comcast.money.api.EventInfo;
+
+@EqualsAndHashCode(exclude = {"additionalAttributes", "lock"})
+final class CoreExceptionEvent implements EventInfo {
+ @Getter private final Throwable exception;
+ @Getter private final long timestampNanos;
+ private final Attributes additionalAttributes;
+ private final Object lock = new Object();
+ private transient Attributes attributes;
+
+ public CoreExceptionEvent(Throwable exception, long timestampNanos, Attributes attributes) {
+ this.exception = exception;
+ this.timestampNanos = timestampNanos;
+ this.additionalAttributes = attributes;
+ }
+
+ @Override
+ public String name() {
+ return SemanticAttributes.EXCEPTION_EVENT_NAME;
+ }
+
+ @Override
+ public Attributes attributes() {
+ if (attributes != null) {
+ return attributes;
+ }
+ synchronized (lock) {
+ if (attributes != null) {
+ return attributes;
+ }
+
+ attributes = mergeExceptionAttributes(additionalAttributes, exception);
+ return attributes;
+ }
+ }
+
+ private Attributes mergeExceptionAttributes(Attributes attributes, Throwable exception) {
+ AttributesBuilder builder = additionalAttributes != null ? attributes.toBuilder() : Attributes.builder();
+ builder.put(SemanticAttributes.EXCEPTION_TYPE, exception.getClass().getCanonicalName());
+ String message = exception.getMessage();
+ if (message != null && !message.isEmpty()) {
+ builder.put(SemanticAttributes.EXCEPTION_MESSAGE, message);
+ }
+ StringWriter writer = new StringWriter();
+ exception.printStackTrace(new PrintWriter(writer));
+ builder.put(SemanticAttributes.EXCEPTION_STACKTRACE, writer.toString());
+ return builder.build();
+ }
+}
diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala b/money-core/src/main/java/com/comcast/money/core/CoreLink.java
similarity index 67%
rename from money-core/src/main/scala/com/comcast/money/core/CoreLink.scala
rename to money-core/src/main/java/com/comcast/money/core/CoreLink.java
index 4a9d50fa..520505dd 100644
--- a/money-core/src/main/scala/com/comcast/money/core/CoreLink.scala
+++ b/money-core/src/main/java/com/comcast/money/core/CoreLink.java
@@ -14,12 +14,16 @@
* limitations under the License.
*/
-package com.comcast.money.core
+package com.comcast.money.core;
-import com.comcast.money.api.SpanInfo
-import io.opentelemetry.api.common.Attributes
-import io.opentelemetry.api.trace.SpanContext
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.SpanContext;
+import lombok.Value;
-private[core] final case class CoreLink(
- spanContext: SpanContext,
- attributes: Attributes = Attributes.empty()) extends SpanInfo.Link
+import com.comcast.money.api.LinkInfo;
+
+@Value
+class CoreLink implements LinkInfo {
+ SpanContext spanContext;
+ Attributes attributes;
+}
diff --git a/money-core/src/main/java/com/comcast/money/core/CoreSpanInfo.java b/money-core/src/main/java/com/comcast/money/core/CoreSpanInfo.java
new file mode 100644
index 00000000..15bfc9b4
--- /dev/null
+++ b/money-core/src/main/java/com/comcast/money/core/CoreSpanInfo.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012 Comcast Cable Communications Management, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.comcast.money.core;
+
+import java.util.List;
+import java.util.Map;
+
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import lombok.Builder;
+import lombok.Value;
+
+import com.comcast.money.api.EventInfo;
+import com.comcast.money.api.InstrumentationLibrary;
+import com.comcast.money.api.LinkInfo;
+import com.comcast.money.api.Note;
+import com.comcast.money.api.SpanId;
+import com.comcast.money.api.SpanInfo;
+
+@Value
+@Builder(toBuilder = true)
+public class CoreSpanInfo implements SpanInfo {
+ String appName;
+ String host;
+ InstrumentationLibrary library;
+ SpanId id;
+ String name;
+ SpanKind kind;
+ List links;
+ long startTimeNanos;
+ long endTimeNanos;
+ boolean hasEnded;
+ long durationNanos;
+ StatusCode status;
+ String description;
+ Map> notes;
+ List events;
+
+ @Override
+ public boolean isRecording() {
+ return true;
+ }
+}
diff --git a/money-core/src/main/scala/com/comcast/money/core/context/ContextStorageFilter.scala b/money-core/src/main/java/com/comcast/money/core/context/ContextStorageFilter.java
similarity index 70%
rename from money-core/src/main/scala/com/comcast/money/core/context/ContextStorageFilter.scala
rename to money-core/src/main/java/com/comcast/money/core/context/ContextStorageFilter.java
index b2bdc97a..60c0700e 100644
--- a/money-core/src/main/scala/com/comcast/money/core/context/ContextStorageFilter.scala
+++ b/money-core/src/main/java/com/comcast/money/core/context/ContextStorageFilter.java
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.comcast.money.core.context
+package com.comcast.money.core.context;
-import io.opentelemetry.context.{ Context, ContextStorage, Scope }
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.ContextStorage;
+import io.opentelemetry.context.Scope;
-trait ContextStorageFilter {
- def attach(context: Context, storage: ContextStorage): Scope
+public interface ContextStorageFilter {
+ Scope attach(Context context, ContextStorage storage);
}
diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala b/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala
deleted file mode 100644
index d714fa4a..00000000
--- a/money-core/src/main/scala/com/comcast/money/core/CoreEvent.scala
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2012 Comcast Cable Communications Management, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.comcast.money.core
-
-import java.io.{ PrintWriter, StringWriter }
-import com.comcast.money.api.SpanInfo
-import io.opentelemetry.api.common.Attributes
-import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
-
-private[core] case class CoreEvent(
- name: String,
- eventAttributes: Attributes,
- timestamp: Long,
- exception: Throwable) extends SpanInfo.Event {
-
- lazy val attributes: Attributes = initializeAttributes()
-
- private def initializeAttributes(): Attributes = {
- if (exception != null) {
- val attributeBuilder = if (eventAttributes != null) {
- eventAttributes.toBuilder
- } else {
- Attributes.builder()
- }
- attributeBuilder.put(SemanticAttributes.EXCEPTION_TYPE, exception.getClass.getCanonicalName)
- if (exception.getMessage != null) {
- attributeBuilder.put(SemanticAttributes.EXCEPTION_MESSAGE, exception.getMessage)
- }
- val writer = new StringWriter
- exception.printStackTrace(new PrintWriter(writer))
- attributeBuilder.put(SemanticAttributes.EXCEPTION_STACKTRACE, writer.toString)
- attributeBuilder.build()
- } else if (eventAttributes != null) {
- eventAttributes
- } else {
- Attributes.empty()
- }
- }
-}
-
diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala
index bf11eef4..49d93742 100644
--- a/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala
+++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpan.scala
@@ -16,17 +16,15 @@
package com.comcast.money.core
-import java.time.Instant
-import java.util.concurrent.TimeUnit
import com.comcast.money.api._
+import io.opentelemetry.api.common.Attributes
+import io.opentelemetry.api.trace.{ SpanContext, SpanKind, StatusCode }
+import io.opentelemetry.context.Scope
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicBoolean
import scala.collection.JavaConverters._
import scala.collection.concurrent.TrieMap
-import io.opentelemetry.api.trace.{ SpanContext, SpanKind, StatusCode, Span => OtelSpan }
-import io.opentelemetry.api.common.{ AttributeKey, Attributes }
-import io.opentelemetry.context.Scope
-import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
-
import scala.collection.mutable.ListBuffer
/**
@@ -37,15 +35,17 @@ import scala.collection.mutable.ListBuffer
* @param handler The [[SpanHandler]] responsible for processing the span once it is stopped
*/
private[core] case class CoreSpan(
- id: SpanId,
+ override val id: SpanId,
var name: String,
kind: SpanKind = SpanKind.INTERNAL,
- links: List[SpanInfo.Link] = Nil,
+ links: List[LinkInfo] = Nil,
startTimeNanos: Long = SystemClock.now,
library: InstrumentationLibrary = Money.InstrumentationLibrary,
clock: Clock = SystemClock,
handler: SpanHandler = DisabledSpanHandler) extends Span {
+ private val ended: AtomicBoolean = new AtomicBoolean()
+
private var endTimeNanos: Long = 0
private var status: StatusCode = StatusCode.UNSET
private var description: String = _
@@ -53,38 +53,9 @@ private[core] case class CoreSpan(
// use concurrent maps
private val timers = new TrieMap[String, Long]()
private val noted = new TrieMap[String, Note[_]]()
- private val events = new ListBuffer[SpanInfo.Event]()
+ private val events = new ListBuffer[EventInfo]()
private var scopes: List[Scope] = Nil
- override def stop(): Unit = stop(clock.now, StatusCode.UNSET)
-
- override def stop(result: java.lang.Boolean): Unit =
- if (result == null) {
- stop(clock.now, StatusCode.UNSET)
- } else {
- stop(clock.now, if (result) StatusCode.OK else StatusCode.ERROR)
- }
-
- private def stop(endTimeNanos: Long, status: StatusCode): Unit = {
- this.endTimeNanos = endTimeNanos
-
- // process any hanging timers
- val openTimers = timers.keys
- openTimers.foreach(stopTimer)
-
- scopes.foreach { _.close() }
- scopes = Nil
-
- this.status = (this.status, status) match {
- case (StatusCode.UNSET, StatusCode.UNSET) => StatusCode.OK
- case (StatusCode.UNSET, other) => other
- case (other, StatusCode.UNSET) => other
- case (_, other) => other
- }
-
- handler.handle(info())
- }
-
override def stopTimer(timerKey: String): Unit =
timers.remove(timerKey) foreach {
timerStartInstant =>
@@ -107,51 +78,37 @@ private[core] case class CoreSpan(
}
override def info(): SpanInfo =
- CoreSpanInfo(
- id = id,
- name = name,
- kind = kind,
- library = library,
- startTimeNanos = startTimeNanos,
- endTimeNanos = endTimeNanos,
- durationNanos = calculateDurationNanos,
- status = status,
- description = description,
- notes = noted.toMap[String, Note[_]].asJava,
- events = events.asJava,
- links = links.asJava)
-
- override def close(): Unit = stop()
-
- override def setAttribute(attributeName: String, value: String): Span = record(Note.of(attributeName, value))
- override def setAttribute(attributeName: String, value: scala.Long): Span = record(Note.of(attributeName, value))
- override def setAttribute(attributeName: String, value: Double): Span = record(Note.of(attributeName, value))
- override def setAttribute(attributeName: String, value: Boolean): Span = record(Note.of(attributeName, value))
- override def setAttribute[T](key: AttributeKey[T], value: T): Span = record(Note.of(key, value))
-
- override def addEvent(eventName: String): Span = addEventInternal(eventName, Attributes.empty(), clock.now)
- override def addEvent(eventName: String, timestamp: Instant): Span = addEventInternal(eventName, Attributes.empty(), timestamp)
- override def addEvent(eventName: String, timestamp: scala.Long, unit: TimeUnit): Span = addEventInternal(eventName, Attributes.empty(), unit.toNanos(timestamp))
- override def addEvent(eventName: String, eventAttributes: Attributes): Span = addEventInternal(eventName, eventAttributes, clock.now)
- override def addEvent(eventName: String, eventAttributes: Attributes, timestamp: Instant): Span = addEventInternal(eventName, eventAttributes, timestamp)
- override def addEvent(eventName: String, eventAttributes: Attributes, timestamp: scala.Long, unit: TimeUnit): Span = addEventInternal(eventName, eventAttributes, unit.toNanos(timestamp))
-
- private def addEventInternal(eventName: String, eventAttributes: Attributes, timestamp: Instant): Span = {
- val timestampNanos = if (timestamp != null)
- TimeUnit.SECONDS.toNanos(timestamp.getEpochSecond) + timestamp.getNano
- else clock.now
- addEventInternal(eventName, eventAttributes, timestampNanos)
+ CoreSpanInfo.builder()
+ .appName(Money.Environment.applicationName)
+ .host(Money.Environment.hostName)
+ .library(library)
+ .id(id)
+ .name(name)
+ .kind(kind)
+ .links(links.asJava)
+ .startTimeNanos(startTimeNanos)
+ .endTimeNanos(endTimeNanos)
+ .hasEnded(ended.get())
+ .durationNanos(calculateDurationNanos)
+ .status(status)
+ .description(description)
+ .notes(noted.toMap[String, Note[_]].asJava)
+ .events(events.asJava)
+ .build()
+
+ override def close(): Unit = end()
+
+ override def addEvent(eventName: String, eventAttributes: Attributes): Span = addEvent(eventName, eventAttributes, clock.now, TimeUnit.NANOSECONDS)
+ override def addEvent(eventName: String, eventAttributes: Attributes, timestamp: scala.Long, unit: TimeUnit): Span = {
+ events += new CoreEvent(eventName, unit.toNanos(timestamp), eventAttributes)
+ this
}
- private def addEventInternal(eventName: String, eventAttributes: Attributes, timestampNanos: scala.Long, exception: Throwable = null): Span = {
- events += CoreEvent(eventName, eventAttributes, timestampNanos, exception)
+ override def recordException(exception: Throwable, eventAttributes: Attributes): Span = {
+ events += new CoreExceptionEvent(exception, clock.now, eventAttributes)
this
}
- override def recordException(exception: Throwable): Span = recordException(exception, Attributes.empty())
- override def recordException(exception: Throwable, eventAttributes: Attributes): Span =
- addEventInternal(SemanticAttributes.EXCEPTION_EVENT_NAME, eventAttributes, clock.now, exception)
-
override def setStatus(canonicalCode: StatusCode): Span = setStatus(canonicalCode, null)
override def setStatus(canonicalCode: StatusCode, description: String): Span = {
@@ -165,12 +122,27 @@ private[core] case class CoreSpan(
this
}
- override def end(): Unit = stop()
- override def `end`(endTimeStamp: Long, unit: TimeUnit): Unit = stop(unit.toNanos(endTimeStamp), StatusCode.UNSET)
+ override def end(): Unit = end(clock.now, TimeUnit.NANOSECONDS)
+
+ override def end(timestamp: Long, unit: TimeUnit): Unit =
+ if (ended.compareAndSet(false, true)) {
+ this.endTimeNanos = unit.toNanos(timestamp)
+
+ // process any hanging timers
+ val openTimers = timers.keys
+ openTimers.foreach(stopTimer)
+
+ scopes.foreach {
+ _.close()
+ }
+ scopes = Nil
+
+ handler.handle(info())
+ }
override def getSpanContext: SpanContext = id.toSpanContext
- override def isRecording: Boolean = startTimeNanos > 0 && endTimeNanos <= 0
+ override def isRecording: Boolean = true
private def calculateDurationNanos: Long =
if (endTimeNanos <= 0L && startTimeNanos <= 0L)
diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala
index 0935bc3a..f55b423e 100644
--- a/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala
+++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpanBuilder.scala
@@ -16,20 +16,20 @@
package com.comcast.money.core
-import java.time.Instant
-import java.util.concurrent.TimeUnit
-import com.comcast.money.api.{ InstrumentationLibrary, Note, Span, SpanBuilder, SpanHandler, SpanId, SpanInfo }
+import com.comcast.money.api._
+import com.comcast.money.core.internal.{ SpanContext, SpanLocal }
import com.comcast.money.core.samplers.{ DropResult, RecordResult, Sampler }
-import io.opentelemetry.api.common.{ AttributeKey, Attributes }
+import io.opentelemetry.api.common.Attributes
+import io.opentelemetry.api.trace.{ SpanKind, TraceFlags, SpanContext => OtelSpanContext }
import io.opentelemetry.context.Context
-import io.opentelemetry.api.trace.{ SpanContext, SpanKind, TraceFlags, Span => OtelSpan }
-import java.util.Optional
+import java.util.concurrent.TimeUnit
import scala.collection.JavaConverters._
private[core] class CoreSpanBuilder(
spanId: Option[SpanId],
- var parentSpan: Option[Span],
+ var parentContext: Context = Context.current(),
+ spanContext: SpanContext = SpanLocal,
spanName: String,
clock: Clock,
handler: SpanHandler,
@@ -40,25 +40,10 @@ private[core] class CoreSpanBuilder(
var spanKind: SpanKind = SpanKind.INTERNAL
var startTimeNanos: Long = 0L
var notes: List[Note[_]] = List()
- var links: List[SpanInfo.Link] = List()
+ var links: List[LinkInfo] = List()
override def setParent(context: Context): SpanBuilder = {
- parentSpan = Option(context)
- .flatMap { ctx => Option(OtelSpan.fromContextOrNull(ctx)) }
- .flatMap {
- case span: Span => Some(span)
- case _ => None
- }
- this
- }
-
- override def setParent(span: Span): SpanBuilder = {
- parentSpan = Option(span)
- this
- }
-
- override def setParent(span: Optional[Span]): SpanBuilder = {
- parentSpan = if (span.isPresent) Some(span.get) else None
+ parentContext = context
this
}
@@ -68,27 +53,15 @@ private[core] class CoreSpanBuilder(
}
override def setNoParent(): SpanBuilder = {
- parentSpan = None
+ parentContext = Context.root
this
}
- override def addLink(spanContext: SpanContext): SpanBuilder = addLink(spanContext, Attributes.empty)
-
- override def addLink(spanContext: SpanContext, attributes: Attributes): SpanBuilder = {
- links = CoreLink(spanContext, attributes) :: links
+ override def addLink(spanContext: OtelSpanContext, attributes: Attributes): SpanBuilder = {
+ links = new CoreLink(spanContext, attributes) :: links
this
}
- override def setAttribute(key: String, value: String): SpanBuilder = setAttribute[String](AttributeKey.stringKey(key), value)
-
- override def setAttribute(key: String, value: Long): SpanBuilder = setAttribute[java.lang.Long](AttributeKey.longKey(key), value)
-
- override def setAttribute(key: String, value: Double): SpanBuilder = setAttribute[java.lang.Double](AttributeKey.doubleKey(key), value)
-
- override def setAttribute(key: String, value: Boolean): SpanBuilder = setAttribute[java.lang.Boolean](AttributeKey.booleanKey(key), value)
-
- override def setAttribute[T](key: AttributeKey[T], value: T): SpanBuilder = record(Note.of(key, value))
-
override def record(note: Note[_]): SpanBuilder = {
notes = note :: notes
this
@@ -104,13 +77,6 @@ private[core] class CoreSpanBuilder(
this
}
- override def setStartTimestamp(startTimestamp: Instant): SpanBuilder = {
- this.startTimeNanos = if (startTimestamp != null)
- TimeUnit.SECONDS.toNanos(startTimestamp.getEpochSecond) + startTimestamp.getNano
- else 0L
- this
- }
-
private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long): Span = CoreSpan(
id = id,
name = name,
@@ -122,6 +88,7 @@ private[core] class CoreSpanBuilder(
handler = handler)
override def startSpan(): Span = {
+ val parentSpan = spanContext.fromContext(parentContext)
val parentSpanId = parentSpan.map { _.info.id }
val spanId = (this.spanId, parentSpanId) match {
diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpanFactory.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpanFactory.scala
index 2160f516..b63601ed 100644
--- a/money-core/src/main/scala/com/comcast/money/core/CoreSpanFactory.scala
+++ b/money-core/src/main/scala/com/comcast/money/core/CoreSpanFactory.scala
@@ -20,6 +20,7 @@ import com.comcast.money.api.{ InstrumentationLibrary, Span, SpanBuilder, SpanFa
import com.comcast.money.core.formatters.Formatter
import com.comcast.money.core.internal.SpanContext
import com.comcast.money.core.samplers.Sampler
+import io.opentelemetry.context.Context
private[core] final case class CoreSpanFactory(
spanContext: SpanContext,
@@ -45,13 +46,19 @@ private[core] final case class CoreSpanFactory(
.setSticky(true)
.startSpan()
- private[core] def spanBuilder(spanName: String, spanId: Option[SpanId] = None, parentSpan: Option[Span] = spanContext.current): SpanBuilder =
+ private[core] def spanBuilder(spanName: String, spanId: Option[SpanId] = None, parentSpan: Option[Span] = spanContext.current): SpanBuilder = {
+ val parentContext = parentSpan match {
+ case Some(s) => Context.current().`with`(s)
+ case None => Context.root()
+ }
new CoreSpanBuilder(
spanId = spanId,
- parentSpan = parentSpan,
+ parentContext = parentContext,
+ spanContext = spanContext,
spanName = spanName,
clock = clock,
handler = handler,
sampler = sampler,
library = library)
+ }
}
diff --git a/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala b/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala
deleted file mode 100644
index 1a4759a2..00000000
--- a/money-core/src/main/scala/com/comcast/money/core/CoreSpanInfo.scala
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2012 Comcast Cable Communications Management, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.comcast.money.core
-
-import java.util.Collections
-import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo }
-import io.opentelemetry.api.trace.{ Span, SpanKind, StatusCode }
-
-private[core] case class CoreSpanInfo(
- id: SpanId,
- name: String,
- kind: SpanKind = SpanKind.INTERNAL,
- startTimeNanos: Long = 0L,
- endTimeNanos: Long = 0L,
- durationNanos: Long = 0L,
- status: StatusCode = StatusCode.UNSET,
- description: String = "",
- notes: java.util.Map[String, Note[_]] = Collections.emptyMap(),
- override val events: java.util.List[SpanInfo.Event] = Collections.emptyList(),
- override val links: java.util.List[SpanInfo.Link] = Collections.emptyList(),
- 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..6cc892b7 100644
--- a/money-core/src/main/scala/com/comcast/money/core/Disabled.scala
+++ b/money-core/src/main/scala/com/comcast/money/core/Disabled.scala
@@ -16,16 +16,15 @@
package com.comcast.money.core
-import java.time.Instant
-import java.util.concurrent.TimeUnit
import com.comcast.money.api._
import com.comcast.money.core.context.ContextStorageFilter
+import com.comcast.money.core.formatters.Formatter
import io.opentelemetry.api.common.{ AttributeKey, Attributes }
+import io.opentelemetry.api.trace.{ SpanContext, SpanKind, StatusCode }
import io.opentelemetry.context.{ Context, ContextStorage, Scope }
-import io.opentelemetry.api.trace.{ SpanContext, SpanKind, StatusCode, Span => OtelSpan }
-import com.comcast.money.core.formatters.Formatter
-import java.util.Optional
+import java.time.Instant
+import java.util.concurrent.TimeUnit
// $COVERAGE-OFF$
object DisabledSpanHandler extends SpanHandler {
@@ -99,10 +98,6 @@ object DisabledSpanFactory extends SpanFactory {
object DisabledSpanBuilder extends SpanBuilder {
override def setParent(context: Context): SpanBuilder = this
- override def setParent(span: Span): SpanBuilder = this
-
- override def setParent(span: Optional[Span]): SpanBuilder = this
-
override def setSticky(sticky: Boolean): SpanBuilder = this
override def setNoParent(): SpanBuilder = this
@@ -134,16 +129,14 @@ object DisabledSpanBuilder extends SpanBuilder {
object DisabledSpan extends Span {
- override def stop(): Unit = ()
-
- override def stop(result: java.lang.Boolean): Unit = ()
-
override def stopTimer(timerKey: String): Unit = ()
override def record(note: Note[_]): Span = this
override def startTimer(timerKey: String): Scope = () => ()
+ override def id(): SpanId = SpanId.getInvalid
+
override def info(): SpanInfo = null
override def attachScope(scope: Scope): Span = this
diff --git a/money-core/src/main/scala/com/comcast/money/core/MoneyTracerProvider.scala b/money-core/src/main/scala/com/comcast/money/core/MoneyTracerProvider.scala
index e8c69e1f..544052c8 100644
--- a/money-core/src/main/scala/com/comcast/money/core/MoneyTracerProvider.scala
+++ b/money-core/src/main/scala/com/comcast/money/core/MoneyTracerProvider.scala
@@ -16,9 +16,7 @@
package com.comcast.money.core
-import com.comcast.money.api.{ InstrumentationLibrary, SpanFactory }
-import io.opentelemetry.api.trace
-import io.opentelemetry.api.trace.TracerProvider
+import com.comcast.money.api.{ InstrumentationLibrary, SpanFactory, Tracer => ApiTracer, TracerProvider }
import scala.collection.concurrent.TrieMap
@@ -26,13 +24,11 @@ final case class MoneyTracerProvider(tracer: Tracer) extends TracerProvider {
private val tracers = new TrieMap[InstrumentationLibrary, Tracer]()
- override def get(instrumentationName: String): trace.Tracer = get(instrumentationName, null)
- override def get(instrumentationName: String, instrumentationVersion: String): trace.Tracer = {
- val library = new InstrumentationLibrary(instrumentationName, instrumentationVersion)
- tracers.getOrElseUpdate(library, {
+ override def get(instrumentationLibrary: InstrumentationLibrary): ApiTracer = {
+ tracers.getOrElseUpdate(instrumentationLibrary, {
tracer.spanFactory match {
case csf: CoreSpanFactory => new Tracer {
- override val spanFactory: SpanFactory = csf.copy(library = library)
+ override val spanFactory: SpanFactory = csf.copy(library = instrumentationLibrary)
}
case _ => tracer
}
diff --git a/money-core/src/main/scala/com/comcast/money/core/Tracer.scala b/money-core/src/main/scala/com/comcast/money/core/Tracer.scala
index 7b11abcd..6cb80aff 100644
--- a/money-core/src/main/scala/com/comcast/money/core/Tracer.scala
+++ b/money-core/src/main/scala/com/comcast/money/core/Tracer.scala
@@ -16,17 +16,18 @@
package com.comcast.money.core
-import java.io.Closeable
-
-import com.comcast.money.api.{ MoneyTracer, Note, Span, SpanBuilder, SpanFactory }
+import com.comcast.money.api
+import com.comcast.money.api.{ Note, Span, SpanBuilder, SpanFactory }
import com.comcast.money.core.internal.{ SpanContext, SpanLocal }
+import io.opentelemetry.api.trace.StatusCode
import io.opentelemetry.context.Scope
-import io.opentelemetry.api.trace.{ StatusCode, Span => OtelSpan }
+
+import java.io.Closeable
/**
* Primary API to be used for tracing
*/
-trait Tracer extends MoneyTracer with Closeable {
+trait Tracer extends api.Tracer with Closeable {
val spanFactory: SpanFactory
diff --git a/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala b/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala
index 6cf849ae..de9eb3dd 100644
--- a/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala
+++ b/money-core/src/main/scala/com/comcast/money/core/UnrecordedSpan.scala
@@ -16,7 +16,6 @@
package com.comcast.money.core
-import java.lang
import java.time.Instant
import java.util.concurrent.TimeUnit
@@ -31,7 +30,8 @@ private[core] final case class UnrecordedSpan(
private var scopes: List[Scope] = Nil
- override def info(): SpanInfo = CoreSpanInfo(spanId, name)
+ override def id(): SpanId = spanId
+ override def info(): SpanInfo = CoreSpanInfo.builder().id(spanId).name(name).build()
override def getSpanContext: SpanContext = spanId.toSpanContext
override def attachScope(scope: Scope): Span = {
@@ -46,8 +46,6 @@ private[core] final case class UnrecordedSpan(
override def isRecording: Boolean = false
// $COVERAGE-OFF$
- override def stop(): Unit = close()
- override def stop(result: lang.Boolean): Unit = close()
override def `end`(): Unit = close()
override def `end`(endTimeStamp: Long, unit: TimeUnit): Unit = close()
diff --git a/money-core/src/main/scala/com/comcast/money/core/internal/SpanLocal.scala b/money-core/src/main/scala/com/comcast/money/core/internal/SpanLocal.scala
index 7fc4d994..669c8735 100644
--- a/money-core/src/main/scala/com/comcast/money/core/internal/SpanLocal.scala
+++ b/money-core/src/main/scala/com/comcast/money/core/internal/SpanLocal.scala
@@ -18,7 +18,6 @@ package com.comcast.money.core.internal
import com.comcast.money.api.Span
import io.opentelemetry.context.{ Context, Scope }
-import io.opentelemetry.api.trace.{ Span => OtelSpan }
/**
* Provides a context for storing SpanIds. Keeps a stack of trace ids so that we
@@ -36,10 +35,7 @@ object SpanLocal extends SpanContext {
def fromContext(context: Context): Option[Span] =
if (context != null) {
- Option(OtelSpan.fromContextOrNull(context)) match {
- case Some(span: Span) => Some(span)
- case _ => None
- }
+ Option(Span.fromContextOrNull(context))
} else None
def clear(): Unit = Context.root.makeCurrent()
diff --git a/money-core/src/main/scala/com/comcast/money/core/japi/JMoney.java b/money-core/src/main/scala/com/comcast/money/core/japi/JMoney.java
deleted file mode 100644
index a03a79be..00000000
--- a/money-core/src/main/scala/com/comcast/money/core/japi/JMoney.java
+++ /dev/null
@@ -1,621 +0,0 @@
-/*
- * Copyright 2012 Comcast Cable Communications Management, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.comcast.money.core.japi;
-
-import io.opentelemetry.api.common.AttributeKey;
-import io.opentelemetry.context.Scope;
-
-import com.comcast.money.api.Note;
-import com.comcast.money.api.Span;
-import com.comcast.money.api.SpanBuilder;
-import com.comcast.money.core.Money;
-import com.comcast.money.core.Tracer;
-
-/**
- * Java API for working directly with money from Java code
- */
-public class JMoney {
- private static Tracer tracer = Money.Environment().tracer();
-
- private JMoney() { }
-
- public static boolean isEnabled() {
-
- return Money.Environment().enabled();
- }
-
- /**
- * @return the {@link com.comcast.money.core.Tracer} to use for tracing
- */
- public static Tracer tracer() {
- return tracer;
- }
-
- protected static void setTracer(Tracer tracer) {
- if (tracer != null) {
- JMoney.tracer = tracer;
- } else {
- JMoney.tracer = Money.Environment().tracer();
- }
- }
-
- /**
- * Records a note in the current trace span if one is present
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- */
- public static void record(String noteName, Double value) {
-
- tracer().record(noteName, value, false);
- }
-
- /**
- * Records a note in the current trace span if one is present
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- * @param propagate whether or not to propagate note to children spans
- */
- public static void record(String noteName, Double value, boolean propagate) {
-
- tracer().record(noteName, value, propagate);
- }
-
- /**
- * Records a note in the current trace span if one is present
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- */
- public static void record(String noteName, String value) {
-
- tracer().record(noteName, value, false);
- }
-
- /**
- * Records a note in the current trace span if one is present
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- * @param propagate whether or not to propagate note to children spans
- */
- public static void record(String noteName, String value, boolean propagate) {
-
- tracer().record(noteName, value, propagate);
- }
-
- /**
- * Records a note in the current trace span if one is present
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- */
- public static void record(String noteName, Long value) {
-
- tracer().record(noteName, value, false);
- }
-
- /**
- * Records a note in the current trace span if one is present
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- * @param propagate whether or not to propagate note to children spans
- */
- public static void record(String noteName, Long value, boolean propagate) {
-
- tracer().record(noteName, value, propagate);
- }
-
- /**
- * Records a note in the current trace span if one is present
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- */
- public static void record(String noteName, boolean value) {
-
- tracer().record(noteName, value, false);
- }
-
- /**
- * Records a note in the current trace span if one is present
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- * @param propagate whether or not to propagate note to children spans
- */
- public static void record(String noteName, boolean value, boolean propagate) {
-
- tracer().record(noteName, value, propagate);
- }
-
- /**
- * Records a note in the current trace span if one is present. If the Object value that is provided
- * is null; or if it is a type other than String, Long, Double, or Boolean, then we record a
- * String Note, and perform a "toString" on the value.
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- * @param propagate whether or not to propagate the note to child spans
- */
- public static void record(String noteName, Object value, boolean propagate) {
-
- tracer().record(toNote(noteName, value, propagate));
- }
-
- /**
- * Records a note in the current trace span if one is present. If the Object value that is provided
- * is null; or if it is a type other than String, Long, Double, or Boolean, then we record a
- * String Note, and perform a "toString" on the value.
- * @param noteName The name of the note to record
- * @param value The value to be recorded on the note
- */
- public static void record(String noteName, Object value) {
-
- record(noteName, value, false);
- }
-
- /**
- * Records a note in the current trace span if one is present.
- * @param key The name and type of the attribute to record
- * @param value The value to be recorded on the note
- */
- public static void record(AttributeKey key, T value) {
- tracer().record(Note.of(key, value));
- }
-
- /**
- * Records a note in the current trace span if one is present.
- * @param key The name and type of the attribute to record
- * @param value The value to be recorded on the note
- * @param propagate whether or not to propagate note to children spans
- */
- public static void record(AttributeKey key, T value, boolean propagate) {
- tracer().record(Note.of(key, value, propagate));
- }
-
- /**
- * Records a note in the current trace span if one is present
- *
- * This will store the current timestamp as the value of the note
- * @param noteName The name of the note to record
- */
- public static void record(String noteName) {
-
- tracer().time(noteName);
- }
-
- /**
- * Starts a timer that will be recorded on the current span
- *
- * You should call {@link Scope#close()} stop the timer.
- *
- * If you do not stop the timer, then the duration of the timer will be whenever
- * the current Span is stopped
- * @param noteName The name of the note that will be recorded for the timer
- */
- public static Scope startTimer(String noteName) {
-
- return tracer().startTimer(noteName);
- }
-
- /**
- * Stops a timer that was previously started on the current span.
- *
- * This will add a note to the span with the name provided. The value will be
- * the duration between the startTimer and stopTimer invocations.
- * @param noteName The name of the timer to stop
- * @deprecated Close the {@link Scope} returned by {@link JMoney#startTimer(String)}
- */
- @Deprecated
- public static void stopTimer(String noteName) {
-
- tracer().stopTimer(noteName);
- }
-
- /**
- * Starts a new span with the given name.
- *
- * If a span already exists, the new span will be a child span of the existing span.
- *
- * If no span exists, the new span will be a root span
- *
- * Use this method carefully, you must be certain to stop the span. If you do not stop the span,
- * it will eventually timeout and no data will be recorded. Under heavy volume, this will
- * add memory pressure to the process
- *
- * @param spanName The name of the span
- * @return the created span
- */
- public static Span startSpan(String spanName) {
-
- return tracer().startSpan(spanName);
- }
-
- /**
- * Creates a new {@link SpanBuilder} for configuring and creating a new span with the given name.
- *
- * If a span already exists, the new span will be a child span of the existing span.
- *
- * If no span exists, the new span will be a root span
- *
- * @param spanName The name of the span
- * @return the {@link SpanBuilder}
- */
- public static SpanBuilder spanBuilder(String spanName) {
-
- return tracer().spanBuilder(spanName);
- }
-
- /**
- * Stops the existing span if one is present.
- *
- * Once the span is stopped, the data for the span will be emitted
- * @param success An indicator of whether or not the span was successful
- * @deprecated Close the {@lin Scope} returned from {@link JMoney#startSpan(String)}
- */
- @Deprecated
- public static void stopSpan(boolean success) {
-
- tracer().stopSpan(success);
- }
-
- /**
- * Stops the existing span if one is present. Assumes a result of success
- * @deprecated Close the {@lin Scope} returned from {@link JMoney#startSpan(String)}
- */
- @Deprecated
- public static void stopSpan() {
- tracer().stopSpan(true);
- }
-
- /**
- * Creates a new span that can be used in Java that is closeable. Once the try
- * block completes the span will automatically be stopped.
- *
- *
- * @param spanName The name of the span
- * @return a {@link com.comcast.money.core.japi.JMoney.TraceSpan} that really can only be closed to stop the span
- */
- public static TraceSpan newSpan(String spanName) {
-
- return newSpan(spanName, true);
- }
-
- /**
- * Creates a new span that can be used in Java that is closeable. Once the try
- * block completes the span will automatically be stopped with the specified
- * success indicator unless changed on the returned span.
- *
- *
- * @param spanName The name of the span
- * @param success An indicator of whether or not the span was successful by default
- * @return a {@link com.comcast.money.core.japi.JMoney.TraceSpan} that really can only be closed to stop the span
- */
- public static TraceSpan newSpan(String spanName, boolean success) {
- startSpan(spanName);
- return new TraceSpan(success);
- }
-
- /**
- * Starts a new timer that can be used in Java that is closeable. Once the try block
- * completes the timer will automatically be stopped.
- *
- *
- * try (JMoney.TraceTimer timer = JMoney.newTimer("timer-name")) {
- * // do something here
- * }
- *
- * @param timerName The name of the timer
- * @return a {@link com.comcast.money.core.japi.JMoney.TraceTimer} that really can only be closed to stop the timer
- */
- public static TraceTimer newTimer(String timerName) {
-
- startTimer(timerName);
- return new TraceTimer(timerName);
- }
-
- /**
- * Creates a span to measure the computation of a task which may throw an exception
- *
- *
- * JMoney.trace("span-name", () -> {
- * // do something here
- * });
- *
- * @param spanName The name of the span
- * @param runnable The task to complete
- * @param the type of checked exception
- * @throws E if unable to complete the task
- */
- public static void trace(String spanName, CheckedRunnable runnable) throws E {
- try (TraceSpan span = newSpan(spanName, false)) {
- runnable.run();
- span.success();
- }
- }
-
- /**
- * Creates a span to measure the computation of a task which may throw an exception
- *
- *
- * @param spanName The name of the span
- * @param consumer The task to complete
- * @param the type of checked exception
- * @throws E if unable to complete the task
- */
- public static void trace(String spanName, CheckedConsumer consumer) throws E {
- try (TraceSpan span = newSpan(spanName)) {
- try {
- consumer.accept(span);
- } catch (Exception exception) {
- span.fail();
- @SuppressWarnings("unchecked")
- E checked = (E) exception;
- throw checked;
- }
- }
- }
-
- /**
- * Creates a span to measure the computation of a task which returns a result and may throw an exception
- *
- *
- * String value = JMoney.trace("span-name", () -> {
- * // do something here
- * return "Result";
- * });
- *
- * @param spanName The name of the span
- * @param callable The task to compute the result
- * @param the type of the result
- * @param the type of checked exception
- * @return the computed result of the task
- * @throws E if unable to complete the task
- */
- public static V trace(String spanName, CheckedCallable callable) throws E {
- try (TraceSpan span = newSpan(spanName, false)) {
- V result = callable.call();
- span.success();
- return result;
- }
- }
-
- /**
- * Creates a span to measure the computation of a task which returns a result and may throw an exception
- *
- *
- * @param spanName The name of the span
- * @param function The task to compute the result
- * @param the type of the result
- * @param the type of checked exception
- * @return the computed result of the task
- * @throws E if unable to complete the task
- */
- public static V trace(String spanName, CheckedFunction function) throws E {
- try (TraceSpan span = newSpan(spanName)) {
- try {
- return function.apply(span);
- } catch (Exception exception) {
- span.fail();
- @SuppressWarnings("unchecked")
- E checked = (E) exception;
- throw checked;
- }
- }
- }
-
- /**
- * Creates a timer to measure the duration of a task
- *
- *
- * JMoney.time("timer-name", () -> {
- * // do something here
- * });
- *
- * @param timerName The name of the timer
- * @param runnable The task to be timed
- * @param the type of checked exception
- * @throws E if unable to complete the task
- */
- public static void time(String timerName, CheckedRunnable runnable) throws E {
- try (TraceTimer timer = newTimer(timerName)) {
- runnable.run();
- }
- }
-
- /**
- * Creates a timer to measure the duration of a task that computes a result
- *
- *
- * String result = JMoney.time("timer-name", () -> {
- * // do something here
- * return "Result";
- * });
- *
- * @param timerName The name of the timer
- * @param callable The task to be timed
- * @param the type of the result
- * @param the type of checked exception
- * @return the computed result of the task
- * @throws E if unable to complete the task
- */
- public static V time(String timerName, CheckedCallable callable) throws E {
- try (TraceTimer timer = newTimer(timerName)) {
- return callable.call();
- }
- }
-
- /**
- * A {@link java.lang.AutoCloseable} that has a close method that will stop the current span
- */
- public static class TraceSpan implements AutoCloseable {
-
- private boolean success;
- private boolean stopped = false;
-
- public TraceSpan() {
- this(true);
- }
-
- public TraceSpan(boolean success) {
- this.success = success;
- }
-
- public void fail() {
- success = false;
- }
-
- public void success() {
- success = true;
- }
-
- @Override public void close() {
- if (!stopped) {
- stopped = true;
- stopSpan(success);
- }
- }
- }
-
- /**
- * A {@link java.lang.AutoCloseable} that has a close method that will stop the timer
- */
- public static class TraceTimer implements AutoCloseable {
-
- private final String noteName;
- private boolean stopped = false;
-
- TraceTimer(String noteName) {
- this.noteName = noteName;
- }
-
- @Override
- public void close() {
- if (!stopped) {
- stopped = true;
- stopTimer(noteName);
- }
- }
- }
-
- /**
- * A task that returns a result and may throw an exception.
- * @param the type of the result
- * @param the type of checked exception
- */
- @FunctionalInterface
- public interface CheckedCallable {
- /**
- * Computes a result or throws a checked exception
- * @return the computed result
- * @throws E if unable to compute a result
- */
- V call() throws E;
- }
-
- /**
- * A task that does not return a result and may throw an exception
- * @param the type of checked exception
- */
- @FunctionalInterface
- public interface CheckedRunnable {
- /**
- * Runs a task or throws a checked exception
- * @throws E if unable to complete the task
- */
- void run() throws E;
- }
-
- /**
- * A task that accepts a single input, does not return a result and may throw an exception
- * @param the type of the input to the task
- * @param the type of checked exception
- */
- @FunctionalInterface
- public interface CheckedConsumer {
- /**
- * Runs a task with the accepted input
- * @param value the input argument
- * @throws E if unable to complete the task
- */
- void accept(V value) throws E;
- }
-
- /**
- * A task that accepts a single input, returns a result and may throw an exception
- * @param the type of the input to the task
- * @param the type of the result
- * @param the type of checked exception
- */
- @FunctionalInterface
- public interface CheckedFunction {
- /**
- * Runs a task with the accepted input and computes a result
- * @param value the input argument
- * @return the computed result
- * @throws E if unable to complete the task
- */
- R apply(V value) throws E;
- }
-
- private static Note> toNote(String name, Object value, boolean propagate) {
-
- if (value == null) {
- return Note.of(name, null, propagate);
- } else if (value instanceof Boolean) {
- Boolean bool = (Boolean)value;
- return Note.of(name, bool, propagate);
- } else if (value instanceof Double) {
- Double dbl = (Double)value;
- return Note.of(name, dbl, propagate);
- } else if (value instanceof Long) {
- Long lng = (Long)value;
- return Note.of(name, lng, propagate);
- } else {
- return Note.of(name, value.toString(), propagate);
- }
- }
-}
diff --git a/money-core/src/main/scala/com/comcast/money/core/logging/MethodTracer.scala b/money-core/src/main/scala/com/comcast/money/core/logging/MethodTracer.scala
index 67982741..2a6938c1 100644
--- a/money-core/src/main/scala/com/comcast/money/core/logging/MethodTracer.scala
+++ b/money-core/src/main/scala/com/comcast/money/core/logging/MethodTracer.scala
@@ -54,16 +54,16 @@ trait MethodTracer extends Reflections with TraceLogging {
case Some(future) =>
future
case None =>
- span.stop(true)
+ span.end(true)
result
}
case Success(result) =>
- span.stop(true)
+ span.end(true)
result
case Failure(exception) =>
logException(exception)
span.recordException(exception)
- span.stop(exceptionMatches(exception, annotation.ignoredExceptions()))
+ span.end(exceptionMatches(exception, annotation.ignoredExceptions()))
throw exception
}
} finally {
@@ -119,7 +119,7 @@ trait MethodTracer extends Reflections with TraceLogging {
}
// stop the captured span with the success/failure flag
- span.stop(result)
+ span.end(result)
} finally {
// reset the current thread context
scope.close()
diff --git a/money-core/src/test/scala/com/comcast/money/core/CoreSpanBuilderSpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanBuilderSpec.scala
index b53a1a61..ecb0dace 100644
--- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanBuilderSpec.scala
+++ b/money-core/src/test/scala/com/comcast/money/core/CoreSpanBuilderSpec.scala
@@ -32,6 +32,7 @@ import org.scalatestplus.mockito.MockitoSugar
import scala.collection.JavaConverters._
import InstantImplicits._
+import com.comcast.money.core.internal.SpanLocal
import java.util.Optional
@@ -46,7 +47,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val span = mock[Span]
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) {
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) {
override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = {
name shouldBe "test"
span
@@ -65,7 +66,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val span = mock[Span]
when(sampler.shouldSample(argEq(spanId), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(Some(spanId), None, "test", clock, handler, sampler, library) {
+ val underTest = new CoreSpanBuilder(Some(spanId), Context.root(), SpanLocal, "test", clock, handler, sampler, library) {
override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = {
id shouldBe spanId
name shouldBe "test"
@@ -85,11 +86,13 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val parentSpanInfo = mock[SpanInfo]
val parentSpan = mock[Span]
val span = mock[Span]
+ when(parentSpan.storeInContext(any())).thenCallRealMethod()
when(parentSpan.info).thenReturn(parentSpanInfo)
when(parentSpanInfo.id).thenReturn(parentSpanId)
when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, Some(parentSpan), "test", clock, handler, sampler, library) {
+ val parentContext = Context.root.`with`(parentSpan)
+ val underTest = new CoreSpanBuilder(None, parentContext, SpanLocal, "test", clock, handler, sampler, library) {
override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = {
id.traceId shouldBe parentSpanId.traceId
id.parentId shouldBe parentSpanId.selfId
@@ -111,10 +114,11 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val parentSpan = mock[Span]
val span = mock[Span]
when(parentSpan.info).thenReturn(parentSpanInfo)
+ when(parentSpan.storeInContext(any())).thenCallRealMethod()
when(parentSpanInfo.id).thenReturn(parentSpanId)
when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) {
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) {
override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = {
id.traceId shouldBe parentSpanId.traceId
id.parentId shouldBe parentSpanId.selfId
@@ -124,65 +128,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
}
val result = underTest
- .setParent(parentSpan)
- .startSpan()
- result shouldBe span
- }
-
- "create a child span with an explicit parent span wrapped in an Option" in {
- val handler = mock[SpanHandler]
- val sampler = mock[Sampler]
-
- val parentSpanId = SpanId.createNew()
- val parentSpanInfo = mock[SpanInfo]
- val parentSpan = mock[Span]
- val span = mock[Span]
- when(parentSpan.info).thenReturn(parentSpanInfo)
- when(parentSpanInfo.id).thenReturn(parentSpanId)
- when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
-
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) {
- override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = {
- id.traceId shouldBe parentSpanId.traceId
- id.parentId shouldBe parentSpanId.selfId
- name shouldBe "test"
- span
- }
- }
-
- val result = underTest
- .setParent(Optional.of(parentSpan))
- .startSpan()
-
- result shouldBe span
- }
-
- "create a child span with an explicit parent span wrapped in an Context" in {
- val handler = mock[SpanHandler]
- val sampler = mock[Sampler]
-
- val parentSpanId = SpanId.createNew()
- val parentSpanInfo = mock[SpanInfo]
- val parentSpan = mock[Span]
- val span = mock[Span]
- when(parentSpan.info).thenReturn(parentSpanInfo)
- when(parentSpanInfo.id).thenReturn(parentSpanId)
- when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
-
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) {
- override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = {
- id.traceId shouldBe parentSpanId.traceId
- id.parentId shouldBe parentSpanId.selfId
- name shouldBe "test"
- span
- }
- }
-
- when(parentSpan.storeInContext(Context.root())).thenCallRealMethod()
- val context = Context.root.`with`(parentSpan)
-
- val result = underTest
- .setParent(context)
+ .setParent(Context.root().`with`(parentSpan))
.startSpan()
result shouldBe span
}
@@ -194,7 +140,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val parentSpan = mock[Span]
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, Some(parentSpan), "test", clock, handler, sampler, library) {
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) {
override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = {
name shouldBe "test"
span
@@ -213,7 +159,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val span = mock[Span]
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) {
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) {
override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span
}
@@ -253,6 +199,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val parentSpan = mock[Span]
val span = mock[Span]
when(parentSpan.info).thenReturn(parentSpanInfo)
+ when(parentSpan.storeInContext(any())).thenCallRealMethod()
when(parentSpanInfo.id).thenReturn(parentSpanId)
val notes: Map[String, Note[_]] = Map(
"test" -> Note.of("some", "note", true),
@@ -260,7 +207,8 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
when(parentSpanInfo.notes).thenReturn(notes.asJava)
when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, Some(parentSpan), "test", clock, handler, sampler, library) {
+ val parentContext = Context.root.`with`(parentSpan)
+ val underTest = new CoreSpanBuilder(None, parentContext, SpanLocal, "test", clock, handler, sampler, library) {
override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span
}
@@ -284,6 +232,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val parentSpanInfo = mock[SpanInfo]
val parentSpan = mock[Span]
val span = mock[Span]
+ when(parentSpan.storeInContext(any())).thenCallRealMethod()
when(parentSpan.info).thenReturn(parentSpanInfo)
when(parentSpanInfo.id).thenReturn(parentSpanId)
val notes: Map[String, Note[_]] = Map(
@@ -292,7 +241,8 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
when(parentSpanInfo.notes).thenReturn(notes.asJava)
when(sampler.shouldSample(any(), argEq(Some(parentSpanId)), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, Some(parentSpan), "test", clock, handler, sampler, library) {
+ val parentContext = Context.root.`with`(parentSpan)
+ val underTest = new CoreSpanBuilder(None, parentContext, SpanLocal, "test", clock, handler, sampler, library) {
override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span
}
@@ -309,7 +259,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val sampler = mock[Sampler]
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library)
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library)
val result = underTest
.setSpanKind(SpanKind.SERVER)
@@ -323,7 +273,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val sampler = mock[Sampler]
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.Drop)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library)
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library)
val result = underTest
.setSpanKind(SpanKind.SERVER)
@@ -337,7 +287,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val sampler = mock[Sampler]
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library)
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library)
val result = underTest.startSpan()
@@ -349,7 +299,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val sampler = mock[Sampler]
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.Record)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library)
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library)
val result = underTest.startSpan()
@@ -364,7 +314,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
when(sampler.shouldSample(any(), argEq(None), argEq("test")))
.thenReturn(SamplerResult.RecordAndSample.withNote(Note.of("sampler", "note")))
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library) {
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library) {
override private[core] def createSpan(id: SpanId, name: String, kind: SpanKind, startTimeNanos: Long) = span
}
@@ -383,7 +333,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val sampler = mock[Sampler]
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library)
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library)
val result = underTest
.setStartTimestamp(12345789L, TimeUnit.NANOSECONDS)
@@ -397,7 +347,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
val sampler = mock[Sampler]
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library)
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library)
val instant = Instant.now
val result = underTest
@@ -413,7 +363,7 @@ class CoreSpanBuilderSpec extends AnyWordSpec with Matchers with MockitoSugar {
when(sampler.shouldSample(any(), argEq(None), argEq("test"))).thenReturn(SamplerResult.RecordAndSample)
- val underTest = new CoreSpanBuilder(None, None, "test", clock, handler, sampler, library)
+ val underTest = new CoreSpanBuilder(None, Context.root(), SpanLocal, "test", clock, handler, sampler, library)
val linkedContext = SpanContext.create(IdGenerator.generateRandomTraceIdAsHex(), IdGenerator.generateRandomIdAsHex(), TraceFlags.getSampled, TraceState.getDefault)
val attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar")
diff --git a/money-core/src/test/scala/com/comcast/money/core/CoreSpanFactorySpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanFactorySpec.scala
index e20bc006..fe70ff68 100644
--- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanFactorySpec.scala
+++ b/money-core/src/test/scala/com/comcast/money/core/CoreSpanFactorySpec.scala
@@ -19,7 +19,7 @@ package com.comcast.money.core
import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanHandler, SpanId }
import com.comcast.money.core.formatters.MoneyTraceFormatter
import com.comcast.money.core.handlers.TestData
-import com.comcast.money.core.internal.SpanContext
+import com.comcast.money.core.internal.{ SpanContext, SpanLocal }
import com.comcast.money.core.samplers.{ AlwaysOffSampler, AlwaysOnSampler, RecordResult, Sampler, SamplerResult }
import io.opentelemetry.api.trace.TraceFlags
import org.scalatest.matchers.should.Matchers
@@ -28,7 +28,7 @@ import org.scalatestplus.mockito.MockitoSugar
class CoreSpanFactorySpec extends AnyWordSpec with Matchers with MockitoSugar with TestData {
- val context = mock[SpanContext]
+ val context = SpanLocal
val handler = mock[SpanHandler]
val formatter = MoneyTraceFormatter
val sampler = AlwaysOnSampler
diff --git a/money-core/src/test/scala/com/comcast/money/core/CoreSpanInfoSpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanInfoSpec.scala
deleted file mode 100644
index ca49c270..00000000
--- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanInfoSpec.scala
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2012 Comcast Cable Communications Management, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.comcast.money.core
-
-import com.comcast.money.api.SpanId
-import org.scalatest.matchers.should.Matchers
-import org.scalatest.wordspec.AnyWordSpec
-
-class CoreSpanInfoSpec extends AnyWordSpec with Matchers {
-
- "CoreSpanInfo" should {
- "have acceptable default values" in {
- val spanId = SpanId.createNew()
- val underTest = CoreSpanInfo(spanId, "test")
-
- underTest.id shouldBe spanId
- underTest.name shouldBe "test"
- underTest.appName shouldBe Money.Environment.applicationName
- underTest.host shouldBe Money.Environment.hostName
- underTest.library shouldBe Money.InstrumentationLibrary
- underTest.notes shouldBe empty
- underTest.success shouldBe null
- underTest.durationMicros shouldBe 0L
- underTest.startTimeMicros shouldBe 0L
- underTest.startTimeMillis shouldBe 0L
- underTest.endTimeMicros shouldBe 0L
- underTest.endTimeMillis shouldBe 0L
- }
- }
-}
diff --git a/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala b/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala
index 6c7a41dd..4ea28fd0 100644
--- a/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala
+++ b/money-core/src/test/scala/com/comcast/money/core/CoreSpanSpec.scala
@@ -124,7 +124,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
val event = underTest.info.events.get(0)
event.name shouldBe "event"
event.attributes should have size 0
- event.timestamp should not be 0L
+ event.timestampNanos should not be 0L
event.exception should be(null)
}
@@ -137,7 +137,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
val event = underTest.info.events.get(0)
event.name shouldBe "event"
event.attributes should have size 0
- event.timestamp shouldBe 100L
+ event.timestampNanos shouldBe 100L
event.exception should be(null)
}
@@ -151,7 +151,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
val event = underTest.info.events.get(0)
event.name shouldBe "event"
event.attributes should have size 0
- event.timestamp shouldBe instant.toEpochNano
+ event.timestampNanos shouldBe instant.toEpochNano
event.exception should be(null)
}
@@ -178,7 +178,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
event.name shouldBe "event"
event.attributes should have size 1
event.attributes.get(AttributeKey.stringKey("foo")) shouldBe "bar"
- event.timestamp shouldBe 100L
+ event.timestampNanos shouldBe 100L
event.exception should be(null)
}
@@ -193,7 +193,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
event.name shouldBe "event"
event.attributes should have size 1
event.attributes.get(AttributeKey.stringKey("foo")) shouldBe "bar"
- event.timestamp shouldBe instant.toEpochNano
+ event.timestampNanos shouldBe instant.toEpochNano
event.exception should be(null)
}
@@ -210,7 +210,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
event.attributes.get(SemanticAttributes.EXCEPTION_TYPE) shouldBe "java.lang.RuntimeException"
event.attributes.get(SemanticAttributes.EXCEPTION_MESSAGE) shouldBe "BOOM"
event.attributes.get(SemanticAttributes.EXCEPTION_STACKTRACE) should startWith("java.lang.RuntimeException: BOOM")
- event.timestamp should not be 0
+ event.timestampNanos should not be 0
event.exception shouldBe exception
}
@@ -228,7 +228,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
event.attributes.get(SemanticAttributes.EXCEPTION_MESSAGE) shouldBe "BOOM"
event.attributes.get(SemanticAttributes.EXCEPTION_STACKTRACE) should startWith("java.lang.RuntimeException: BOOM")
event.attributes.get(AttributeKey.stringKey("foo")) shouldBe "bar"
- event.timestamp should not be 0
+ event.timestampNanos should not be 0
event.exception shouldBe exception
}
@@ -239,7 +239,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
underTest.info.success shouldBe (null)
- underTest.stop()
+ underTest.end()
underTest.info.success shouldBe (true)
}
@@ -251,7 +251,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
underTest.info.success shouldBe (null)
- underTest.stop()
+ underTest.end()
underTest.info.success shouldBe (false)
}
@@ -263,7 +263,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
underTest.info.success shouldBe (null)
- underTest.stop(false)
+ underTest.end(false)
underTest.info.success shouldBe (false)
}
@@ -275,7 +275,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
underTest.info.success shouldBe (null)
- underTest.stop(true)
+ underTest.end(true)
underTest.info.success shouldBe (true)
}
@@ -305,10 +305,6 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
val underTest = CoreSpan(SpanId.createNew(), "test")
underTest.isRecording shouldBe true
-
- underTest.stop()
-
- underTest.isRecording shouldBe false
}
"gets SpanContext" in {
@@ -326,7 +322,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
when(clock.now).thenReturn(3000000000L)
val underTest = CoreSpan(SpanId.createNew(), "test", startTimeNanos = 1000000000, clock = clock)
- underTest.stop(true)
+ underTest.end(true)
val state = underTest.info
@@ -339,11 +335,11 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
"invoke the span handler when stopped" in {
val handler = mock[SpanHandler]
- val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo])
+ val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo])
val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now)
underTest.record(testLongNote)
- underTest.stop(true)
+ underTest.end(true)
verify(handler).handle(handleCaptor.capture())
@@ -359,7 +355,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
"invoke the span handler when closed" in {
val handler = mock[SpanHandler]
- val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo])
+ val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo])
val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now)
underTest.record(testLongNote)
@@ -386,7 +382,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
underTest.attachScope(scope1)
underTest.attachScope(scope2)
- underTest.stop()
+ underTest.end()
verify(scope1).close()
verify(scope2).close()
@@ -394,7 +390,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
"invoke the span handler when ended" in {
val handler = mock[SpanHandler]
- val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo])
+ val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo])
val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now)
underTest.record(testLongNote)
@@ -414,7 +410,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
"invoke the span handler when ended with timestamp" in {
val handler = mock[SpanHandler]
- val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo])
+ val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo])
val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now)
underTest.record(testLongNote)
@@ -434,7 +430,7 @@ class CoreSpanSpec extends AnyWordSpec with Matchers with TestData with MockitoS
"invoke the span handler when ended with instant" in {
val handler = mock[SpanHandler]
- val handleCaptor = ArgumentCaptor.forClass(classOf[SpanInfo])
+ val handleCaptor: ArgumentCaptor[SpanInfo] = ArgumentCaptor.forClass(classOf[SpanInfo])
val underTest = CoreSpan(SpanId.createNew(), "test", handler = handler, startTimeNanos = SystemClock.now)
val instant = Instant.now
diff --git a/money-core/src/test/scala/com/comcast/money/core/handlers/MetricsHandlerSpec.scala b/money-core/src/test/scala/com/comcast/money/core/handlers/MetricsHandlerSpec.scala
index aab5681e..a40c4855 100644
--- a/money-core/src/test/scala/com/comcast/money/core/handlers/MetricsHandlerSpec.scala
+++ b/money-core/src/test/scala/com/comcast/money/core/handlers/MetricsHandlerSpec.scala
@@ -17,6 +17,7 @@
package com.comcast.money.core.handlers
import com.codahale.metrics.{ Histogram, Meter, MetricRegistry }
+import com.comcast.money.core.CoreSpanInfo
import com.typesafe.config.Config
import io.opentelemetry.api.trace.StatusCode
import org.mockito.Mockito._
@@ -61,7 +62,7 @@ class MetricsHandlerSpec extends AnyWordSpec with Matchers with MockitoSugar wit
when(underTest.metricRegistry.histogram(anyString())).thenReturn(latencyMetric)
when(underTest.metricRegistry.meter(anyString())).thenReturn(errorMetric)
- underTest.handle(testSpanInfo.copy(status = StatusCode.ERROR))
+ underTest.handle(testSpanInfo.toBuilder.status(StatusCode.ERROR).build())
verify(latencyMetric).update(testSpanInfo.durationMicros)
verify(errorMetric).mark()
diff --git a/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala b/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala
index b425dbae..bf133354 100644
--- a/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala
+++ b/money-core/src/test/scala/com/comcast/money/core/handlers/SpanLogFormatterSpec.scala
@@ -38,27 +38,31 @@ class SpanLogFormatterSpec extends AnyWordSpec with Matchers {
val spanLogFormatter = SpanLogFormatter(emitterConf)
val spanId = SpanId.createNew()
- val sampleData = CoreSpanInfo(
- id = spanId,
- startTimeNanos = 1000000L,
- endTimeNanos = 26000000L,
- durationNanos = 35000000L,
- name = "key",
- appName = "unknown",
- host = "host",
- notes = Map[String, Note[_]]("bob" -> Note.of("bob", "craig"), "what" -> Note.of("what", 1L), "when" -> Note.of("when", 2L)).asJava,
- status = StatusCode.OK)
+ val sampleData = CoreSpanInfo.builder()
+ .id(spanId)
+ .startTimeNanos(1000000L)
+ .endTimeNanos(26000000L)
+ .durationNanos(35000000L)
+ .hasEnded(true)
+ .name("key")
+ .appName("unknown")
+ .host("host")
+ .notes(Map[String, Note[_]]("bob" -> Note.of("bob", "craig"), "what" -> Note.of("what", 1L), "when" -> Note.of("when", 2L)).asJava)
+ .status(StatusCode.OK)
+ .build()
- val withNull = CoreSpanInfo(
- id = spanId,
- startTimeNanos = 1000000L,
- endTimeNanos = 26000000L,
- durationNanos = 35000000L,
- name = "key",
- appName = "unknown",
- host = "host",
- notes = Map[String, Note[_]]("empty" -> Note.of("empty", null)).asJava,
- status = StatusCode.OK)
+ val withNull = CoreSpanInfo.builder()
+ .id(spanId)
+ .startTimeNanos(1000000L)
+ .endTimeNanos(26000000L)
+ .durationNanos(35000000L)
+ .hasEnded(true)
+ .name("key")
+ .appName("unknown")
+ .host("host")
+ .notes(Map[String, Note[_]]("empty" -> Note.of("empty", null)).asJava)
+ .status(StatusCode.OK)
+ .build()
"A LogEmitter must" must {
"have a correctly formatted message" in {
diff --git a/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala b/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala
index ed6338dd..d82299ce 100644
--- a/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala
+++ b/money-core/src/test/scala/com/comcast/money/core/handlers/TestData.scala
@@ -42,17 +42,19 @@ trait TestData {
val clock: Clock = SystemClock
- val testSpanInfo = CoreSpanInfo(
- 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 testSpanInfo = CoreSpanInfo.builder()
+ .id(SpanId.createNew())
+ .startTimeNanos(clock.now)
+ .endTimeNanos(clock.now)
+ .durationNanos(123456000L)
+ .hasEnded(true)
+ .status(StatusCode.OK)
+ .name("test-span")
+ .appName("test")
+ .host("localhost")
+ .notes(Map[String, Note[_]]("str" -> testStringNote, "lng" -> testLongNote, "dbl" -> testDoubleNote, "bool" -> testBooleanNote).asJava)
+ .events(Collections.emptyList())
+ .build()
val testSpanId = SpanId.createNew()
val testSpan = CoreSpan(testSpanId, "test-span")
@@ -60,15 +62,17 @@ trait TestData {
val childSpan = CoreSpan(childSpanId, "child-span")
val fixedTestSpanId = SpanId.createRemote("5092ddfe-3701-4f84-b3d2-21f5501c0d28", 5176425846116696835L, 5176425846116696835L, TraceFlags.getSampled, TraceState.getDefault)
- val fixedTestSpanInfo = CoreSpanInfo(
- id = fixedTestSpanId,
- startTimeNanos = 100000000L,
- endTimeNanos = 300000000L,
- durationNanos = 200000L,
- status = StatusCode.OK,
- name = "test-span",
- appName = "test",
- host = "localhost",
- notes = Map[String, Note[_]]("str" -> testStringNote, "lng" -> testLongNote, "dbl" -> testDoubleNote, "bool" -> testBooleanNote).asJava,
- events = Collections.emptyList())
+ val fixedTestSpanInfo = CoreSpanInfo.builder()
+ .id(fixedTestSpanId)
+ .startTimeNanos(100000000L)
+ .endTimeNanos(300000000L)
+ .durationNanos(200000L)
+ .hasEnded(true)
+ .status(StatusCode.OK)
+ .name("test-span")
+ .appName("test")
+ .host("localhost")
+ .notes(Map[String, Note[_]]("str" -> testStringNote, "lng" -> testLongNote, "dbl" -> testDoubleNote, "bool" -> testBooleanNote).asJava)
+ .events(Collections.emptyList())
+ .build()
}
diff --git a/money-core/src/test/scala/com/comcast/money/core/japi/JMoneySpec.scala b/money-core/src/test/scala/com/comcast/money/core/japi/JMoneySpec.scala
deleted file mode 100644
index a27cb559..00000000
--- a/money-core/src/test/scala/com/comcast/money/core/japi/JMoneySpec.scala
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright 2012 Comcast Cable Communications Management, LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.comcast.money.core.japi
-
-import com.comcast.money.api.{ Note, Span, SpanFactory, SpanId }
-import com.comcast.money.core.handlers.TestData
-import com.comcast.money.core.Tracer
-import com.comcast.money.core.internal.SpanLocal
-import com.comcast.money.core.japi.JMoney._
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers._
-import org.mockito.Mockito._
-import org.scalatest._
-import org.scalatest.wordspec.AnyWordSpec
-import org.scalatest.matchers.should.Matchers
-import org.scalatestplus.mockito.MockitoSugar
-
-class JMoneySpec extends AnyWordSpec
- with Matchers with MockitoSugar with TestData with BeforeAndAfterEach with OneInstancePerTest {
-
- val tracer = mock[Tracer]
-
- override def beforeEach() = {
- SpanLocal.clear()
- reset(tracer)
- JMoney.setTracer(tracer)
- }
-
- override def afterEach() = {
- SpanLocal.clear()
- JMoney.setTracer(null)
- }
-
- "Java API" should {
- "starts a span" in {
- JMoney.startSpan("java-span")
- verify(tracer).startSpan("java-span")
- }
- "stops a span" in {
- JMoney.stopSpan(true)
- verify(tracer).stopSpan(true)
- }
- "support closeable" in {
- val span: TraceSpan = JMoney.newSpan("closeable")
- span.close()
- span.close()
-
- verify(tracer).startSpan("closeable")
- verify(tracer, times(1)).stopSpan(true)
- }
- "support failed closeable" in {
- val span: TraceSpan = JMoney.newSpan("closeable")
- span.fail()
- span.close()
- span.close()
-
- verify(tracer).startSpan("closeable")
- verify(tracer, times(1)).stopSpan(false)
- }
- "support closeable with failed default" in {
- val span: TraceSpan = JMoney.newSpan("closeable", false)
- span.close()
- span.close()
-
- verify(tracer).startSpan("closeable")
- verify(tracer, times(1)).stopSpan(false)
- }
- "support successful closeable with failed default" in {
- val span: TraceSpan = JMoney.newSpan("closeable", false)
- span.success()
- span.close()
- span.close()
-
- verify(tracer).startSpan("closeable")
- verify(tracer, times(1)).stopSpan(true)
- }
- "support runnable lambda" in {
- val runnable = mock[CheckedRunnable[Exception]]
- JMoney.trace("runnable", runnable)
-
- verify(tracer).startSpan("runnable")
- verify(runnable).run()
- verify(tracer, times(1)).stopSpan(true)
- }
- "support throwing runnable lambda" in {
- val runnable = mock[CheckedRunnable[Exception]]
- doThrow(new Exception()).when(runnable).run()
-
- assertThrows[Exception] {
- JMoney.trace("runnable", runnable)
- }
-
- verify(tracer).startSpan("runnable")
- verify(runnable).run()
- verify(tracer, times(1)).stopSpan(false)
- }
- "support consumer lambda with span" in {
- val consumer = mock[CheckedConsumer[TraceSpan, Exception]]
-
- JMoney.trace("consumer", consumer)
-
- verify(tracer).startSpan("consumer")
- verify(consumer).accept(any())
- verify(tracer).stopSpan(true)
- }
- "support consumer lambda with failed span" in {
- val consumer: CheckedConsumer[TraceSpan, Exception] = span => { span.fail() }
-
- JMoney.trace("consumer", consumer)
-
- verify(tracer).startSpan("consumer")
- verify(tracer).stopSpan(false)
- }
- "support throwing consumer lambda" in {
- val consumer = mock[CheckedConsumer[TraceSpan, Exception]]
- doThrow(new Exception()).when(consumer).accept(any())
-
- assertThrows[Exception] {
- JMoney.trace("consumer", consumer)
- }
-
- verify(tracer).startSpan("consumer")
- verify(consumer).accept(any())
- verify(tracer).stopSpan(false)
- }
- "support callable lambda" in {
- val callable = mock[CheckedCallable[String, Exception]]
- when(callable.call()).thenReturn("Hello")
- val result = JMoney.trace("callable", callable)
-
- result shouldBe "Hello"
-
- verify(tracer).startSpan("callable")
- verify(callable).call()
- verify(tracer).stopSpan(true)
- }
- "support throwing callable lambda" in {
- val callable = mock[CheckedCallable[String, Exception]]
- doThrow(new Exception()).when(callable).call()
- assertThrows[Exception] {
- JMoney.trace("callable", callable)
- }
-
- verify(tracer).startSpan("callable")
- verify(callable).call
- verify(tracer).stopSpan(false)
- }
- "support function lambda with span" in {
- val function = mock[CheckedFunction[TraceSpan, String, Exception]]
- when(function.apply(any())).thenReturn("Hello")
-
- val result = JMoney.trace("function", function)
-
- result shouldBe "Hello"
-
- verify(tracer).startSpan("function")
- verify(function).apply(any())
- verify(tracer).stopSpan(true)
- }
- "support function lambda with failed span" in {
- val function: CheckedFunction[TraceSpan, String, Exception] = span => {
- span.fail()
- "Hello"
- }
-
- val result = JMoney.trace("function", function)
-
- result shouldBe "Hello"
-
- verify(tracer).startSpan("function")
- verify(tracer).stopSpan(false)
- }
- "support throwing function lambda" in {
- val function = mock[CheckedFunction[TraceSpan, String, Exception]]
- doThrow(new Exception()).when(function).apply(any())
-
- assertThrows[Exception] {
- JMoney.trace("function", function)
- }
-
- verify(tracer).startSpan("function")
- verify(function).apply(any())
- verify(tracer).stopSpan(false)
- }
- "start a timer" in {
- JMoney.startTimer("the-timer")
- verify(tracer).startTimer("the-timer")
- }
- "stop a timer" in {
- JMoney.stopTimer("the-timer")
- verify(tracer).stopTimer("the-timer")
- }
- "support closeable timer" in {
- val timer: TraceTimer = JMoney.newTimer("the-timer")
- timer.close()
- timer.close()
-
- verify(tracer).startTimer("the-timer")
- verify(tracer, times(1)).stopTimer("the-timer")
- }
- "support runnable timer lambda" in {
- val runnable = mock[CheckedRunnable[Exception]]
-
- JMoney.time("the-timer", runnable)
-
- verify(tracer).startTimer("the-timer")
- verify(runnable).run()
- verify(tracer).stopTimer("the-timer")
- }
- "support throwing runnable timer lambda" in {
- val runnable = mock[CheckedRunnable[Exception]]
- doThrow(new Exception()).when(runnable).run()
-
- assertThrows[Exception] {
- JMoney.time("the-timer", runnable)
- }
-
- verify(tracer).startTimer("the-timer")
- verify(runnable).run()
- verify(tracer).stopTimer("the-timer")
- }
- "support callable timer lambda" in {
- val callable = mock[CheckedCallable[String, Exception]]
- when(callable.call()).thenReturn("Hello")
-
- val result = JMoney.time("the-timer", callable)
-
- result shouldBe "Hello"
-
- verify(tracer).startTimer("the-timer")
- verify(callable).call()
- verify(tracer).stopTimer("the-timer")
- }
- "support throwing callable timer lambda" in {
- val callable = mock[CheckedCallable[String, Exception]]
- doThrow(new Exception()).when(callable).call()
-
- assertThrows[Exception] {
- JMoney.time("the-timer", callable)
- }
-
- verify(tracer).startTimer("the-timer")
- verify(callable).call()
- verify(tracer).stopTimer("the-timer")
- }
- "record long values" in {
- JMoney.record("the-long", java.lang.Long.valueOf(1L))
- verify(tracer).record("the-long", java.lang.Long.valueOf(1L), false)
- }
- "record propagate-able long values" in {
- JMoney.record("the-long", java.lang.Long.valueOf(1L), true)
- verify(tracer).record("the-long", java.lang.Long.valueOf(1L), true)
- }
- "record boolean values" in {
- JMoney.record("the-bool", true)
- verify(tracer).record("the-bool", true, false)
- }
- "record propagate-able boolean values" in {
- JMoney.record("the-bool", true, true)
- verify(tracer).record("the-bool", true, true)
- }
- "record double values" in {
- JMoney.record("the-double", java.lang.Double.valueOf(3.14))
- verify(tracer).record("the-double", java.lang.Double.valueOf(3.14), false)
- }
- "record propagate-able double values" in {
- JMoney.record("the-double", java.lang.Double.valueOf(3.14), true)
- verify(tracer).record("the-double", java.lang.Double.valueOf(3.14), true)
- }
- "record string values" in {
- JMoney.record("the-string", "yo")
- verify(tracer).record("the-string", "yo", false)
- }
- "record propagate-able string values" in {
- JMoney.record("the-string", "yo", true)
- verify(tracer).record("the-string", "yo", true)
- }
- "record a timestamp" in {
- JMoney.record("stamp-this")
- verify(tracer).time("stamp-this")
- }
- "record a long as an Object" in {
- val lng: java.lang.Long = 100L
- val obj: AnyRef = lng
- JMoney.record("long", obj)
-
- val captor = ArgumentCaptor.forClass(classOf[Note[_]])
- verify(tracer).record(captor.capture())
- val note = captor.getValue
- note.name shouldBe "long"
- note.value shouldBe 100L
- }
- "record a boolean as an Object" in {
- val boo: java.lang.Boolean = true
- val obj: AnyRef = boo
- JMoney.record("bool", obj)
-
- val captor = ArgumentCaptor.forClass(classOf[Note[_]])
- verify(tracer).record(captor.capture())
- val note = captor.getValue
- note.name shouldBe "bool"
- note.value shouldBe true
- }
- "record a double as an Object" in {
- val dbl: java.lang.Double = 3.14
- val obj: AnyRef = dbl
- JMoney.record("double", obj)
-
- val captor = ArgumentCaptor.forClass(classOf[Note[_]])
- verify(tracer).record(captor.capture())
- val note = captor.getValue
- note.name shouldBe "double"
- note.value shouldBe 3.14
- }
- "record a string as an Object" in {
- val str: java.lang.String = "hello"
- val obj: AnyRef = str
- JMoney.record("string", obj)
-
- val captor = ArgumentCaptor.forClass(classOf[Note[_]])
- verify(tracer).record(captor.capture())
- val note = captor.getValue
- note.name shouldBe "string"
- note.value shouldBe "hello"
- }
- "record any object as an Object" in {
- val lst = List(1)
- JMoney.record("list", lst)
-
- val captor = ArgumentCaptor.forClass(classOf[Note[_]])
- verify(tracer).record(captor.capture())
- val note = captor.getValue
- note.name shouldBe "list"
- note.value shouldBe "List(1)"
- }
- "record null as a string Note with None" in {
- val obj: AnyRef = null
- JMoney.record("nill", obj)
-
- val captor = ArgumentCaptor.forClass(classOf[Note[_]])
- verify(tracer).record(captor.capture())
- val note = captor.getValue
- val nil: AnyRef = null
- note.name shouldBe "nill"
- note.value shouldBe nil
- }
- }
-}
diff --git a/money-core/src/test/scala/com/comcast/money/core/logging/MethodTracerSpec.scala b/money-core/src/test/scala/com/comcast/money/core/logging/MethodTracerSpec.scala
index edc5739a..c72e395f 100644
--- a/money-core/src/test/scala/com/comcast/money/core/logging/MethodTracerSpec.scala
+++ b/money-core/src/test/scala/com/comcast/money/core/logging/MethodTracerSpec.scala
@@ -80,7 +80,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with
verify(mockSpanBuilder).startSpan()
verify(mockSpan).storeInContext(any())
verify(mockContext).makeCurrent()
- verify(mockSpan).stop(true)
+ verify(mockSpan).end(true)
verify(mockScope).close()
result shouldBe "result"
@@ -107,7 +107,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with
verify(mockSpanBuilder).startSpan()
verify(mockSpan).storeInContext(any())
verify(mockContext).makeCurrent()
- verify(mockSpan).stop(false)
+ verify(mockSpan).end(false)
verify(mockScope).close()
}
"that throws an ignored exception" in {
@@ -131,7 +131,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with
verify(mockSpanBuilder).startSpan()
verify(mockSpan).storeInContext(any())
verify(mockContext).makeCurrent()
- verify(mockSpan).stop(true)
+ verify(mockSpan).end(true)
verify(mockScope).close()
}
}
@@ -157,14 +157,14 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with
verify(mockSpanBuilder).startSpan()
verify(mockSpan).storeInContext(any())
verify(mockContext).makeCurrent()
- verify(mockSpan, never()).stop(any())
+ verify(mockSpan, never()).end(any())
verify(mockScope).close()
result shouldBe "result2"
handler.callback(Success("result3"))
- verify(mockSpan).stop(true)
+ verify(mockSpan).end(true)
}
"that complete exceptionally" in {
val method = tracedAsyncMethod
@@ -187,14 +187,14 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with
verify(mockSpanBuilder).startSpan()
verify(mockSpan).storeInContext(any())
verify(mockContext).makeCurrent()
- verify(mockSpan, never()).stop(any())
+ verify(mockSpan, never()).end(any())
verify(mockScope).close()
result shouldBe "result2"
handler.callback(Failure(new Exception))
- verify(mockSpan).stop(false)
+ verify(mockSpan).end(false)
}
"that complete exceptionally with ignored exception" in {
val method = tracedAsyncMethodWithIgnoredExceptions
@@ -217,14 +217,14 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with
verify(mockSpanBuilder).startSpan()
verify(mockSpan).storeInContext(any())
verify(mockContext).makeCurrent()
- verify(mockSpan, never()).stop(any())
+ verify(mockSpan, never()).end(any())
verify(mockScope).close()
result shouldBe "result2"
handler.callback(Failure(new IllegalArgumentException()))
- verify(mockSpan).stop(true)
+ verify(mockSpan).end(true)
}
"with unhandled return value" in {
val method = tracedAsyncMethod
@@ -246,7 +246,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with
verify(mockSpanBuilder).startSpan()
verify(mockSpan).storeInContext(any())
verify(mockContext).makeCurrent()
- verify(mockSpan).stop(true)
+ verify(mockSpan).end(true)
verify(mockScope).close()
result shouldBe "result"
@@ -273,7 +273,7 @@ class MethodTracerSpec extends AnyWordSpec with Matchers with MockitoSugar with
verify(mockSpanBuilder).startSpan()
verify(mockSpan).storeInContext(any())
verify(mockContext).makeCurrent()
- verify(mockSpan).stop(false)
+ verify(mockSpan).end(false)
verify(mockScope).close()
}
}
diff --git a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala
index 8577bc4f..4caa8051 100644
--- a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala
+++ b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyEvent.scala
@@ -16,13 +16,13 @@
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
+ override def getEpochNanos: Long = event.timestampNanos
override def getTotalAttributeCount: Int = event.attributes.size
}
diff --git a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyLink.scala b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyLink.scala
index feae7269..0e1d1a8f 100644
--- a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyLink.scala
+++ b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyLink.scala
@@ -16,12 +16,12 @@
package com.comcast.money.otel.handlers
-import com.comcast.money.api.SpanInfo
+import com.comcast.money.api.{ LinkInfo, SpanInfo }
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.api.trace.SpanContext
import io.opentelemetry.sdk.trace.data.LinkData
-private[otel] case class MoneyLink(link: SpanInfo.Link) extends LinkData {
+private[otel] case class MoneyLink(link: LinkInfo) extends LinkData {
override def getSpanContext: SpanContext = link.spanContext
override def getAttributes: Attributes = link.attributes
override def getTotalAttributeCount: Int = link.attributes.size
diff --git a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyReadableSpanData.scala b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyReadableSpanData.scala
index d5c93925..a25582ae 100644
--- a/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyReadableSpanData.scala
+++ b/money-otel-handler/src/main/scala/com/comcast/money/otel/handlers/MoneyReadableSpanData.scala
@@ -17,7 +17,7 @@
package com.comcast.money.otel.handlers
import java.util
-import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo }
+import com.comcast.money.api.{ EventInfo, InstrumentationLibrary, LinkInfo, Note, SpanId, SpanInfo }
import io.opentelemetry.api.common.{ Attributes, AttributesBuilder }
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo
import io.opentelemetry.sdk.resources.Resource
@@ -81,14 +81,14 @@ private[otel] class MoneyReadableSpanData(info: SpanInfo) extends ReadableSpan w
}
.build()
- private def convertEvents(events: util.List[SpanInfo.Event]): util.List[EventData] =
+ private def convertEvents(events: util.List[EventInfo]): util.List[EventData] =
events.asScala
.map({
event => MoneyEvent(event).asInstanceOf[EventData]
})
.asJava
- private def convertLinks(links: util.List[SpanInfo.Link]): util.List[LinkData] =
+ private def convertLinks(links: util.List[LinkInfo]): util.List[LinkData] =
links.asScala
.map({
link => MoneyLink(link).asInstanceOf[LinkData]
diff --git a/money-otel-handler/src/test/java/com/comcast/money/otel/handlers/TestSpanInfo.java b/money-otel-handler/src/test/java/com/comcast/money/otel/handlers/TestSpanInfo.java
index b2da5767..e5b6a3ef 100644
--- a/money-otel-handler/src/test/java/com/comcast/money/otel/handlers/TestSpanInfo.java
+++ b/money-otel-handler/src/test/java/com/comcast/money/otel/handlers/TestSpanInfo.java
@@ -20,10 +20,10 @@
import java.util.List;
import java.util.Map;
-import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
+import com.comcast.money.api.EventInfo;
import com.comcast.money.api.InstrumentationLibrary;
import com.comcast.money.api.Note;
import com.comcast.money.api.SpanId;
@@ -42,7 +42,7 @@ public Map> notes() {
}
@Override
- public List events() {
+ public List events() {
return Collections.emptyList();
}
@@ -91,6 +91,16 @@ public long durationNanos() {
return 0;
}
+ @Override
+ public boolean hasEnded() {
+ return true;
+ }
+
+ @Override
+ public boolean isRecording() {
+ return true;
+ }
+
@Override
public String appName() {
return "appName";
diff --git a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala
index eeb2a1c2..bac9e0b8 100644
--- a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala
+++ b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyEventSpec.scala
@@ -16,17 +16,17 @@
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
+ override def timestampNanos(): Long = 1234567890L
override def exception(): Throwable = null
}
@@ -35,7 +35,7 @@ class MoneyEventSpec extends AnyWordSpec with Matchers {
val underTest = MoneyEvent(event)
underTest.getName shouldBe event.name()
- underTest.getEpochNanos shouldBe event.timestamp()
+ underTest.getEpochNanos shouldBe event.timestampNanos()
underTest.getTotalAttributeCount shouldBe 1
underTest.getAttributes shouldBe Attributes.of(AttributeKey.stringKey("foo"), "bar")
}
diff --git a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyLinkSpec.scala b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyLinkSpec.scala
index 4bf9533c..ff9f58eb 100644
--- a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyLinkSpec.scala
+++ b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyLinkSpec.scala
@@ -16,7 +16,7 @@
package com.comcast.money.otel.handlers
-import com.comcast.money.api.{ IdGenerator, SpanInfo }
+import com.comcast.money.api.{ IdGenerator, LinkInfo, SpanInfo }
import io.opentelemetry.api.common.{ AttributeKey, Attributes }
import io.opentelemetry.api.trace.{ SpanContext, TraceFlags, TraceState }
import org.scalatest.matchers.should.Matchers
@@ -25,7 +25,7 @@ import org.scalatest.wordspec.AnyWordSpec
class MoneyLinkSpec extends AnyWordSpec with Matchers {
val linkedContext = SpanContext.create(IdGenerator.generateRandomTraceIdAsHex(), IdGenerator.generateRandomIdAsHex(), TraceFlags.getSampled, TraceState.getDefault)
- val link = new SpanInfo.Link {
+ val link = new LinkInfo {
override def spanContext(): SpanContext = linkedContext
override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar")
}
diff --git a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala
index 21f0b8fa..c7729377 100644
--- a/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala
+++ b/money-otel-handler/src/test/scala/com/comcast/money/otel/handlers/MoneyReadableSpanDataSpec.scala
@@ -18,7 +18,7 @@ package com.comcast.money.otel.handlers
import java.util
import java.util.UUID
-import com.comcast.money.api.{ IdGenerator, InstrumentationLibrary, Note, SpanId, SpanInfo }
+import com.comcast.money.api.{ EventInfo, IdGenerator, InstrumentationLibrary, LinkInfo, Note, SpanId, SpanInfo }
import io.opentelemetry.api.common.{ AttributeKey, Attributes }
import io.opentelemetry.sdk.resources.Resource
import io.opentelemetry.api.trace.{ Span, SpanContext, SpanKind, StatusCode, TraceFlags, TraceState }
@@ -93,21 +93,23 @@ class MoneyReadableSpanDataSpec extends AnyWordSpec with Matchers {
override def endTimeNanos(): Long = 3000000L
override def status(): StatusCode = StatusCode.OK
override def description(): String = "description"
+ override def hasEnded: Boolean = true
+ override def isRecording: Boolean = true
override def durationNanos(): Long = 2000000L
override def notes(): util.Map[String, Note[_]] = Map[String, Note[_]]("foo" -> Note.of("foo", "bar")).asJava
- override def events(): util.List[SpanInfo.Event] = List(event).asJava
- override def links(): util.List[SpanInfo.Link] = List(link).asJava
+ override def events(): util.List[EventInfo] = List(event).asJava
+ override def links(): util.List[LinkInfo] = List(link).asJava
}
- val event = new SpanInfo.Event {
+ val event = new EventInfo {
override def name(): String = "event"
override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar")
- override def timestamp(): Long = 1234567890L
+ override def timestampNanos(): Long = 1234567890L
override def exception(): Throwable = null
}
val linkedContext = SpanContext.create(IdGenerator.generateRandomTraceIdAsHex(), IdGenerator.generateRandomIdAsHex(), TraceFlags.getSampled, TraceState.getDefault)
- val link = new SpanInfo.Link {
+ val link = new LinkInfo {
override def spanContext(): SpanContext = linkedContext
override def attributes(): Attributes = Attributes.of(AttributeKey.stringKey("foo"), "bar")
}
diff --git a/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java b/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java
index 09d4a029..95c18860 100644
--- a/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java
+++ b/money-spring/src/test/java/com/comcast/money/spring/MoneyClientHttpInterceptorSpec.java
@@ -50,21 +50,23 @@ public class MoneyClientHttpInterceptorSpec {
@Before
public void setUp() {
Span span = mock(Span.class);
- SpanInfo testSpanInfo = new CoreSpanInfo(
- id,
- "testName",
- SpanKind.INTERNAL,
- 0L,
- 0L,
- 0L,
- StatusCode.OK,
- "",
- Collections.emptyMap(),
- Collections.emptyList(),
- Collections.emptyList(),
- new InstrumentationLibrary("test", "0.0.1"),
- "testAppName",
- "testHost");
+ SpanInfo testSpanInfo = CoreSpanInfo.builder()
+ .id(id)
+ .name("testName")
+ .kind(SpanKind.INTERNAL)
+ .startTimeNanos(0L)
+ .endTimeNanos(0L)
+ .hasEnded(true)
+ .durationNanos(0L)
+ .status(StatusCode.OK)
+ .description("")
+ .notes(Collections.emptyMap())
+ .links(Collections.emptyList())
+ .events(Collections.emptyList())
+ .library(new InstrumentationLibrary("test", "0.0.1"))
+ .appName("testAppName")
+ .host("testHost")
+ .build();
when(span.info()).thenReturn(testSpanInfo);
when(span.storeInContext(any())).thenCallRealMethod();
diff --git a/money-spring/src/test/java/com/comcast/money/spring/TracedMethodInterceptorSpec.java b/money-spring/src/test/java/com/comcast/money/spring/TracedMethodInterceptorSpec.java
index aa805d88..2cdd19e6 100644
--- a/money-spring/src/test/java/com/comcast/money/spring/TracedMethodInterceptorSpec.java
+++ b/money-spring/src/test/java/com/comcast/money/spring/TracedMethodInterceptorSpec.java
@@ -90,7 +90,7 @@ public void testTracing() throws Exception {
verify(span).storeInContext(any());
verify(context).makeCurrent();
verify(springTracer).record("foo", "bar", false);
- verify(span).stop(true);
+ verify(span).end(true);
verify(scope).close();
}
@@ -158,7 +158,7 @@ public void testTracingRecordsFailureOnException() throws Exception {
verify(span).storeInContext(any());
verify(context).makeCurrent();
verify(springTracer).record("foo", "bar", false);
- verify(span).stop(false);
+ verify(span).end(false);
verify(scope).close();
}
@@ -172,7 +172,7 @@ public void testTracingDoesNotTraceMethodsWithoutAnnotation() {
@Test(expected = IllegalArgumentException.class)
public void testTracingIgnoresException() {
sampleTraceBean.doSomethingButIgnoreException();
- verify(span).stop(true);
+ verify(span).end(true);
}
@Configuration
diff --git a/money-wire/src/main/avro/money.avsc b/money-wire/src/main/avro/money.avsc
index c88e529a..fa30f169 100644
--- a/money-wire/src/main/avro/money.avsc
+++ b/money-wire/src/main/avro/money.avsc
@@ -26,6 +26,11 @@
"type": ["null","string"],
"default": null
},
+ {
+ "name": "hasEnded",
+ "type": "boolean",
+ "default": false
+ },
{
"name": "duration",
"type": "long",
diff --git a/money-wire/src/main/scala/com/comcast/money/wire/SpanConverters.scala b/money-wire/src/main/scala/com/comcast/money/wire/SpanConverters.scala
index b430894b..036e6655 100644
--- a/money-wire/src/main/scala/com/comcast/money/wire/SpanConverters.scala
+++ b/money-wire/src/main/scala/com/comcast/money/wire/SpanConverters.scala
@@ -21,7 +21,7 @@ import java.util
import java.util.Collections
import java.util.concurrent.TimeUnit
import com.comcast.money.api
-import com.comcast.money.api.{ InstrumentationLibrary, Note, SpanId, SpanInfo }
+import com.comcast.money.api.{ EventInfo, InstrumentationLibrary, Note, SpanId, SpanInfo }
import com.comcast.money.core._
import com.comcast.money.wire.avro
import com.comcast.money.wire.avro.NoteType
@@ -115,6 +115,7 @@ trait SpanWireConverters {
span.host,
span.library.name,
span.library.version,
+ span.hasEnded,
span.durationMicros,
if (success == null) true else success,
span.startTimeMillis,
@@ -139,8 +140,10 @@ trait SpanWireConverters {
new SpanInfo {
override def notes(): util.Map[String, Note[_]] = toNotesMap(from.getNotes)
- override def events(): util.List[SpanInfo.Event] = Collections.emptyList()
+ override def events(): util.List[EventInfo] = Collections.emptyList()
override def startTimeNanos(): Long = TimeUnit.MILLISECONDS.toNanos(from.getStartTime)
+ override def hasEnded: Boolean = from.getHasEnded
+ override def isRecording: Boolean = true
override def endTimeNanos(): Long = startTimeNanos + durationNanos
override def status(): StatusCode = if (from.getSuccess) StatusCode.OK else StatusCode.ERROR
override def kind(): SpanKind = SpanKind.INTERNAL
diff --git a/money-wire/src/test/scala/com/comcast/money/wire/TestSpanInfo.scala b/money-wire/src/test/scala/com/comcast/money/wire/TestSpanInfo.scala
index dac3bc74..64e4b3ce 100644
--- a/money-wire/src/test/scala/com/comcast/money/wire/TestSpanInfo.scala
+++ b/money-wire/src/test/scala/com/comcast/money/wire/TestSpanInfo.scala
@@ -28,9 +28,11 @@ case class TestSpanInfo(
library: InstrumentationLibrary = new InstrumentationLibrary("test", "0.0.1"),
startTimeNanos: Long = 0L,
endTimeNanos: Long = 0L,
+ hasEnded: Boolean = true,
+ isRecording: Boolean = true,
durationNanos: Long = 0L,
status: StatusCode = StatusCode.UNSET,
description: String = "",
- notes: java.util.Map[String, Note[_]] = Collections.emptyMap(),
+ override val notes: java.util.Map[String, Note[_]] = Collections.emptyMap(),
appName: String = Money.Environment.applicationName,
- host: String = Money.Environment.hostName) extends SpanInfo
+ host: String = Money.Environment.hostName) extends SpanInfo
\ No newline at end of file
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 771ae3a8..9bc65530 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -13,6 +13,8 @@ object Dependencies {
val openTelemetryInstV = "0.17.0"
val openTelemetrySemConvV = "0.17.0-alpha"
+ val lombok = "org.projectlombok" % "lombok" % "1.18.18" % Provided
+
val akka = "com.typesafe.akka" %% "akka-actor" % akkaV
val akkaStream = "com.typesafe.akka" %% "akka-stream" % akkaV
val akkaLog = "com.typesafe.akka" %% "akka-slf4j" % akkaV
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 00cab406..10b88712 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -20,3 +20,5 @@ addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3")
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.10.0-RC1")
addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.3")
+
+addSbtPlugin("com.thoughtworks.sbt" % "delombokjavadoc" % "1.0.0")
\ No newline at end of file