Skip to content
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

zio-logging: add writeMarkerCause, fix zio doc references #814

Merged
merged 6 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/tofu.logging.recipes.zio.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ object BarService {
```

What can we learn from this code?
1. According to ZIO [Module Pattern 2.0](https://zio.dev/docs/datatypes/contextual/index#module-pattern-20)
1. According to ZIO [Module Pattern 2.0](https://zio.dev/1.x/datatypes/contextual/index#module-pattern-20)
class constructors are used to define service dependencies. At the end of the day the class constructor
is lifted to ZLayer: `(new BarServiceImpl(_, _)).toLayer`
2. `TofuLogs` is a type alias for `Has[ZLogging.Make]`, but ZIO encourages us to use explicitly the `Has` wrapper
Expand Down Expand Up @@ -139,9 +139,9 @@ Here we use [Tofu Derevo](https://github.com/tofu-tf/derevo) for automatic deriv
One possible way to add a context to your logs is to use `layerPlainWithContext` which encapsulates dealing with the context inside
(otherwise you can use `layerContextual` retrieving the context from a ZIO environment `R`, but we won't cover it here).

The main idea of this approach is to store your context in ZIO [FiberRef](https://zio.dev/docs/datatypes/fiber/fiberref).
The main idea of this approach is to store your context in ZIO [FiberRef](https://zio.dev/1.x/datatypes/fiber/fiberref).
It provides all the power of State Monad. Unlike Java's `ThreadLocal`, `FiberRef` has copy-on-fork semantic:
a child [Fiber](https://zio.dev/docs/datatypes/fiber/fiber/) starts with `FiberRef` values of its parent.
a child [Fiber](https://zio.dev/1.x/datatypes/fiber/fiber/) starts with `FiberRef` values of its parent.
When the child set a new value of FiberRef, the change is visible only to the child itself. This means if we set `requestId` value to `117`
(e.g. at the start of the request) and pass the `FiberRef` to a child fiber, it sees the value `117`.

Expand Down
7 changes: 7 additions & 0 deletions modules/logging/layout/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package tofu.logging

import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.read.ListAppender
import org.scalatest.funsuite.AnyFunSuite
import org.slf4j.LoggerFactory
import tofu.{Delay, WithContext}

import scala.jdk.CollectionConverters._

class LogbackSuite extends AnyFunSuite {
type Ctx = Map[String, String]
implicit val delay: Delay[Ctx => *] = new Delay[Ctx => *] {
def delay[A](a: => A) = _ => a
}

implicit val context: WithContext[Ctx => *, Ctx] = WithContext.make(identity)

implicit val logs: Logging.Make[Ctx => *] = Logging.Make.contextual[Ctx => *, Ctx]

val appender = new ListAppender[ILoggingEvent]

val logger = LoggerFactory.getLogger(this.getClass()).asInstanceOf[Logger]
appender.start()
logger.addAppender(appender)

val logging = logs.forService[LogbackSuite]
test("throwable sent as throwable") {

val error = new Exception("Wild Effect Appeared")

logging.debugCause("Hello", error, Map("oh" -> "noo"))(Map("ehm" -> "umm"))

Thread.sleep(100)

val first = appender.list.asScala.head.getThrowableProxy()
assert(first.getMessage === "Wild Effect Appeared")
assert(first.getClassName === classOf[Exception].getName)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,37 @@ object UniversalLogging {
case Warn => logger.warn(marker, message, values: _*)
case Error => logger.error(marker, message, values: _*)
}

private[impl] final def writeCause(
level: Logging.Level,
logger: Logger,
cause: Throwable,
message: String,
values: Seq[LoggedValue]
): Unit =
level match {
case Trace => logger.trace(message, values :+ cause: _*)
case Debug => logger.debug(message, values :+ cause: _*)
case Info => logger.info(message, values :+ cause: _*)
case Warn => logger.warn(message, values :+ cause: _*)
case Error => logger.error(message, values :+ cause: _*)
}

private[impl] final def writeMarkerCause(
level: Logging.Level,
logger: Logger,
marker: Marker,
cause: Throwable,
message: String,
values: Seq[LoggedValue]
): Unit =
level match {
case Trace => logger.trace(marker, message, values :+ cause: _*)
case Debug => logger.debug(marker, message, values :+ cause: _*)
case Info => logger.info(marker, message, values :+ cause: _*)
case Warn => logger.warn(marker, message, values :+ cause: _*)
case Error => logger.error(marker, message, values :+ cause: _*)
}
}

class UniversalLogging[F[_]](name: String)(implicit F: Delay[F]) extends Logging[F] {
Expand All @@ -54,6 +85,13 @@ class UniversalLogging[F[_]](name: String)(implicit F: Delay[F]) extends Logging
if (UniversalLogging.enabled(level, logger))
UniversalLogging.writeMarker(level, logger, marker, message, values)
}

override def writeCause(level: Logging.Level, message: String, cause: Throwable, values: LoggedValue*): F[Unit] =
F.delay {
val logger = LoggerFactory.getLogger(name)
if (UniversalLogging.enabled(level, logger))
UniversalLogging.writeCause(level, logger, cause, message, values)
}
}

class UniversalContextLogging[F[_]](name: String, fctx: (LoggedValue => Unit) => F[Unit]) extends Logging[F] {
Expand All @@ -64,6 +102,13 @@ class UniversalContextLogging[F[_]](name: String, fctx: (LoggedValue => Unit) =>
UniversalLogging.writeMarker(level, logger, ContextMarker(ctx), message, values)
}

override def writeCause(level: Logging.Level, message: String, cause: Throwable, values: LoggedValue*): F[Unit] =
fctx { ctx =>
val logger = LoggerFactory.getLogger(name)
if (UniversalLogging.enabled(level, logger))
UniversalLogging.writeMarkerCause(level, logger, ContextMarker(ctx), cause, message, values)
}

override def writeMarker(level: Logging.Level, message: String, marker: Marker, values: LoggedValue*): F[Unit] =
fctx { ctx =>
val logger = LoggerFactory.getLogger(name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,18 @@ class ZUniversalContextLogging[R, C: Loggable](name: String, ctxLog: URIO[R, C])
UniversalLogging.writeMarker(level, logger, ContextMarker(ctx, List(marker)), message, values)
}
}

override def writeCause(
level: Logging.Level,
message: String,
cause: Throwable,
values: LoggedValue*
): URIO[R, Unit] =
ctxLog.flatMap { ctx =>
ZIO.effectTotal {
val logger = LoggerFactory.getLogger(name)
if (UniversalLogging.enabled(level, logger))
UniversalLogging.writeMarkerCause(level, logger, ContextMarker(ctx), cause, message, values)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,11 @@ class ZUniversalLogging(name: String) extends Logging[UIO] {
if (UniversalLogging.enabled(level, logger))
UniversalLogging.writeMarker(level, logger, marker, message, values)
}

override def writeCause(level: Logging.Level, message: String, cause: Throwable, values: LoggedValue*): UIO[Unit] =
ZIO.effectTotal {
val logger = LoggerFactory.getLogger(name)
if (UniversalLogging.enabled(level, logger))
UniversalLogging.writeCause(level, logger, cause, message, values)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package tofu.logging.zlogs
import tofu.logging.impl.{ZUniversalContextLogging, ZUniversalLogging}
import tofu.logging.{Loggable, Logging}
import zio._
import zio.interop.catz._

import scala.annotation.unused

object ZLogging {
Expand Down Expand Up @@ -36,5 +38,7 @@ object ZLogging {
new ZUniversalContextLogging[Any, Ctx](_, getContext(cs))
}
}

val layerEmpty: ULayer[Has[ZLogging.Make]] = ZLayer.succeed(_ => Logging.empty[UIO])
}
}