From 04ff3e124d7ba4c4ca560689cbabaaa5a21c1359 Mon Sep 17 00:00:00 2001 From: Oleg Nizhnik Date: Thu, 23 Jan 2020 11:14:01 +0300 Subject: [PATCH] Add reference logging --- build.sbt | 17 ++++++-- core/src/main/scala/tofu/syntax/bracket.scala | 2 +- .../scala-2.12/tofu/logging/LogsVOps.scala | 2 +- .../scala-2.13/tofu/logging/LogsVOps.scala | 2 +- .../src/main/scala/tofu/logging/Logging.scala | 6 +-- .../src/main/scala/tofu/logging/Logs.scala | 6 +-- .../scala/tofu/logging/atom/AtomLogger.scala | 43 +++++++++++++++++++ 7 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 logging/util/src/main/scala/tofu/logging/atom/AtomLogger.scala diff --git a/build.sbt b/build.sbt index c82acad55..8ce3729b3 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,7 @@ import Publish._, Dependencies._ import com.typesafe.sbt.SbtGit.git -val libVersion = "0.6.1" +val libVersion = "0.6.2" lazy val setMinorVersion = minorVersion := { CrossVersion.partialVersion(scalaVersion.value) match { @@ -105,9 +105,20 @@ lazy val loggingLayout = project ) .dependsOn(loggingStr) +lazy val loggingUtil = project + .in(file("logging/util")) + .settings( + defaultSettings, + publishName := "logging-util", + libraryDependencies += slf4j, + ) + .dependsOn(loggingStr, concurrent) + + + lazy val logging = project - .dependsOn(loggingStr, loggingDer, loggingLayout) - .aggregate(loggingStr, loggingDer, loggingLayout) + .dependsOn(loggingStr, loggingDer, loggingLayout, loggingUtil) + .aggregate(loggingStr, loggingDer, loggingLayout, loggingUtil) .settings(defaultSettings) lazy val env = project diff --git a/core/src/main/scala/tofu/syntax/bracket.scala b/core/src/main/scala/tofu/syntax/bracket.scala index d7fa38731..dd31e25fe 100644 --- a/core/src/main/scala/tofu/syntax/bracket.scala +++ b/core/src/main/scala/tofu/syntax/bracket.scala @@ -19,7 +19,7 @@ object bracket { def bracketAlways[B, C]( use: A => F[B] - )(release: A => F[C])(implicit F: Applicative[F], FG: Guarantee[F]): F[B] = + )(release: A => F[C])(FG: Guarantee[F]): F[B] = FG.bracket(fa)(use) { case (a, _) => release(a) } def guaranteeIncomplete[B](release: F[B])(implicit F: Applicative[F], FG: Guarantee[F]): F[A] = diff --git a/logging/structured/src/main/scala-2.12/tofu/logging/LogsVOps.scala b/logging/structured/src/main/scala-2.12/tofu/logging/LogsVOps.scala index cb823c7a4..bc4b902fc 100644 --- a/logging/structured/src/main/scala-2.12/tofu/logging/LogsVOps.scala +++ b/logging/structured/src/main/scala-2.12/tofu/logging/LogsVOps.scala @@ -1,3 +1,3 @@ package tofu.logging -trait LogsVOps[I[_], F[_]] +trait LogsVOps[+I[_], F[_]] diff --git a/logging/structured/src/main/scala-2.13/tofu/logging/LogsVOps.scala b/logging/structured/src/main/scala-2.13/tofu/logging/LogsVOps.scala index bfcbdd5ad..cbc335eaf 100644 --- a/logging/structured/src/main/scala-2.13/tofu/logging/LogsVOps.scala +++ b/logging/structured/src/main/scala-2.13/tofu/logging/LogsVOps.scala @@ -1,6 +1,6 @@ package tofu.logging -trait LogsVOps[I[_], F[_]] { self: Logs[I, F] => +trait LogsVOps[+I[_], F[_]] { self: Logs[I, F] => final def named[name <: String with Singleton]( implicit name: ValueOf[name] ): I[ServiceLogging[F, name]] = diff --git a/logging/structured/src/main/scala/tofu/logging/Logging.scala b/logging/structured/src/main/scala/tofu/logging/Logging.scala index 5feeb1bd9..97e909b7e 100644 --- a/logging/structured/src/main/scala/tofu/logging/Logging.scala +++ b/logging/structured/src/main/scala/tofu/logging/Logging.scala @@ -7,9 +7,9 @@ import cats.{Applicative, Apply, FlatMap} import com.github.ghik.silencer.silent import org.slf4j.{Logger, LoggerFactory, Marker} import tofu.higherKind -import tofu.higherKind.{Embed, RepresentableK} +import tofu.higherKind.{Embed, Function2K, RepresentableK} import tofu.logging.impl.EmbedLogging -import tofu.syntax.functionK._ +import tofu.syntax.monoidalK._ import scala.reflect.ClassTag @@ -94,7 +94,7 @@ object Logging { /** having two logging implementation call `first` after `second` */ def combine[F[_]: Apply](first: Logging[F], second: Logging[F]): Logging[F] = - loggingRepresentable.map2K(first, second)(makeFunctionK(t => t.first *> t.second)) + first.zipWithK(second)(Function2K[F, F, F](_ *> _)) private[logging] def loggerForService[S](implicit ct: ClassTag[S]): Logger = LoggerFactory.getLogger(ct.runtimeClass) diff --git a/logging/structured/src/main/scala/tofu/logging/Logs.scala b/logging/structured/src/main/scala/tofu/logging/Logs.scala index b9ba0ccec..62dad24c5 100644 --- a/logging/structured/src/main/scala/tofu/logging/Logs.scala +++ b/logging/structured/src/main/scala/tofu/logging/Logs.scala @@ -12,7 +12,7 @@ import tofu.syntax.monadic._ import scala.reflect.ClassTag -trait Logs[I[_], F[_]] extends LogsVOps[I, F] { +trait Logs[+I[_], F[_]] extends LogsVOps[I, F] { def forService[Svc: ClassTag]: I[Logging[F]] def byName(name: String): I[Logging[F]] @@ -53,7 +53,7 @@ object Logs { def byName(name: String): I[Logging[F]] = logging.pure[I] } - def empty[I[_]: Applicative, F[_]: Applicative]: Logs[I, F] = const(Logging.empty[F]) + def empty[I[_]: Applicative, F[_]: Applicative]: Logs[I, F] = const[I, F](Logging.empty[F]) def combine[I[_]: Apply, F[_]: Apply](las: Logs[I, F], lbs: Logs[I, F]): Logs[I, F] = new Logs[I, F] { def forService[Svc: ClassTag]: I[Logging[F]] = las.forService.map2(lbs.forService)(Logging.combine[F]) @@ -61,7 +61,7 @@ object Logs { } implicit def logsMonoid[I[_]: Applicative, F[_]: Applicative]: Monoid[Logs[I, F]] = new Monoid[Logs[I, F]] { - def empty: Logs[I, F] = Logs.empty + def empty: Logs[I, F] = Logs.empty[I, F] def combine(x: Logs[I, F], y: Logs[I, F]): Logs[I, F] = Logs.combine(x, y) } diff --git a/logging/util/src/main/scala/tofu/logging/atom/AtomLogger.scala b/logging/util/src/main/scala/tofu/logging/atom/AtomLogger.scala new file mode 100644 index 000000000..e45cf373f --- /dev/null +++ b/logging/util/src/main/scala/tofu/logging/atom/AtomLogger.scala @@ -0,0 +1,43 @@ +package tofu.logging.atom +import java.time.Instant +import java.util.concurrent.TimeUnit + +import cats.effect.Clock +import cats.{Applicative, FlatMap} +import tofu.concurrent.Atom +import tofu.higherKind.Embed +import tofu.logging.{LoggedValue, Logging, Logs} +import tofu.syntax.monadic._ + +import scala.reflect.{ClassTag, classTag} + +final case class LogLine( + loggerName: String, + level: Logging.Level, + message: String, + timestamp: Instant, + values: Vector[LoggedValue], +) + +class AtomLogging[F[_]: FlatMap: Clock](log: Atom[F, Vector[LogLine]], name: String) extends Logging[F] { + override def write(level: Logging.Level, message: String, values: LoggedValue*): F[Unit] = + Clock[F].realTime(TimeUnit.MILLISECONDS).flatMap { time => + log.update( + _ :+ LogLine( + loggerName = name, + level = level, + message = message, + timestamp = Instant.ofEpochMilli(time), + values = values.toVector + ) + ) + } + +} + +final case class AtomLogs[I[_]: Applicative, F[_]: FlatMap: Clock](flog: F[Atom[F, Vector[LogLine]]]) + extends Logs[I, F] { + def forService[Svc: ClassTag]: I[Logging[F]] = byName(classTag[Svc].runtimeClass.getName) + def byName(name: String): I[Logging[F]] = + Embed.of(flog.map[Logging[F]](new AtomLogging[F](_, name))).pure[I] +}