Skip to content

Commit

Permalink
Merge pull request #372 from iRevive/sdk-trace/span-data
Browse files Browse the repository at this point in the history
sdk-trace: add `SpanData`
  • Loading branch information
iRevive authored Nov 15, 2023
2 parents 6fd42f2 + 324344f commit 56e3809
Show file tree
Hide file tree
Showing 6 changed files with 448 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/*
* Copyright 2023 Typelevel
*
* 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 org.typelevel.otel4s.sdk
package trace
package data

import cats.Hash
import cats.Show
import cats.syntax.foldable._
import cats.syntax.show._
import org.typelevel.otel4s.sdk.common.InstrumentationScope
import org.typelevel.otel4s.trace.SpanContext
import org.typelevel.otel4s.trace.SpanKind

import scala.concurrent.duration.FiniteDuration

/** Immutable representation of all data collected by the
* [[org.typelevel.otel4s.trace.Span Span]].
*
* @see
* [[https://opentelemetry.io/docs/specs/otel/trace/api/#span]]
*/
sealed trait SpanData {

/** The name of the span.
*/
def name: String

/** The span context associated with this span.
*/
def spanContext: SpanContext

/** The parent span context, if any.
*/
def parentSpanContext: Option[SpanContext]

/** The kind of the span.
*/
def kind: SpanKind

/** The start timestamp of the span.
*/
def startTimestamp: FiniteDuration

/** The end time of the span.
*/
def endTimestamp: Option[FiniteDuration]

/** The status data of the span.
*/
def status: StatusData

/** The attributes associated with the span.
*/
def attributes: Attributes

/** The events associated with the span.
*/
def events: List[EventData]

/** The links associated with the span.
*/
def links: List[LinkData]

/** The instrumentation scope associated with the span.
*/
def instrumentationScope: InstrumentationScope

/** The resource associated with the span.
*/
def resource: Resource

/** Whether the span has ended.
*/
final def hasEnded: Boolean = endTimestamp.isDefined

override final def hashCode(): Int =
Hash[SpanData].hash(this)

override final def equals(obj: Any): Boolean =
obj match {
case other: SpanData => Hash[SpanData].eqv(this, other)
case _ => false
}

override final def toString: String =
Show[SpanData].show(this)
}

