aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/core/reporting
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/core/reporting')
-rw-r--r--src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala14
-rw-r--r--src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala17
-rw-r--r--src/main/scala/xyz/driver/core/reporting/NoReporter.scala8
-rw-r--r--src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala8
-rw-r--r--src/main/scala/xyz/driver/core/reporting/Reporter.scala46
-rw-r--r--src/main/scala/xyz/driver/core/reporting/ScalaLoggerLike.scala32
-rw-r--r--src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala36
-rw-r--r--src/main/scala/xyz/driver/core/reporting/SpanContext.scala4
8 files changed, 96 insertions, 69 deletions
diff --git a/src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala b/src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala
new file mode 100644
index 0000000..f5c41cf
--- /dev/null
+++ b/src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala
@@ -0,0 +1,14 @@
+package xyz.driver.core
+package reporting
+
+import org.slf4j.MDC
+
+trait GoogleMdcLogger extends Reporter { self: GoogleReporter =>
+
+ abstract override def log(severity: Reporter.Severity, message: String, reason: Option[Throwable])(
+ implicit ctx: SpanContext): Unit = {
+ MDC.put("trace", s"projects/${credentials.getProjectId}/traces/${ctx.traceId}")
+ super.log(severity, message, reason)
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala b/src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala
index d4d20a4..14c4954 100644
--- a/src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala
+++ b/src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala
@@ -1,5 +1,6 @@
package xyz.driver.core
package reporting
+
import java.security.Signature
import java.time.Instant
import java.util
@@ -9,8 +10,6 @@ import akka.stream.scaladsl.{Flow, RestartSink, Sink, Source, SourceQueueWithCom
import akka.stream.{Materializer, OverflowStrategy}
import com.google.auth.oauth2.ServiceAccountCredentials
import com.softwaremill.sttp._
-import com.typesafe.scalalogging.Logger
-import org.slf4j.MDC
import spray.json.DerivedJsonProtocol._
import spray.json._
import xyz.driver.core.reporting.Reporter.CausalRelation
@@ -18,22 +17,21 @@ import xyz.driver.core.reporting.Reporter.CausalRelation
import scala.async.Async._
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}
-import scala.util.{Failure, Random, Success, Try}
import scala.util.control.NonFatal
+import scala.util.{Failure, Random, Success, Try}
/** A reporter that collects traces and submits them to
* [[https://cloud.google.com/trace/docs/reference/v2/rest/ Google's Stackdriver Trace API]].
*/
class GoogleReporter(
- credentials: ServiceAccountCredentials,
+ val credentials: ServiceAccountCredentials,
namespace: String,
- val logger: Logger,
buffer: Int = GoogleReporter.DefaultBufferSize,
interval: FiniteDuration = GoogleReporter.DefaultInterval)(
implicit client: SttpBackend[Future, _],
mat: Materializer,
ec: ExecutionContext
-) extends Reporter with ScalaLoggerLike {
+) extends Reporter {
import GoogleReporter._
private val getToken: () => Future[String] = Refresh.every(55.minutes) {
@@ -156,10 +154,9 @@ class GoogleReporter(
}
override def log(severity: Reporter.Severity, message: String, reason: Option[Throwable])(
- implicit ctx: SpanContext): Unit = {
- MDC.put("trace", s"projects/${credentials.getProjectId}/traces/${ctx.traceId}")
- super.log(severity, message, reason)
- }
+ implicit ctx: SpanContext): Unit =
+ sys.error("Submitting logs directly to GCP is " +
+ "currently not supported. Messages should go to stdout.") // TODO: attach logs to traces and submit them directly
}
diff --git a/src/main/scala/xyz/driver/core/reporting/NoReporter.scala b/src/main/scala/xyz/driver/core/reporting/NoReporter.scala
new file mode 100644
index 0000000..c1c81f4
--- /dev/null
+++ b/src/main/scala/xyz/driver/core/reporting/NoReporter.scala
@@ -0,0 +1,8 @@
+package xyz.driver.core
+package reporting
+
+trait NoReporter extends NoTraceReporter {
+ override def log(severity: Reporter.Severity, message: String, reason: Option[Throwable])(
+ implicit ctx: SpanContext): Unit = ()
+}
+object NoReporter extends NoReporter
diff --git a/src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala b/src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala
index 9179f42..b49cfda 100644
--- a/src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala
+++ b/src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala
@@ -1,18 +1,20 @@
package xyz.driver.core
package reporting
-import com.typesafe.scalalogging.Logger
-
import scala.concurrent.Future
-class NoTraceReporter(val logger: Logger) extends Reporter with ScalaLoggerLike {
+/** A reporter mixin that does not emit traces. */
+trait NoTraceReporter extends Reporter {
+
override def traceWithOptionalParent[A](
name: String,
tags: Map[String, String],
parent: Option[(SpanContext, Reporter.CausalRelation)])(op: SpanContext => A): A = op(SpanContext.fresh())
+
override def traceWithOptionalParentAsync[A](
name: String,
tags: Map[String, String],
parent: Option[(SpanContext, Reporter.CausalRelation)])(op: SpanContext => Future[A]): Future[A] =
op(SpanContext.fresh())
+
}
diff --git a/src/main/scala/xyz/driver/core/reporting/Reporter.scala b/src/main/scala/xyz/driver/core/reporting/Reporter.scala
index 57e2310..469084c 100644
--- a/src/main/scala/xyz/driver/core/reporting/Reporter.scala
+++ b/src/main/scala/xyz/driver/core/reporting/Reporter.scala
@@ -1,8 +1,5 @@
-package xyz.driver.core.reporting
-
-import com.typesafe.scalalogging.Logger
-import org.slf4j.helpers.NOPLogger
-import xyz.driver.core.reporting.Reporter.{CausalRelation, Severity}
+package xyz.driver.core
+package reporting
import scala.concurrent.Future
@@ -13,7 +10,7 @@ import scala.concurrent.Future
* that led to a particular message. In synchronous systems, execution contexts can easily be determined by an
* external observer, and, as such, do not need to be propagated explicitly to sub-components (e.g. a stack trace on
* the JVM shows all relevant information). In asynchronous systems and especially distributed systems however,
- * execution contexts are not easily determined by an external observer and hence need to be explcictly passed across
+ * execution contexts are not easily determined by an external observer and hence need to be explicitly passed across
* service boundaries.
*
* This reporter provides tracing and logging utilities that explicitly require references to execution contexts
@@ -44,14 +41,15 @@ import scala.concurrent.Future
* }
* }}}
*
- * Note that computing traces may be a more expensive operation than traditional logging frameworks provide (in terms
- * of memory and processing). It should be used in interesting and actionable code paths.
+ * '''Note that computing traces may be a more expensive operation than traditional logging frameworks provide (in terms
+ * of memory and processing). It should be used in interesting and actionable code paths.'''
*
* @define rootWarning Note: the idea of the reporting framework is to pass along references to traces as
* implicit parameters. This method should only be used for top-level traces when no parent
* traces are available.
*/
trait Reporter {
+ import Reporter._
def traceWithOptionalParent[A](
name: String,
@@ -66,7 +64,7 @@ trait Reporter {
*
* $rootWarning
*/
- def traceRoot[A](name: String, tags: Map[String, String] = Map.empty)(op: SpanContext => A): A =
+ final def traceRoot[A](name: String, tags: Map[String, String] = Map.empty)(op: SpanContext => A): A =
traceWithOptionalParent(
name,
tags,
@@ -79,7 +77,8 @@ trait Reporter {
*
* @see traceRoot
*/
- def traceRootAsync[A](name: String, tags: Map[String, String] = Map.empty)(op: SpanContext => Future[A]): Future[A] =
+ final def traceRootAsync[A](name: String, tags: Map[String, String] = Map.empty)(
+ op: SpanContext => Future[A]): Future[A] =
traceWithOptionalParentAsync(
name,
tags,
@@ -102,8 +101,11 @@ trait Reporter {
* @tparam A Return type of the operation.
* @return The value of the child operation.
*/
- def trace[A](name: String, tags: Map[String, String] = Map.empty, relation: CausalRelation = CausalRelation.Child)(
- op: /* implicit (gotta wait for Scala 3) */ SpanContext => A)(implicit ctx: SpanContext): A =
+ final def trace[A](
+ name: String,
+ tags: Map[String, String] = Map.empty,
+ relation: CausalRelation = CausalRelation.Child)(op: /* implicit (gotta wait for Scala 3) */ SpanContext => A)(
+ implicit ctx: SpanContext): A =
traceWithOptionalParent(
name,
tags,
@@ -117,7 +119,7 @@ trait Reporter {
*
* @see trace
*/
- def traceAsync[A](
+ final def traceAsync[A](
name: String,
tags: Map[String, String] = Map.empty,
relation: CausalRelation = CausalRelation.Child)(
@@ -132,31 +134,29 @@ trait Reporter {
def log(severity: Severity, message: String, reason: Option[Throwable])(implicit ctx: SpanContext): Unit
/** Log a debug message. */
- def debug(message: String)(implicit ctx: SpanContext): Unit = log(Severity.Debug, message, None)
- def debug(message: String, reason: Throwable)(implicit ctx: SpanContext): Unit =
+ final def debug(message: String)(implicit ctx: SpanContext): Unit = log(Severity.Debug, message, None)
+ final def debug(message: String, reason: Throwable)(implicit ctx: SpanContext): Unit =
log(Severity.Debug, message, Some(reason))
/** Log an informational message. */
- def info(message: String)(implicit ctx: SpanContext): Unit = log(Severity.Informational, message, None)
- def info(message: String, reason: Throwable)(implicit ctx: SpanContext): Unit =
+ final def info(message: String)(implicit ctx: SpanContext): Unit = log(Severity.Informational, message, None)
+ final def info(message: String, reason: Throwable)(implicit ctx: SpanContext): Unit =
log(Severity.Informational, message, Some(reason))
/** Log a warning message. */
- def warn(message: String)(implicit ctx: SpanContext): Unit = log(Severity.Warning, message, None)
- def warn(message: String, reason: Throwable)(implicit ctx: SpanContext): Unit =
+ final def warn(message: String)(implicit ctx: SpanContext): Unit = log(Severity.Warning, message, None)
+ final def warn(message: String, reason: Throwable)(implicit ctx: SpanContext): Unit =
log(Severity.Warning, message, Some(reason))
/** Log an error message. */
- def error(message: String)(implicit ctx: SpanContext): Unit = log(Severity.Error, message, None)
- def error(message: String, reason: Throwable)(implicit ctx: SpanContext): Unit =
+ final def error(message: String)(implicit ctx: SpanContext): Unit = log(Severity.Error, message, None)
+ final def error(message: String, reason: Throwable)(implicit ctx: SpanContext): Unit =
log(Severity.Error, message, Some(reason))
}
object Reporter {
- val NoReporter: Reporter = new NoTraceReporter(Logger.apply(NOPLogger.NOP_LOGGER))
-
/** A relation in cause.
*
* Corresponds to
diff --git a/src/main/scala/xyz/driver/core/reporting/ScalaLoggerLike.scala b/src/main/scala/xyz/driver/core/reporting/ScalaLoggerLike.scala
deleted file mode 100644
index eda81fb..0000000
--- a/src/main/scala/xyz/driver/core/reporting/ScalaLoggerLike.scala
+++ /dev/null
@@ -1,32 +0,0 @@
-package xyz.driver.core.reporting
-import com.typesafe.scalalogging.Logger
-
-trait ScalaLoggerLike extends Reporter {
-
- def logger: Logger
-
- override def log(severity: Reporter.Severity, message: String, reason: Option[Throwable])(
- implicit ctx: SpanContext): Unit = severity match {
- case Reporter.Severity.Debug => logger.debug(message, reason.orNull)
- case Reporter.Severity.Informational => logger.info(message, reason.orNull)
- case Reporter.Severity.Warning => logger.warn(message, reason.orNull)
- case Reporter.Severity.Error => logger.error(message, reason.orNull)
- }
-
-}
-
-object ScalaLoggerLike {
- import scala.language.implicitConversions
-
- def defaultScalaLogger(json: Boolean = false): Logger = {
- if (json) {
- System.setProperty("logback.configurationFile", "deployed-logback.xml")
- } else {
- System.setProperty("logback.configurationFile", "logback.xml")
- }
- Logger.apply("application")
- }
-
- implicit def toScalaLogger(reporter: ScalaLoggerLike): Logger = reporter.logger
-
-}
diff --git a/src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala b/src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala
new file mode 100644
index 0000000..0ff5574
--- /dev/null
+++ b/src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala
@@ -0,0 +1,36 @@
+package xyz.driver.core
+package reporting
+
+import com.typesafe.scalalogging.{Logger => ScalaLogger}
+
+/** Compatibility mixin for reporters, that enables implicit conversions to scala-logging loggers. */
+trait ScalaLoggingCompat extends Reporter {
+ import Reporter.Severity
+
+ def logger: ScalaLogger
+
+ override def log(severity: Severity, message: String, reason: Option[Throwable])(implicit ctx: SpanContext): Unit =
+ severity match {
+ case Severity.Debug => logger.debug(message, reason.orNull)
+ case Severity.Informational => logger.info(message, reason.orNull)
+ case Severity.Warning => logger.warn(message, reason.orNull)
+ case Severity.Error => logger.error(message, reason.orNull)
+ }
+
+}
+
+object ScalaLoggingCompat {
+ import scala.language.implicitConversions
+
+ def defaultScalaLogger(json: Boolean = false): ScalaLogger = {
+ if (json) {
+ System.setProperty("logback.configurationFile", "deployed-logback.xml")
+ } else {
+ System.setProperty("logback.configurationFile", "logback.xml")
+ }
+ ScalaLogger.apply("application")
+ }
+
+ implicit def toScalaLogger(logger: ScalaLoggingCompat): ScalaLogger = logger.logger
+
+}
diff --git a/src/main/scala/xyz/driver/core/reporting/SpanContext.scala b/src/main/scala/xyz/driver/core/reporting/SpanContext.scala
index ecc2ba3..04a822d 100644
--- a/src/main/scala/xyz/driver/core/reporting/SpanContext.scala
+++ b/src/main/scala/xyz/driver/core/reporting/SpanContext.scala
@@ -1,10 +1,12 @@
package xyz.driver.core
package reporting
+
import scala.util.Random
case class SpanContext private[core] (traceId: String, spanId: String)
+
object SpanContext {
- def fresh() = SpanContext(
+ def fresh(): SpanContext = SpanContext(
f"${Random.nextLong()}%016x${Random.nextLong()}%016x",
f"${Random.nextLong()}%016x"
)