Skip to content

Commit a02d377

Browse files
josephhajdukjenkins
authored andcommitted
finagle-opencensus-tracing: upgraded opencensus and added TextFormat param
Problem B3Format is currently the only propagation method we can use. Invalid or missing propagation headers result in a 500 response. Solution raised opencensusVersion in build.sbt to "0.24.0" added optional TextFormat parameter to the withOpenCensusTracing for both Server and Client that defaults to previously used B3Format in StackServerOps.scala previous version seemed to expect SpanContext.INVALID as a failure result from TextFormat.extract, which was incorrect. This resulted in missing or invalid propagation headers causing a 500 response. TextFormat.extract throws an exception on bad or missing data, which is now treated as SpanContext.INVALID added a test dependency to opencensus-contrib-http-util which provides an implementation of TextFormat for stackdriver style "x-cloud-trace-context" headers. added copy of existing test that uses the CloudTraceFormat from opencensus-contrib-http-util Result You can now use other forms of tracing header propagation, i.e. the CloudTraceFormat format from the opencensus-contrib-http-util package. JIRA Issues: CSL-9378 Differential Revision: https://phabricator.twitter.biz/D432003
1 parent 9ece6a7 commit a02d377

6 files changed

Lines changed: 91 additions & 21 deletions

File tree

‎CHANGELOG.rst‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ Note that ``PHAB_ID=#`` and ``RB_ID=#`` correspond to associated messages in com
77
Unreleased
88
----------
99

10+
New Features
11+
~~~~~~~~~~~~
12+
13+
* finagle-opencensus-tracing: Add support for providing a custom TextFormat for header
14+
propagation. ``PHAB_ID=D432003``
15+
1016
Runtime Behavior Changes
1117
~~~~~~~~~~~~~~~~~~~~~~~~
1218

@@ -27,6 +33,9 @@ Bug Fixes
2733
from a HTTP/1.x pipeline, removing some traps such as unintended timeouts.
2834
``PHAB_ID=D429416``
2935

36+
* finagle-opencensus-tracing: Fixed internal server error when invalid or no propagation headers
37+
are provided. ``PHAB_ID=D432003``
38+
3039
20.1.0
3140
------
3241

