Skip to content

Commit

Permalink
twitter-server: Introduce OpenCensus zPages integration
Browse files Browse the repository at this point in the history
Problem

Users of OpenCensus would like access to it's zPages functionality.

Solution

Introduce a new module, `opencensus`, with a trait,
`com.twitter.server.opencensus.ZPagesAdminRoutes`, that can be mixed
into a `c.t.server.AdminHttpServer` to provide these pages.

Result

Users of this trait can visit these endpoints on their admin server
and see the zPages:

 - /rpcz
 - /statz
 - /tracez
 - /traceconfigz

JIRA Issues: CSL-7329

Differential Revision: https://phabricator.twitter.biz/D284757
  • Loading branch information
kevinoliver authored and jenkins committed Mar 12, 2019
1 parent 92e4dad commit 829bd84
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
21 changes: 21 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ val jacksonLibs = Seq(
"com.fasterxml.jackson.core" % "jackson-databind" % jacksonVersion,
"com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion
)
val opencensusVersion = "0.19.1"
val slf4jVersion = "1.7.21"

def util(which: String) = "com.twitter" %% ("util-"+which) % releaseVersion
Expand Down Expand Up @@ -102,6 +103,7 @@ lazy val root = (project in file("."))
.settings(noPublishSettings)
.aggregate(
twitterServer,
twitterServerOpenCensus,
twitterServerSlf4jJdk14,
twitterServerSlf4jLog4j12,
twitterServerSlf4jLogbackClassic)
Expand Down Expand Up @@ -132,6 +134,25 @@ lazy val twitterServer = (project in file("server"))
),
libraryDependencies ++= jacksonLibs)

lazy val twitterServerOpenCensus = (project in file("opencensus"))
.enablePlugins(
ScalaUnidocPlugin
)
.settings(
name := "twitter-server-opencensus",
moduleName := "twitter-server-opencensus",
sharedSettings)
.settings(
libraryDependencies ++= Seq(
finagle("core"),
finagle("http"),
"io.opencensus" % "opencensus-api" % opencensusVersion,
"io.opencensus" % "opencensus-contrib-zpages" % opencensusVersion
))
.dependsOn(
twitterServer)


lazy val twitterServerSlf4jJdk14 = (project in file("slf4j-jdk14"))
.settings(
name := "twitter-server-slf4j-jdk14",
Expand Down
29 changes: 29 additions & 0 deletions opencensus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# OpenCensus zPages

This module integrates TwitterServer with [OpenCensus zPages](https://opencensus.io/zpages/).

## Current State

This library is in an experimental state.

## Details

By mixing in `ZPagesAdminRoutes` into a `TwitterServer`, zPages
will be served on admin routes:

- /rpcz
- /statz
- /tracez
- /traceconfigz

For example:

```
import com.twitter.server.TwitterServer
import com.twitter.server.opencensus.ZPagesAdminRoutes
object MyServer extends TwitterServer with ZPagesAdminRoutes {
// ...
}
```

11 changes: 11 additions & 0 deletions opencensus/src/main/scala/com/twitter/server/opencensus/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
scala_library(
sources = globs("*.scala"),
compiler_option_sets = {"fatal_warnings"},
dependencies = [
"3rdparty/jvm/io/opencensus:opencensus-api",
"3rdparty/jvm/io/opencensus:opencensus-contrib-zpages",
"finagle/finagle-core",
"finagle/finagle-http",
"twitter-server/server/src/main/scala",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.twitter.server.opencensus

import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.io.Buf
import com.twitter.server.AdminHttpServer
import com.twitter.util.{Future, FuturePool}
import io.opencensus.contrib.zpages.{ZPageHandler, ZPageHandlers}
import java.io.ByteArrayOutputStream
import scala.collection.JavaConverters._

private object ZPagesAdminRoutes {
private def zpageHandlerToService(
handler: ZPageHandler,
name: String
): Service[Request, Response] =
new Service[Request, Response] {
override def toString: String = s"ZPageHandlerService($name)"

def apply(request: Request): Future[Response] = {
val requestParams =
request
.getParamNames().asScala.map { name =>
name -> request.getParam(name)
}.toMap.asJava

// process in a FuturePool to handle the possibility
// of zpages having blocking code.
FuturePool.unboundedPool {
val output = new ByteArrayOutputStream()
handler.emitHtml(requestParams, output)
Response(request)
.status(Status.Ok)
.content(Buf.ByteArray.Owned(output.toByteArray))
}
}
}
}

/**
* Mix into an [[AdminHttpServer]] to serve OpenCensus zPages on admin routes.
*
* The zPages will be available at:
* - /rpcz
* - /statz
* - /tracez
* - /traceconfigz
*
* For example:
* {{{
* import com.twitter.server.TwitterServer
* import com.twitter.server.opencensus.ZPagesAdminRoutes
*
* object MyServer extends TwitterServer with ZPagesAdminRoutes {
* // ...
* }
* }}}
*
* @see [[https://opencensus.io/zpages/]]
*/
trait ZPagesAdminRoutes { self: AdminHttpServer =>

addAdminRoutes {
val handlers =
(ZPageHandlers.getRpczZpageHandler, "RPCz") ::
(ZPageHandlers.getStatszZPageHandler, "Statz") ::
(ZPageHandlers.getTraceConfigzZPageHandler, "Trace Configz") ::
(ZPageHandlers.getTracezZPageHandler, "Tracez") ::
Nil

handlers.map {
case (handler, name) =>
AdminHttpServer.mkRoute(
path = handler.getUrlPath,
handler = ZPagesAdminRoutes.zpageHandlerToService(handler, name),
alias = name,
group = Some("OpenCensus"),
includeInIndex = true
)
}
}
}

0 comments on commit 829bd84

Please sign in to comment.