-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
X-Ray exporter implementation #4
base: master
Are you sure you want to change the base?
Conversation
Thanks! Will check tomorrow |
Matt, first of all, thanks for the contribution! I went through your implementation and found a suspicious logic: you generate and use your own random I thought and realized that we had a design flaw in the I did a draft POC in the I also need @janstenpickle's opinion on what I am proposing. |
P.S. And this PR should also include |
Thank you Matt, this is great! RE the new typeclasses for ID generation, yes I completely agree. Having specific, derivable typeclasses for this makes a lot of sense. RE testing: it's a shame that LocalStack doesn't support X-Ray, but I agree with the best effort apporach of listening for the data. |
I spent some more time thinking on the changes in the
Given these constraints, if we even try to make generation of TraceId customizable at the level of construction of What we still can do:
It is still not clear, when and how we should provide alternative generators and how to make sure they are compatible with the exporters. Also, if tomorrow we want to integrate with a tracing system that uses 100-char alphanumeric traceIds, we won't be able to do it, unfortunately. |
With the proposed changes, the old code written for previously supported systems will be source compatible. Users of X-Ray will have to import a custom instance to the local scope that will be passed to |
modules/xray-udp-exporter/src/main/scala/io/janstenpickle/trace4cats/xray/XRayUdpSpan.scala
Outdated
Show resolved
Hide resolved
modules/xray-udp-exporter/src/main/scala/io/janstenpickle/trace4cats/xray/XRayUdpSpan.scala
Outdated
Show resolved
Hide resolved
modules/xray-udp-exporter/src/main/scala/io/janstenpickle/trace4cats/xray/XRayUdpSpan.scala
Show resolved
Hide resolved
modules/xray-udp-exporter/src/main/scala/io/janstenpickle/trace4cats/xray/XRayUdpSpan.scala
Outdated
Show resolved
Hide resolved
@mrdziuban let's wait for the changes in t4c-core. We will cut a snapshot release and you can proceed with it. |
Co-authored-by: catostrophe <[email protected]>
You can use v0.13.1+12-491cff7e |
With the recent changes in trace4cats, the generator for TraceId in object xray extends XRayCompatInstances0
trait XRayCompatInstances0 extends XRayCompatInstances1 {
implicit def fromRandomClockTraceIdGen[F[_]: Apply: Random: Clock]: TraceId.Gen[F] = TraceId.Gen.from {
val timeSize = Integer.BYTES
val rndSize = TraceId.size - timeSize
(Clock[F].realTime, Random[F].nextBytes(rndSize)).mapN { (t, r) =>
val bytes = ByteBuffer.allocate(TraceId.size).putInt(t.toSeconds.toInt).put(r).array
TraceId.unsafe(bytes)
}
}
}
trait XRayCompatInstances1 { this: XRayCompatInstances0 =>
implicit def fromSyncTraceIdGen[F[_]: Sync]: TraceId.Gen[F] = {
implicit val rnd: Random[F] = Random.javaUtilConcurrentThreadLocalRandom[F]
fromRandomClockTraceIdGen[F]
}
}
// maybe use different name
def toHex(traceId: TraceId): String = {
val timeSize = Integer.BYTES
val rndSize = TraceId.size - timeSize
val arr = traceId.value
val t = new String(Hex.encodeHex(arr, 0, timeSize, true))
val r = new String(Hex.encodeHex(arr, timeSize, rndSize, true))
s"1-$t-$r"
} |
Please use v0.13.1+16-78f06e87 |
@mrdziuban do you have any blockers that prevent you from proceeding with this PR? |
|
||
import scala.util.Try | ||
|
||
case class XRayExceptionId private (value: Array[Byte]) extends AnyVal { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just copied TraceId
from model
private def toEpochSeconds(i: Instant): Double = | ||
ChronoUnit.MICROS.between(Instant.EPOCH, i).toDouble / 1000_000 | ||
|
||
private def spanStatusFaultJson[F[_]: Applicative: XRayExceptionId.Gen](status: SpanStatus): F[JsonObject] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requires XRayExceptionId.Gen
(which is autoderivable) instead of Random
JsonObject( | ||
"name" := goodName, | ||
"id" := span.context.spanId.show, | ||
"trace_id" := xRayTraceIdExportShow.show(span.context.traceId), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly use an alternative Show
instance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use traceId
direct from the span
* 1. the first 4 bytes are epoch time; | ||
* 1. the rest bytes are random. | ||
*/ | ||
def xRayTraceIdGen[F[_]: Apply: Random: Clock]: TraceId.Gen[F] = TraceId.Gen.from { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative generator of traceId
that is compatible with the rest trace4cats
integrations.
@catostrophe I don't have any blockers other than time, work and life got in the way of me being able to commit time to this. Feel free to take it from here if you'd like, otherwise I can circle back to it when I have time (though I can't say for sure when that might be) |
@mrdziuban I just pushed some changes related to traceId generation. Next, I will ask you some questions regarding the implementation. Take your time, we're not in a hurry. |
private def json[F[_]: Monad: XRayExceptionId.Gen]( | ||
process: Option[TraceProcess], | ||
span: CompletedSpan, | ||
childSpans: Ref[F, Map[TraceId, NonEmptyList[CompletedSpan]]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I saw in the docs, child spans can be sent prior to the parent if they are typed as "subsegment". Is it possible when the parent is not yet sent, even as "in_progress"?
/** Initialize a `Deferred` to hold the target `IpAddress` so we only need to resolve it at most once for | ||
* each call to `exportBatch` | ||
*/ | ||
Deferred[F, IpAddress].flatMap { ipRef => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to resolve IpAddress
every time we send another batch? This is not what we do in other implementations.
Closes #3 and trace4cats/trace4cats#118.
Adds initial implementation of an exporter that sends spans to AWS X-Ray. Some notes and questions:
fs2.io.net.DatagramSocket
for writing to the UDP socket, but I'm open to changing this if there's a different library you'd preferSemanticTags.<kind|status>Tags
aren't valid. For now I've just replaced periods with underscoresDatagramSocket#reads
to verify that data is sent properly?