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

Move logging to kernel-only dependency #710

Merged
merged 4 commits into from
Jul 20, 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
9 changes: 4 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ lazy val loggingStr = project
defaultSettings,
libraryDependencies ++= Seq(
catsCore,
catsEffect,
circeCore,
tethys,
tethysJackson,
Expand All @@ -95,7 +94,7 @@ lazy val loggingStr = project
catsTagless
),
)
.dependsOn(core, concurrent)
.dependsOn(kernel)

lazy val loggingDer = project
.in(file("modules/logging/derivation"))
Expand All @@ -111,7 +110,7 @@ lazy val loggingLayout = project
.in(file("modules/logging/layout"))
.settings(
defaultSettings,
libraryDependencies ++= Seq(catsCore, catsEffect, logback, slf4j),
libraryDependencies ++= Seq(catsCore, logback, slf4j),
name := "tofu-logging-layout"
)
.dependsOn(loggingStr)
Expand All @@ -121,9 +120,9 @@ lazy val loggingUtil = project
.settings(
defaultSettings,
name := "tofu-logging-util",
libraryDependencies += slf4j,
libraryDependencies ++= Vector(slf4j, catsEffect),
)
.dependsOn(loggingStr, concurrent)
.dependsOn(loggingStr)

lazy val loggingShapeless = project
.in(file("modules/logging/interop/shapeless"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,73 +1,79 @@
package tofu.logging

import cats.effect.SyncIO
import cats.effect.concurrent.Ref
import cats.instances.list._
import cats.instances.unit._
import tofu.syntax.monadic._
import cats.syntax.foldable._
import cats.syntax.traverse._
import scala.collection.mutable

import cats.{Applicative, Monoid}
import io.circe.Json
import cats.Eval

sealed trait LogTree {
import LogTree._
def json: SyncIO[Json] = this match {
case LogDict(values) =>
values.get.flatMap { d => d.toList.traverse { case (name, t) => t.json.map(name -> _) }.map(Json.obj(_: _*)) }
def json: Eval[Json] = this match {
case dict: LogDict =>
dict.getList.flatMap { _.traverse { case (name, t) => t.json.map(name -> _) }.map(Json.obj(_: _*)) }
case LogArr(items) => items.toList.traverse(_.json).map(Json.arr(_: _*))
case value: LogParamValue => value.jsonVal.pure[SyncIO]
case value: LogParamValue => value.jsonVal.pure[Eval]
}
}

// pretty much unsafe mutable builder for circe.Json from logs
object LogTree extends LogBuilder[Json] {
implicit def monoid: Monoid[Output] = Applicative.monoid

type Output = SyncIO[Unit]
type ValRes = SyncIO[Boolean]
type Value = Ref[SyncIO, LogTree]
type Top = LogDict
type Output = Eval[Unit]
type ValRes = Eval[Boolean]
class Value(private var tree: LogTree) {
def get = Eval.always(tree)
def set(value: LogTree) = Eval.always { tree = value }
}
type Top = LogDict

final case class LogDict(values: Ref[SyncIO, Map[String, LogTree]]) extends LogTree {
def add(name: String, tree: LogTree): SyncIO[Unit] = values update (_ + (name -> tree))
class LogDict(private val values: mutable.Map[String, LogTree]) extends LogTree {
def add(name: String, tree: LogTree): Eval[Unit] = Eval.always(values.update(name, tree))
def getList: Eval[List[(String, LogTree)]] = Eval.always(values.toList)
}

final case class LogArr(values: Iterable[LogTree]) extends LogTree

private val newdict = Ref[SyncIO].of(Map.empty[String, LogTree]).map(LogDict)
private val newtree = Ref[SyncIO].of(NullValue: LogTree)
private val newdict = Eval.always(mutable.Map.empty[String, LogTree]).map(new LogDict(_))
private val newtree = Eval.always(new Value(NullValue))

val receiver: LogRenderer[LogDict, Value, Output, ValRes] = new LogRenderer[LogDict, Value, Output, ValRes] {

def coalesce(f: Value => ValRes, g: Value => ValRes, v: Value): ValRes =
f(v).flatMap(written => if (written) SyncIO(true) else g(v))
def zero(v: Ref[SyncIO, LogTree]): SyncIO[Boolean] = SyncIO(false)
def noop(i: LogDict): SyncIO[Unit] = SyncIO.unit
def combine(x: SyncIO[Unit], y: SyncIO[Unit]): SyncIO[Unit] = x *> y
def putValue(value: LogParamValue, input: Ref[SyncIO, LogTree]): SyncIO[Boolean] = input.set(value) as true
def sub(name: String, input: LogDict)(receive: Ref[SyncIO, LogTree] => SyncIO[Boolean]): SyncIO[Unit] =
def coalesce(f: Value => ValRes, g: Value => ValRes, v: Value): ValRes =
f(v).flatMap(written => if (written) Eval.now(true) else g(v))
def zero(v: Value): Eval[Boolean] = Eval.now(false)
def noop(i: LogDict): Eval[Unit] = Eval.now(())
def combine(x: Eval[Unit], y: Eval[Unit]): Eval[Unit] = x *> y
def putValue(value: LogParamValue, input: Value): Eval[Boolean] = input.set(value) as true

def sub(name: String, input: LogDict)(receive: Value => Eval[Boolean]): Eval[Unit] =
for {
t <- newtree
_ <- receive(t)
v <- t.get
_ <- input.add(name, v)
} yield ()

def list(size: Int, input: Ref[SyncIO, LogTree])(receive: (Value, Int) => SyncIO[Boolean]): SyncIO[Boolean] =
def list(size: Int, input: Value)(receive: (Value, Int) => Eval[Boolean]): Eval[Boolean] =
for {
ts <- newtree.replicateA(size)
_ <- ts.zipWithIndex.traverse_(receive.tupled)
vs <- ts.traverse(_.get)
_ <- input.set(LogArr(vs))
} yield true
def dict(input: Ref[SyncIO, LogTree])(receive: LogDict => SyncIO[Unit]): SyncIO[Boolean] =
def dict(input: Value)(receive: LogDict => Eval[Unit]): Eval[Boolean] =
newdict flatMap input.set as true
}

def buildJson(buildTree: LogDict => Output): SyncIO[Json] = newdict flatTap buildTree flatMap (_.json)
def buildJson(buildTree: LogDict => Output): Eval[Json] = newdict flatTap buildTree flatMap (_.json)

def make(f: LogDict => SyncIO[Unit]): Json = buildJson(f).unsafeRunSync()
def make(f: LogDict => Eval[Unit]): Json = buildJson(f).value
}

sealed trait LogParamValue extends LogTree {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ trait LoggableContext[F[_]] {
object LoggableContext {
def of[F[_]] = new LoggableContextPA[F]
private[logging] final class LoggableContextPA[F[_]](private val dummy: Boolean = true) extends AnyVal {
def instance[C](implicit ctx: F HasContext C, ctxLog: Loggable[C]): LoggableContext[F] = new LoggableContext[F] {
def instance[C](implicit ctx: F WithContext C, ctxLog: Loggable[C]): LoggableContext[F] = new LoggableContext[F] {
type Ctx = C
val loggable: Loggable[C] = ctxLog
val context: F WithContext C = ctx.asWithContext
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package tofu.logging

import cats.data.Tuple2K
import cats.effect.Sync
import cats.kernel.Monoid
import cats.tagless.syntax.functorK._
import cats.tagless.{ApplyK, FunctorK}
Expand All @@ -24,6 +23,7 @@ import tofu.logging.impl.UniversalLogs
import tofu.Delay
import tofu.WithContext
import tofu.logging.impl.UniversalContextLogs
import tofu.concurrent.MakeQVar

/** A helper for creating instances of [[tofu.logging.Logging]], defining a way these instances will behave while doing logging.
* Can create instances either on a by-name basic or a type tag basic.
Expand Down Expand Up @@ -88,21 +88,21 @@ object Logs extends LogsInstances0 {
/** Returns an instance of [[tofu.logging.Logs]] that requires [[cats.effect.Sync]] to perform logging side-effects.
* Has no notion of context.
*/
def sync[I[_]: Sync, F[_]: Sync]: Logs[I, F] = new Logs[I, F] {
def byName(name: String): I[Logging[F]] = Sync[I].delay(new SyncLogging[F](LoggerFactory.getLogger(name)))
def sync[I[_]: Delay, F[_]: Delay: Monad]: Logs[I, F] = new Logs[I, F] {
def byName(name: String): I[Logging[F]] = Delay[I].delay(new SyncLogging[F](LoggerFactory.getLogger(name)))
}

def universal[F[_]: Delay]: Logs.Universal[F] = new UniversalLogs
def contextual[F[_]: FlatMap: Delay, C: Loggable](implicit FC: F WithContext C): Logs.Universal[F] =
new UniversalContextLogs[F, C]

def withContext[I[_]: Sync, F[_]: Sync](implicit ctx: LoggableContext[F]): Logs[I, F] = {
def withContext[I[_]: Delay, F[_]: Monad: Delay](implicit ctx: LoggableContext[F]): Logs[I, F] = {
import ctx.loggable
new Logs[I, F] {
override def forService[Svc: ClassTag]: I[Logging[F]] =
Sync[I].delay(new ContextSyncLoggingImpl[F, ctx.Ctx](ctx.context, loggerForService[Svc]))
Delay[I].delay(new ContextSyncLoggingImpl[F, ctx.Ctx](ctx.context, loggerForService[Svc]))
override def byName(name: String): I[Logging[F]] =
Sync[I].delay(new ContextSyncLoggingImpl[F, ctx.Ctx](ctx.context, LoggerFactory.getLogger(name)))
Delay[I].delay(new ContextSyncLoggingImpl[F, ctx.Ctx](ctx.context, LoggerFactory.getLogger(name)))
}
}

Expand Down Expand Up @@ -138,7 +138,7 @@ object Logs extends LogsInstances0 {
}

final implicit class LogsOps[I[_], F[_]](private val logs: Logs[I, F]) extends AnyVal {
def cached(implicit IM: Monad[I], IQ: QVars[I], IG: Guarantee[I]): I[Logs[I, F]] =
def cached(implicit IM: Monad[I], IQ: MakeQVar[I, I], IG: Guarantee[I]): I[Logs[I, F]] =
QVars[I]
.of(Map.empty[String, Logging[F]])
.map2(
Expand All @@ -149,7 +149,7 @@ object Logs extends LogsInstances0 {

def cachedUniversal(implicit
IM: Monad[I],
IQ: QVars[I],
IQ: MakeQVar[I, I],
IG: Guarantee[I],
il: Lift[I, F],
F: FlatMap[F]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import cats.Monad
import tofu.Guarantee
import tofu.concurrent.QVar
import tofu.logging.{LoggedValue, Logging, Logs}
import tofu.syntax.bracket._
import tofu.syntax.guarantee._
import tofu.syntax.monadic._

import scala.reflect.{ClassTag, classTag}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import cats.Applicative
import cats.syntax.applicative._
import org.slf4j.{Logger, Marker}

class ContextLoggingImpl[F[_]: Applicative, C: Loggable, Service](context: F HasContext C, logger: Logger)
class ContextLoggingImpl[F[_]: Applicative, C: Loggable, Service](context: F WithContext C, logger: Logger)
extends LoggingImpl[F](logger) {

override def trace(message: String, values: LoggedValue*) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package tofu
package logging
package impl

import cats.effect.Sync
import cats.syntax.applicative._
import org.slf4j.{Logger, Marker}
import tofu.syntax.monadic._
import cats.Monad

class ContextSyncLoggingImpl[F[_], C: Loggable](context: F HasContext C, logger: Logger)(implicit F: Sync[F])
class ContextSyncLoggingImpl[F[_]: Monad, C: Loggable](context: F WithContext C, logger: Logger)(implicit F: Delay[F])
extends LoggingImpl[F](logger) {

override def trace(message: String, values: LoggedValue*) =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package tofu.logging
package impl

import cats.effect.Sync
import cats.syntax.applicative._
import org.slf4j.{Logger, Marker}
import tofu.syntax.monadic._
import cats.Monad
import tofu.Delay

class SyncLogging[F[_]](logger: Logger)(implicit F: Sync[F]) extends LoggingImpl[F](logger) {
class SyncLogging[F[_]: Monad](logger: Logger)(implicit F: Delay[F]) extends LoggingImpl[F](logger) {
override def trace(message: String, values: LoggedValue*): F[Unit] =
F.delay(logger.trace(message, values: _*)).whenA(traceEnabled)
override def debug(message: String, values: LoggedValue*): F[Unit] =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package tofu.logging

import tofu.HasContext
import tofu.compat.unused
import tofu.WithContext

object LoggableContextSuite {

def summonInstance[R: Loggable, F[_]: *[_] HasContext R](): Unit = {
def summonInstance[R: Loggable, F[_]: *[_] WithContext R](): Unit = {
LoggableContext.of[F].instance
()
}

@unused
def summonInstanceUnambiguously[R1: Loggable, R2: Loggable, F[_]: *[_] HasContext R1: *[_] HasContext R2](): Unit = {
def summonInstanceUnambiguously[R1: Loggable, R2: Loggable, F[_]: *[_] WithContext R1: *[_] WithContext R2]()
: Unit = {
LoggableContext.of[F].instance[R1]
()
}
Expand Down