Skip to content

Commit 69b93b7

Browse files
author
Odomontois
authored
Merge pull request #181 from TinkoffCreditSystems/universal-logs
Create caching and embedding universal log implementation for ease of use
2 parents 98ae376 + f73eee0 commit 69b93b7

File tree

9 files changed

+111
-61
lines changed

9 files changed

+111
-61
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ lazy val loggingStr = project
8282
),
8383
macros
8484
)
85-
.dependsOn(core, data)
85+
.dependsOn(core, concurrent, data)
8686

8787
lazy val loggingDer = project
8888
.in(file("logging/derivation"))

core/src/main/scala/tofu/Init.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package tofu
2+
3+
/**
4+
* Initialize value of type A in type F
5+
*
6+
* @tparam F initialization effect type
7+
* @tparam A result type
8+
*/
9+
trait Init[F[_], A] {
10+
def init: F[A]
11+
}

derivation/src/main/scala/tofu/data/derived/Init.scala renamed to derivation/src/main/scala/tofu/data/derived/InitDerivation.scala

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
11
package tofu.data.derived
22
import cats.Monad
3+
import derevo.Derivation
34
import magnolia.{CaseClass, Magnolia}
45
import mercator.Monadic
6+
import tofu.Init
57

6-
/**
7-
* Initialize value of type A in type F
8-
*
9-
* @tparam F initialization effect type
10-
* @tparam A result type
11-
*/
12-
trait Init[F[_], A] {
13-
def init: F[A]
14-
}
15-
16-
class InitDerivation[F[_]: Monad] {
8+
class InitDerivation[F[_]: Monad] extends Derivation[Init[F, *]] {
179
private[this] implicit val magnoliaMonad: Monadic[F] = new MerkatorFromCats[F]
1810

1911
type Typeclass[A] = Init[F, A]

logging/structured/src/main/scala/tofu/logging/Logging.scala

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import cats.syntax.apply._
66
import cats.{Applicative, Apply, FlatMap}
77
import com.github.ghik.silencer.silent
88
import org.slf4j.{Logger, LoggerFactory, Marker}
9-
import tofu.higherKind
10-
import tofu.higherKind.{Embed, Function2K, RepresentableK}
9+
import tofu.{Init, higherKind}
10+
import tofu.higherKind.{Function2K, RepresentableK}
1111
import tofu.logging.impl.EmbedLogging
1212
import tofu.syntax.monoidalK._
1313

@@ -74,8 +74,16 @@ object ServiceLogging {
7474
private[this] val representableAny: RepresentableK[ServiceLogging[*[_], Any]] =
7575
higherKind.derived.genRepresentableK[ServiceLogging[*[_], Any]]
7676

77+
implicit def initByLogs[I[_], F[_], Svc: ClassTag](implicit logs: Logs[I, F]): Init[I, ServiceLogging[F, Svc]] =
78+
new Init[I, ServiceLogging[F, Svc]] {
79+
def init: I[ServiceLogging[F, Svc]] = logs.service[Svc]
80+
}
81+
7782
final implicit def serviceLoggingRepresentable[Svc]: RepresentableK[ServiceLogging[*[_], Svc]] =
7883
representableAny.asInstanceOf[RepresentableK[ServiceLogging[*[_], Svc]]]
84+
85+
final implicit def byUniversal[F[_], Svc: ClassTag](implicit unilogs: Logs.Universal[F]): ServiceLogging[F, Svc] =
86+
unilogs.service[Svc]
7987
}
8088

8189
/** typeclass for logging using specified logger or set of loggers
@@ -122,3 +130,7 @@ private[tofu] class EmptyLogging[F[_]: Applicative] extends Logging[F] {
122130
private[this] val noop = Applicative[F].unit
123131
def write(level: Level, message: String, values: LoggedValue*): F[Unit] = noop
124132
}
133+
134+
trait LoggingCompanion[U[_[_]]] {
135+
type Log[F[_]] = ServiceLogging[F, U[Any]]
136+
}

logging/structured/src/main/scala/tofu/logging/Logs.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,17 @@ import Logging.loggerForService
44
import cats.data.Tuple2K
55
import cats.effect.Sync
66
import cats.kernel.Monoid
7+
import cats.{Applicative, Apply, FlatMap, Functor, Id, Monad}
8+
import impl.{CachedLogs, ContextSyncLoggingImpl, SyncLogging, UniversalEmbedLogs}
79
import cats.tagless.{ApplyK, FunctorK}
810
import cats.tagless.syntax.functorK._
911
import cats.{Applicative, Apply, FlatMap, Functor, ~>}
1012
import impl.{ContextSyncLoggingImpl, SyncLogging}
1113
import org.slf4j.LoggerFactory
14+
import tofu.concurrent.QVars
15+
import tofu.{Guarantee, higherKind}
16+
import tofu.higherKind.RepresentableK
17+
import tofu.lift.Lift
1218
import tofu.higherKind
1319
import tofu.higherKind.{Function2K, MonoidalK, Point, RepresentableK}
1420
import tofu.syntax.monadic._
@@ -23,9 +29,12 @@ trait Logs[+I[_], F[_]] extends LogsVOps[I, F] {
2329
final def biwiden[I1[a] >: I[a], F1[a] >: F[a]]: Logs[I1, F1] = this.asInstanceOf[Logs[I1, F1]]
2430

2531
final def service[Svc: ClassTag]: I[ServiceLogging[F, Svc]] = forService[Svc].asInstanceOf[I[ServiceLogging[F, Svc]]]
32+
2633
}
2734

2835
object Logs extends LogsInstances0 {
36+
type Universal[F[_]] = Logs[Id, F]
37+
2938
def apply[I[_], F[_]](implicit logs: Logs[I, F]): Logs[I, F] = logs
3039

3140
private[this] val logs1RepresentableAny: RepresentableK[Logs[*[_], Any]] =
@@ -79,6 +88,25 @@ object Logs extends LogsInstances0 {
7988
def apply[X: ClassTag](f: Logging[F] => I[X])(implicit logs: Logs[I, F], I: FlatMap[I]) =
8089
logs.forService[X].flatMap(f)
8190
}
91+
92+
final implicit class LogsOps[I[_], F[_]](private val logs: Logs[I, F]) extends AnyVal {
93+
def cached(implicit IM: Monad[I], IQ: QVars[I], IG: Guarantee[I]): I[Logs[I, F]] =
94+
QVars[I]
95+
.of(Map.empty[String, Logging[F]])
96+
.map2(
97+
QVars[I].of(Map.empty[ClassTag[_], Logging[F]])
98+
)(new CachedLogs[I, F](logs, _, _))
99+
100+
def universal(implicit il: Lift[I, F], F: FlatMap[F]): Universal[F] = new UniversalEmbedLogs(logs)
101+
102+
def cachedUniversal(
103+
implicit IM: Monad[I],
104+
IQ: QVars[I],
105+
IG: Guarantee[I],
106+
il: Lift[I, F],
107+
F: FlatMap[F]
108+
): I[Universal[F]] = cached.map(_.universal)
109+
}
82110
}
83111

84112
private[logging] trait LogsInstances0 extends LogsInstances1 {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package tofu.logging.impl
2+
import cats.Monad
3+
import tofu.Guarantee
4+
import tofu.concurrent.QVar
5+
import tofu.logging.{LoggedValue, Logging, Logs}
6+
import tofu.syntax.bracket._
7+
import tofu.syntax.monadic._
8+
9+
import scala.reflect.{ClassTag, classTag}
10+
11+
class CachedLogs[I[_]: Monad: Guarantee, F[_]](
12+
underlying: Logs[I, F],
13+
nameCache: QVar[I, Map[String, Logging[F]]],
14+
tagCache: QVar[I, Map[ClassTag[_], Logging[F]]]
15+
) extends Logs[I, F] {
16+
private[this] case object NoneLogging extends Logging[F] {
17+
def write(level: Logging.Level, message: String, values: LoggedValue*): F[Unit] =
18+
throw new UnsupportedOperationException("CachedLogs.NonLogging should be never used")
19+
}
20+
21+
private[this] def safeGet[K](mvar: QVar[I, Map[K, Logging[F]]], create: => I[Logging[F]], key: K): I[Logging[F]] =
22+
mvar.read.flatMap(_.getOrElse(key, NoneLogging) match {
23+
case NoneLogging =>
24+
mvar.take.bracketIncomplete { map =>
25+
map.getOrElse(key, NoneLogging) match {
26+
case NoneLogging => create.flatTap(logging => mvar.put(map.updated(key, logging)))
27+
case logging => logging.pure[I]
28+
}
29+
}(mvar.put)
30+
case logging => logging.pure[I]
31+
})
32+
33+
def forService[Svc: ClassTag]: I[Logging[F]] = safeGet(tagCache, underlying.forService[Svc], classTag[Svc])
34+
def byName(name: String): I[Logging[F]] = safeGet(nameCache, underlying.byName(name), name)
35+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package tofu.logging.impl
2+
import cats.{FlatMap, Id}
3+
import tofu.lift.Lift
4+
import tofu.logging.{Logging, Logs}
5+
6+
import scala.reflect.ClassTag
7+
8+
class UniversalEmbedLogs[I[_], F[_]: FlatMap](underlying: Logs[I, F])(implicit lift: Lift[I, F])
9+
extends Logs.Universal[F] {
10+
def forService[Svc: ClassTag]: Logging[F] =
11+
Logging.loggingRepresentable.embed(lift.lift(underlying.forService[Svc]))
12+
def byName(name: String): Id[Logging[F]] =
13+
Logging.loggingRepresentable.embed(lift.lift(underlying.byName(name)))
14+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package tofu
2+
3+
package object logging {
4+
type ModuleLog[F[_], U[_[_]]] = ServiceLogging[F, U[Any]]
5+
}

website/i18n/en.json

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)