object SpanData {

/** Creates [[SpanData]] with the given arguments.
*
* @param name
* the name of the span
*
* @param spanContext
* the span context associated with the span
*
* @param parentSpanContext
* an optional parent span context. Use `None` if there is no parent span
* context
*
* @param kind
* the kind of the span
*
* @param startTimestamp
* the start timestamp of the span
*
* @param endTimestamp
* the end timestamp of the span. Use `None` is the span hasn't ended yet
*
* @param status
* the status data of the span
*
* @param attributes
* the attributes associated with the span
*
* @param events
* the events associated with the span
*
* @param links
* the links associated with the span
*
* @param instrumentationScope
* the instrumentation scope associated with the span
*
* @param resource
* the resource associated with the span
*/
def apply(
name: String,
spanContext: SpanContext,
parentSpanContext: Option[SpanContext],
kind: SpanKind,
startTimestamp: FiniteDuration,
endTimestamp: Option[FiniteDuration],
status: StatusData,
attributes: Attributes,
events: List[EventData],
links: List[LinkData],
instrumentationScope: InstrumentationScope,
resource: Resource
): SpanData =
Impl(
name = name,
spanContext = spanContext,
parentSpanContext = parentSpanContext,
kind = kind,
startTimestamp = startTimestamp,
endTimestamp = endTimestamp,
status = status,
attributes = attributes,
events = events,
links = links,
instrumentationScope = instrumentationScope,
resource = resource
)

implicit val spanDataHash: Hash[SpanData] =
Hash.by { data =>
(
data.name,
data.spanContext,
data.parentSpanContext,
data.kind,
data.startTimestamp,
data.endTimestamp,
data.status,
data.attributes,
data.events,
data.links,
data.instrumentationScope,
data.resource
)
}

implicit val spanDataShow: Show[SpanData] =
Show.show { data =>
val endTimestamp =
data.endTimestamp.foldMap(ts => show"endTimestamp=$ts, ")

show"SpanData{" +
show"name=${data.name}, " +
show"spanContext=${data.spanContext}, " +
show"parentSpanContext=${data.parentSpanContext}, " +
show"kind=${data.kind}, " +
show"startTimestamp=${data.startTimestamp}, " +
endTimestamp +
show"hasEnded=${data.hasEnded}, " +
show"status=${data.status}, " +
show"attributes=${data.attributes}, " +
show"events=${data.events}, " +
show"links=${data.links}, " +
show"instrumentationScope=${data.instrumentationScope}, " +
show"resource=${data.resource}}"
}

private final case class Impl(
name: String,
spanContext: SpanContext,
parentSpanContext: Option[SpanContext],
kind: SpanKind,
startTimestamp: FiniteDuration,
endTimestamp: Option[FiniteDuration],
status: StatusData,
attributes: Attributes,
events: List[EventData],
links: List[LinkData],
instrumentationScope: InstrumentationScope,
resource: Resource
) extends SpanData

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ import org.scalacheck.rng.Seed
import org.typelevel.otel4s.Attribute
import org.typelevel.otel4s.AttributeKey
import org.typelevel.otel4s.AttributeType
import org.typelevel.otel4s.sdk.common.InstrumentationScope
import org.typelevel.otel4s.sdk.trace.data.EventData
import org.typelevel.otel4s.sdk.trace.data.LinkData
import org.typelevel.otel4s.sdk.trace.data.SpanData
import org.typelevel.otel4s.sdk.trace.data.StatusData
import org.typelevel.otel4s.sdk.trace.samplers.SamplingDecision
import org.typelevel.otel4s.trace.SpanContext
import org.typelevel.otel4s.trace.SpanKind
import org.typelevel.otel4s.trace.Status
import org.typelevel.otel4s.trace.TraceFlags
import org.typelevel.otel4s.trace.TraceState

Expand Down Expand Up @@ -68,6 +74,11 @@ object Cogens {
implicit val attributesCogen: Cogen[Attributes] =
Cogen[List[Attribute[_]]].contramap(_.toList)

implicit val instrumentationScopeCogen: Cogen[InstrumentationScope] =
Cogen[(String, Option[String], Option[String], Attributes)].contramap { s =>
(s.name, s.version, s.schemaUrl, s.attributes)
}

implicit val resourceCogen: Cogen[Resource] =
Cogen[(Attributes, Option[String])].contramap { r =>
(r.attributes, r.schemaUrl)
Expand All @@ -76,6 +87,12 @@ object Cogens {
implicit val samplingDecisionCogen: Cogen[SamplingDecision] =
Cogen[String].contramap(_.toString)

implicit val spanKindCogen: Cogen[SpanKind] =
Cogen[String].contramap(_.toString)

implicit val statusCogen: Cogen[Status] =
Cogen[String].contramap(_.toString)

implicit val traceFlagsCogen: Cogen[TraceFlags] =
Cogen[Byte].contramap(_.toByte)

Expand All @@ -99,4 +116,47 @@ object Cogens {
Cogen[(String, FiniteDuration, Attributes)].contramap { data =>
(data.name, data.timestamp, data.attributes)
}

implicit val linkDataCogen: Cogen[LinkData] =
Cogen[(SpanContext, Attributes)].contramap { data =>
(data.spanContext, data.attributes)
}

implicit val statusDataCogen: Cogen[StatusData] =
Cogen[(Status, Option[String])].contramap { data =>
(data.status, data.description)
}

implicit val spanDataCogen: Cogen[SpanData] = Cogen[
(
String,
SpanContext,
Option[SpanContext],
SpanKind,
FiniteDuration,
Option[FiniteDuration],
StatusData,
Attributes,
List[EventData],
List[LinkData],
InstrumentationScope,
Resource
)
].contramap { spanData =>
(
spanData.name,
spanData.spanContext,
spanData.parentSpanContext,
spanData.kind,
spanData.startTimestamp,
spanData.endTimestamp,
spanData.status,
spanData.attributes,
spanData.events,
spanData.links,
spanData.instrumentationScope,
spanData.resource
)
}

}
Loading

0 comments on commit 56e3809

Please sign in to comment.