‎build.sbt‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ val netty4LibsTest = Seq(
6565
val netty4Http = "io.netty" % "netty-codec-http" % netty4Version
6666
val netty4Http2 = "io.netty" % "netty-codec-http2" % netty4Version
6767
val netty4StaticSsl = "io.netty" % "netty-tcnative-boringssl-static" % netty4StaticSslVersion
68-
val opencensusVersion = "0.19.1"
68+
val opencensusVersion = "0.24.0"
6969
val jacksonVersion = "2.9.10"
7070
val jacksonDatabindVersion = "2.9.10.1"
7171
val jacksonLibs = Seq(
@@ -808,7 +808,8 @@ lazy val finagleOpenCensusTracing = Project(
808808
name := "finagle-opencensus-tracing",
809809
libraryDependencies ++= Seq(
810810
"io.opencensus" % "opencensus-api" % opencensusVersion,
811-
"io.opencensus" % "opencensus-impl" % opencensusVersion
811+
"io.opencensus" % "opencensus-impl" % opencensusVersion,
812+
"io.opencensus" % "opencensus-contrib-http-util" % opencensusVersion % "test",
812813
) ++ scroogeLibs
813814
).dependsOn(
814815
finagleCore,

‎finagle-opencensus-tracing/src/main/scala/com/twitter/finagle/tracing/opencensus/StackClientOps.scala‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,24 @@ object StackClientOps {
4545

4646
implicit final class HttpOpenCensusTracing(private val client: Http.Client) extends AnyVal {
4747
def withOpenCensusTracing: Http.Client =
48+
withOpenCensusTracing(Tracing.getPropagationComponent.getB3Format)
49+
50+
def withOpenCensusTracing(textFormat: TextFormat): Http.Client =
4851
client.withStack { stack =>
4952
// serialization must happen after we attach the OpenCensus span
5053
stack
5154
.insertBefore(
5255
TraceInitializerFilter.role,
5356
ClientTraceContextFilter.module[http.Request, http.Response]
5457
)
55-
.replace(TraceInitializerFilter.role, httpSerializeModule)
58+
.replace(TraceInitializerFilter.role, httpSerializeModule(textFormat))
5659
}
5760
}
5861

59-
private[this] val httpSerializeFilter: SimpleFilter[http.Request, http.Response] =
62+
private[this] def httpSerializeFilter(
63+
textFormat: TextFormat
64+
): SimpleFilter[http.Request, http.Response] =
6065
new SimpleFilter[http.Request, http.Response] {
61-
private[this] val textFormat = Tracing.getPropagationComponent.getB3Format
6266
private[this] val setter = new TextFormat.Setter[http.Request] {
6367
def put(carrier: http.Request, key: String, value: String): Unit =
6468
carrier.headerMap.set(key, value)
@@ -80,12 +84,14 @@ object StackClientOps {
8084
private[opencensus] val HttpSerializationStackRole: Stack.Role =
8185
Stack.Role("OpenCensusHeaderSerialization")
8286

83-
private[this] val httpSerializeModule: Stackable[ServiceFactory[http.Request, http.Response]] =
87+
private[this] def httpSerializeModule(
88+
textFormat: TextFormat
89+
): Stackable[ServiceFactory[http.Request, http.Response]] =
8490
new Stack.Module0[ServiceFactory[http.Request, http.Response]] {
8591
def make(
8692
next: ServiceFactory[http.Request, http.Response]
8793
): ServiceFactory[http.Request, http.Response] =
88-
httpSerializeFilter.andThen(next)
94+
httpSerializeFilter(textFormat).andThen(next)
8995

9096
def role: Stack.Role = HttpSerializationStackRole
9197

‎finagle-opencensus-tracing/src/main/scala/com/twitter/finagle/tracing/opencensus/StackServerOps.scala‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.twitter.finagle.tracing.opencensus
22

33
import com.twitter.finagle.context.Contexts
44
import com.twitter.finagle._
5-
import com.twitter.util.Future
5+
import com.twitter.util.{Future, Try}
66
import io.opencensus.trace.{SpanContext, Tracing}
77
import io.opencensus.trace.propagation.TextFormat
88

@@ -44,18 +44,20 @@ object StackServerOps {
4444
}
4545

4646
implicit final class HttpOpenCensusTracing(private val server: Http.Server) extends AnyVal {
47-
def withOpenCensusTracing: Http.Server = {
47+
def withOpenCensusTracing: Http.Server =
48+
withOpenCensusTracing(Tracing.getPropagationComponent.getB3Format)
49+
50+
def withOpenCensusTracing(textFormat: TextFormat): Http.Server = {
4851
server.withStack { stack =>
4952
stack
5053
.prepend(ServerTraceContextFilter.module)
51-
.prepend(httpDeserModule) // attach to broadcast ctx before setting OC
54+
.prepend(httpDeserModule(textFormat)) // attach to broadcast ctx before setting OC
5255
}
5356
}
5457
}
5558

56-
private val httpDeserFilter: SimpleFilter[http.Request, http.Response] =
59+
private def httpDeserFilter(textFormat: TextFormat): SimpleFilter[http.Request, http.Response] =
5760
new SimpleFilter[http.Request, http.Response] {
58-
private[this] val textFormat = Tracing.getPropagationComponent.getB3Format
5961
private[this] val getter = new TextFormat.Getter[http.Request] {
6062
def get(carrier: http.Request, key: String): String =
6163
carrier.headerMap.getOrNull(key)
@@ -65,7 +67,7 @@ object StackServerOps {
6567
request: http.Request,
6668
service: Service[http.Request, http.Response]
6769
): Future[http.Response] = {
68-
val spanContext = textFormat.extract(request, getter)
70+
val spanContext = Try(textFormat.extract(request, getter)).getOrElse(SpanContext.INVALID)
6971
if (spanContext != SpanContext.INVALID) {
7072
Contexts.broadcast.let(TraceContextFilter.SpanContextKey, spanContext) {
7173
service(request)
@@ -80,12 +82,14 @@ object StackServerOps {
8082
private[opencensus] val HttpDeserializationStackRole: Stack.Role =
8183
Stack.Role("OpenCensusHeaderDeserialization")
8284

83-
private val httpDeserModule: Stackable[ServiceFactory[http.Request, http.Response]] =
85+
private def httpDeserModule(
86+
textFormat: TextFormat
87+
): Stackable[ServiceFactory[http.Request, http.Response]] =
8488
new Stack.Module0[ServiceFactory[http.Request, http.Response]] {
8589
def make(
8690
next: ServiceFactory[http.Request, http.Response]
8791
): ServiceFactory[http.Request, http.Response] =
88-
httpDeserFilter.andThen(next)
92+
httpDeserFilter(textFormat).andThen(next)
8993

9094
def role: Stack.Role = HttpDeserializationStackRole
9195

‎finagle-opencensus-tracing/src/test/scala/com/twitter/finagle/tracing/opencensus/BUILD‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ junit_tests(
22
compiler_option_sets = {"fatal_warnings"},
33
dependencies = [
44
"3rdparty/jvm/io/opencensus:opencensus-api",
5+
"3rdparty/jvm/io/opencensus:opencensus-contrib-http-util",
56
"3rdparty/jvm/io/opencensus:opencensus-impl",
67
"3rdparty/jvm/junit",
78
"3rdparty/jvm/org/mockito:mockito-all",

‎finagle-opencensus-tracing/src/test/scala/com/twitter/finagle/tracing/opencensus/HttpEndToEndTest.scala‎

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import com.twitter.finagle.http.{Request, Response, Status}
55
import com.twitter.finagle.tracing.opencensus.TracingOps._
66
import com.twitter.finagle.{Address, Http, Name, Service}
77
import com.twitter.util.{Await, Duration, Future}
8+
import io.opencensus.contrib.http.util.HttpPropagationUtil
89
import io.opencensus.trace.{SpanContext, Tracing}
10+
import io.opencensus.trace.propagation.TextFormat
911
import java.net.{InetAddress, InetSocketAddress}
1012
import java.util.concurrent.atomic.AtomicReference
1113
import org.scalatest.FunSuite
@@ -15,14 +17,61 @@ class HttpEndToEndTest extends FunSuite {
1517
private def await[T](f: Future[T]): T =
1618
Await.result(f, Duration.fromSeconds(15))
1719

18-
private def httpServer: Http.Server = {
20+
private def httpServer(
21+
textFormat: TextFormat = Tracing.getPropagationComponent.getB3Format
22+
): Http.Server = {
1923
import com.twitter.finagle.tracing.opencensus.StackServerOps._
20-
Http.server.withOpenCensusTracing
24+
Http.server.withOpenCensusTracing(textFormat)
2125
}
2226

23-
private def httpClient: Http.Client = {
27+
private def httpClient(
28+
textFormat: TextFormat = Tracing.getPropagationComponent.getB3Format
29+
): Http.Client = {
2430
import com.twitter.finagle.tracing.opencensus.StackClientOps._
25-
Http.client.withOpenCensusTracing
31+
Http.client.withOpenCensusTracing(textFormat)
32+
}
33+
34+
test("SpanContext is propagated when using CloudTraceFormat") {
35+
val spanCtx = new AtomicReference[SpanContext](SpanContext.INVALID)
36+
37+
val svc: Service[Request, Response] = Service.mk { req =>
38+
val rep = Contexts.broadcast.get(TraceContextFilter.SpanContextKey) match {
39+
case None =>
40+
val rep = Response(Status.BadRequest)
41+
rep.contentString = "Broadcast context not set"
42+
rep
43+
case Some(ctx) if ctx.getTraceId != spanCtx.get.getTraceId =>
44+
val rep = Response(Status.BadRequest)
45+
rep.contentString =
46+
s"TraceId mismatch, expected ${spanCtx.get.getTraceId}, but was ${ctx.getTraceId}"
47+
rep
48+
case Some(_) =>
49+
Response(Status.Ok)
50+
}
51+
52+
Future.value(rep)
53+
}
54+
val server = httpServer(HttpPropagationUtil.getCloudTraceFormat).serve(
55+
new InetSocketAddress(InetAddress.getLoopbackAddress, 0),
56+
svc
57+
)
58+
val client = httpClient(HttpPropagationUtil.getCloudTraceFormat).newService(
59+
Name.bound(Address(server.boundAddress.asInstanceOf[InetSocketAddress])),
60+
"cheese-processor"
61+
)
62+
63+
val span = Tracing.getTracer
64+
.spanBuilder("test")
65+
.startSpan()
66+
assert(span.getContext.isValid)
67+
span.scopedAndEnd {
68+
spanCtx.set(Tracing.getTracer.getCurrentSpan.getContext)
69+
70+
val rep = await(client(Request()))
71+
assert(rep.status == Status.Ok, rep.contentString)
72+
}
73+
client.close()
74+
server.close()
2675
}
2776

2877
test("SpanContext is propagated") {
@@ -45,11 +94,11 @@ class HttpEndToEndTest extends FunSuite {
4594

4695
Future.value(rep)
4796
}
48-
val server = httpServer.serve(
97+
val server = httpServer().serve(
4998
new InetSocketAddress(InetAddress.getLoopbackAddress, 0),
5099
svc
51100
)
52-
val client = httpClient.newService(
101+
val client = httpClient().newService(
53102
Name.bound(Address(server.boundAddress.asInstanceOf[InetSocketAddress])),
54103
"cheese-processor"
55104
)

0 commit comments

Comments
 (0)