Adopt to new loggin api

This commit is contained in:
eikek
2022-02-19 14:00:47 +01:00
parent 6442771270
commit e483a97de7
130 changed files with 634 additions and 662 deletions

View File

@ -0,0 +1,39 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.logging
import cats.data.NonEmptyList
import cats.syntax.all._
import cats.{Applicative, Id}
final private[logging] class AndThenLogger[F[_]: Applicative](
val loggers: NonEmptyList[Logger[F]]
) extends Logger[F] {
def log(ev: LogEvent): F[Unit] =
loggers.traverse(_.log(ev)).as(())
def asUnsafe: Logger[Id] =
new Logger[Id] { self =>
def log(ev: LogEvent): Unit =
loggers.toList.foreach(_.asUnsafe.log(ev))
def asUnsafe = self
}
}
private[logging] object AndThenLogger {
def combine[F[_]: Applicative](a: Logger[F], b: Logger[F]): Logger[F] =
(a, b) match {
case (aa: AndThenLogger[F], bb: AndThenLogger[F]) =>
new AndThenLogger[F](aa.loggers ++ bb.loggers.toList)
case (aa: AndThenLogger[F], _) =>
new AndThenLogger[F](aa.loggers.prepend(b))
case (_, bb: AndThenLogger[F]) =>
new AndThenLogger[F](bb.loggers.prepend(a))
case _ =>
new AndThenLogger[F](NonEmptyList.of(a, b))
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.logging
import cats.Order
import cats.data.NonEmptyList
import io.circe.{Decoder, Encoder}
sealed trait Level { self: Product =>
val name: String =
productPrefix.toUpperCase
val value: Double
}
object Level {
case object Fatal extends Level {
val value = 600.0
}
case object Error extends Level {
val value = 500.0
}
case object Warn extends Level {
val value = 400.0
}
case object Info extends Level {
val value = 300.0
}
case object Debug extends Level {
val value = 200.0
}
case object Trace extends Level {
val value = 100.0
}
val all: NonEmptyList[Level] =
NonEmptyList.of(Fatal, Error, Warn, Info, Debug, Trace)
def fromString(str: String): Either[String, Level] = {
val s = str.toUpperCase
all.find(_.name == s).toRight(s"Invalid level name: $str")
}
implicit val order: Order[Level] =
Order.by(_.value)
implicit val jsonEncoder: Encoder[Level] =
Encoder.encodeString.contramap(_.name)
implicit val jsonDecoder: Decoder[Level] =
Decoder.decodeString.emap(fromString)
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.logging
import cats.data.NonEmptyList
import io.circe.{Decoder, Encoder}
final case class LogConfig(minimumLevel: Level, format: LogConfig.Format) {}
object LogConfig {
sealed trait Format { self: Product =>
def name: String =
productPrefix.toLowerCase
}
object Format {
case object Plain extends Format
case object Fancy extends Format
case object Json extends Format
case object Logfmt extends Format
val all: NonEmptyList[Format] =
NonEmptyList.of(Plain, Fancy, Json, Logfmt)
def fromString(str: String): Either[String, Format] =
all.find(_.name.equalsIgnoreCase(str)).toRight(s"Invalid format name: $str")
implicit val jsonDecoder: Decoder[Format] =
Decoder.decodeString.emap(fromString)
implicit val jsonEncoder: Encoder[Format] =
Encoder.encodeString.contramap(_.name)
}
implicit val jsonDecoder: Decoder[LogConfig] =
Decoder.forProduct2("minimumLevel", "format")(LogConfig.apply)
implicit val jsonEncoder: Encoder[LogConfig] =
Encoder.forProduct2("minimumLevel", "format")(r => (r.minimumLevel, r.format))
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.logging
import io.circe.{Encoder, Json}
import sourcecode._
final case class LogEvent(
level: Level,
msg: () => String,
additional: List[() => LogEvent.AdditionalMsg],
data: Map[String, () => Json],
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
) {
def asString =
s"${level.name} ${name.value}/${fileName}:${line.value} - ${msg()}"
def data[A: Encoder](key: String, value: => A): LogEvent =
copy(data = data.updated(key, () => Encoder[A].apply(value)))
def addMessage(msg: => String): LogEvent =
copy(additional = (() => Left(msg)) :: additional)
def addError(ex: Throwable): LogEvent =
copy(additional = (() => Right(ex)) :: additional)
def findErrors: List[Throwable] =
additional.map(a => a()).collect { case Right(ex) =>
ex
}
}
object LogEvent {
type AdditionalMsg = Either[String, Throwable]
def of(l: Level, m: => String)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): LogEvent = LogEvent(l, () => m, Nil, Map.empty, pkg, fileName, name, line)
}

View File

@ -0,0 +1,166 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.logging
import java.io.PrintStream
import java.time.Instant
import cats.effect.{Ref, Sync}
import cats.syntax.applicative._
import cats.syntax.functor._
import cats.syntax.order._
import cats.{Applicative, Id}
import sourcecode._
trait Logger[F[_]] extends LoggerExtension[F] {
def log(ev: LogEvent): F[Unit]
def asUnsafe: Logger[Id]
def trace(msg: => String)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(LogEvent.of(Level.Trace, msg))
def traceWith(msg: => String)(modify: LogEvent => LogEvent)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(modify(LogEvent.of(Level.Trace, msg)))
def debug(msg: => String)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(LogEvent.of(Level.Debug, msg))
def debugWith(msg: => String)(modify: LogEvent => LogEvent)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(modify(LogEvent.of(Level.Debug, msg)))
def info(msg: => String)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(LogEvent.of(Level.Info, msg))
def infoWith(msg: => String)(modify: LogEvent => LogEvent)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(modify(LogEvent.of(Level.Info, msg)))
def warn(msg: => String)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(LogEvent.of(Level.Warn, msg))
def warnWith(msg: => String)(modify: LogEvent => LogEvent)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(modify(LogEvent.of(Level.Warn, msg)))
def warn(ex: Throwable)(msg: => String)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(LogEvent.of(Level.Warn, msg).addError(ex))
def error(msg: => String)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(LogEvent.of(Level.Error, msg))
def errorWith(msg: => String)(modify: LogEvent => LogEvent)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(modify(LogEvent.of(Level.Error, msg)))
def error(ex: Throwable)(msg: => String)(implicit
pkg: Pkg,
fileName: FileName,
name: Name,
line: Line
): F[Unit] =
log(LogEvent.of(Level.Error, msg).addError(ex))
}
object Logger {
def off: Logger[Id] =
new Logger[Id] {
def log(ev: LogEvent): Unit = ()
def asUnsafe = this
}
def offF[F[_]: Applicative]: Logger[F] =
new Logger[F] {
def log(ev: LogEvent) = ().pure[F]
def asUnsafe = off
}
def buffer[F[_]: Sync](): F[(Ref[F, Vector[LogEvent]], Logger[F])] =
for {
buffer <- Ref.of[F, Vector[LogEvent]](Vector.empty[LogEvent])
logger =
new Logger[F] {
def log(ev: LogEvent) =
buffer.update(_.appended(ev))
def asUnsafe = off
}
} yield (buffer, logger)
/** Just prints to the given print stream. Useful for testing. */
def simple(ps: PrintStream, minimumLevel: Level): Logger[Id] =
new Logger[Id] {
def log(ev: LogEvent): Unit =
if (ev.level >= minimumLevel)
ps.println(s"${Instant.now()} [${Thread.currentThread()}] ${ev.asString}")
else
()
def asUnsafe = this
}
def simpleF[F[_]: Sync](ps: PrintStream, minimumLevel: Level): Logger[F] =
new Logger[F] {
def log(ev: LogEvent) =
Sync[F].delay(asUnsafe.log(ev))
val asUnsafe = simple(ps, minimumLevel)
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.logging
import cats.Applicative
import fs2.Stream
trait LoggerExtension[F[_]] { self: Logger[F] =>
def stream: Logger[Stream[F, *]] =
new Logger[Stream[F, *]] {
def log(ev: LogEvent) =
Stream.eval(self.log(ev))
def asUnsafe = self.asUnsafe
}
def andThen(other: Logger[F])(implicit F: Applicative[F]): Logger[F] =
AndThenLogger.combine(self, other)
def >>(other: Logger[F])(implicit F: Applicative[F]): Logger[F] =
AndThenLogger.combine(self, other)
}