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

Improve API around transforming loggers #854

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
29 changes: 13 additions & 16 deletions core/shared/src/main/scala/org/typelevel/log4cats/ErrorLogger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@

package org.typelevel.log4cats

import cats._
import cats.*
import org.typelevel.log4cats.extras.LogLevel
trait ErrorLogger[F[_]] {
def error(t: Throwable)(message: => String): F[Unit]
def warn(t: Throwable)(message: => String): F[Unit]
def info(t: Throwable)(message: => String): F[Unit]
def debug(t: Throwable)(message: => String): F[Unit]
def trace(t: Throwable)(message: => String): F[Unit]
def error(t: Throwable)(message: => String): F[Unit] = log(LogLevel.Error, t, message)
def warn(t: Throwable)(message: => String): F[Unit] = log(LogLevel.Warn, t, message)
def info(t: Throwable)(message: => String): F[Unit] = log(LogLevel.Info, t, message)
def debug(t: Throwable)(message: => String): F[Unit] = log(LogLevel.Debug, t, message)
def trace(t: Throwable)(message: => String): F[Unit] = log(LogLevel.Trace, t, message)

def log(ll: LogLevel, t: Throwable, msg: => String): F[Unit]

def mapK[G[_]](fk: F ~> G): ErrorLogger[G] =
ErrorLogger.mapK(fk)(this)
}
Expand All @@ -32,16 +36,9 @@ object ErrorLogger {

private def mapK[G[_], F[_]](f: G ~> F)(logger: ErrorLogger[G]): ErrorLogger[F] =
new ErrorLogger[F] {
def error(t: Throwable)(message: => String): F[Unit] =
f(logger.error(t)(message))
def warn(t: Throwable)(message: => String): F[Unit] =
f(logger.warn(t)(message))
def info(t: Throwable)(message: => String): F[Unit] =
f(logger.info(t)(message))
def debug(t: Throwable)(message: => String): F[Unit] =
f(logger.debug(t)(message))
def trace(t: Throwable)(message: => String): F[Unit] =
f(logger.trace(t)(message))
override def log(ll: LogLevel, t: Throwable, msg: => String): F[Unit] = f(
logger.log(ll, t, msg)
)
}

}
41 changes: 9 additions & 32 deletions core/shared/src/main/scala/org/typelevel/log4cats/Logger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@

package org.typelevel.log4cats

import cats._
import cats.*
import cats.data.{EitherT, Kleisli, OptionT}
import org.typelevel.log4cats.extras.LogLevel

trait Logger[F[_]] extends MessageLogger[F] with ErrorLogger[F] {
def withModifiedString(f: String => String): Logger[F] = Logger.withModifiedString[F](this, f)
override def mapK[G[_]](fk: F ~> G): Logger[G] = Logger.mapK(fk)(this)
}

object Logger {
def apply[F[_]](implicit ev: Logger[F]) = ev
def apply[F[_]](implicit ev: Logger[F]): Logger[F] = ev

implicit def optionTLogger[F[_]: Logger: Functor]: Logger[OptionT[F, *]] =
Logger[F].mapK(OptionT.liftK[F])
Expand All @@ -38,40 +39,16 @@ object Logger {

private def withModifiedString[F[_]](l: Logger[F], f: String => String): Logger[F] =
new Logger[F] {
def error(message: => String): F[Unit] = l.error(f(message))
def error(t: Throwable)(message: => String): F[Unit] = l.error(t)(f(message))
def warn(message: => String): F[Unit] = l.warn(f(message))
def warn(t: Throwable)(message: => String): F[Unit] = l.warn(t)(f(message))
def info(message: => String): F[Unit] = l.info(f(message))
def info(t: Throwable)(message: => String): F[Unit] = l.info(t)(f(message))
def debug(message: => String): F[Unit] = l.debug(f(message))
def debug(t: Throwable)(message: => String): F[Unit] = l.debug(t)(f(message))
def trace(message: => String): F[Unit] = l.trace(f(message))
def trace(t: Throwable)(message: => String): F[Unit] = l.trace(t)(f(message))
override def log(ll: LogLevel, t: Throwable, msg: => String): F[Unit] = l.log(ll, t, f(msg))
override def log(ll: LogLevel, msg: => String): F[Unit] = l.log(ll, f(msg))
}

private def mapK[G[_], F[_]](f: G ~> F)(logger: Logger[G]): Logger[F] =
new Logger[F] {
def error(t: Throwable)(message: => String): F[Unit] =
f(logger.error(t)(message))
def warn(t: Throwable)(message: => String): F[Unit] =
f(logger.warn(t)(message))
def info(t: Throwable)(message: => String): F[Unit] =
f(logger.info(t)(message))
def debug(t: Throwable)(message: => String): F[Unit] =
f(logger.debug(t)(message))
def trace(t: Throwable)(message: => String): F[Unit] =
f(logger.trace(t)(message))
def error(message: => String): F[Unit] =
f(logger.error(message))
def warn(message: => String): F[Unit] =
f(logger.warn(message))
def info(message: => String): F[Unit] =
f(logger.info(message))
def debug(message: => String): F[Unit] =
f(logger.debug(message))
def trace(message: => String): F[Unit] =
f(logger.trace(message))
override def log(ll: LogLevel, t: Throwable, msg: => String): F[Unit] = f(
logger.log(ll, t, msg)
)
override def log(ll: LogLevel, msg: => String): F[Unit] = f(logger.log(ll, msg))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@

package org.typelevel.log4cats

import cats._
import cats.*
import org.typelevel.log4cats.extras.LogLevel

trait MessageLogger[F[_]] {
def error(message: => String): F[Unit]
def warn(message: => String): F[Unit]
def info(message: => String): F[Unit]
def debug(message: => String): F[Unit]
def trace(message: => String): F[Unit]
def error(message: => String): F[Unit] = log(LogLevel.Error, message)
def warn(message: => String): F[Unit] = log(LogLevel.Warn, message)
def info(message: => String): F[Unit] = log(LogLevel.Info, message)
def debug(message: => String): F[Unit] = log(LogLevel.Debug, message)
def trace(message: => String): F[Unit] = log(LogLevel.Trace, message)

def log(ll: LogLevel, msg: => String): F[Unit]

def mapK[G[_]](fk: F ~> G): MessageLogger[G] =
MessageLogger.mapK(fk)(this)
}
Expand All @@ -33,15 +37,6 @@ object MessageLogger {

private def mapK[G[_], F[_]](f: G ~> F)(logger: MessageLogger[G]): MessageLogger[F] =
new MessageLogger[F] {
def error(message: => String): F[Unit] =
f(logger.error(message))
def warn(message: => String): F[Unit] =
f(logger.warn(message))
def info(message: => String): F[Unit] =
f(logger.info(message))
def debug(message: => String): F[Unit] =
f(logger.debug(message))
def trace(message: => String): F[Unit] =
f(logger.trace(message))
override def log(ll: LogLevel, msg: => String): F[Unit] = f(logger.log(ll, msg))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@

package org.typelevel.log4cats

import cats._
import cats.*
import cats.effect.std.UUIDGen
import cats.syntax.all._
import cats.syntax.all.*
import org.typelevel.log4cats.extras.LogLevel

import java.io.{PrintWriter, StringWriter}
import java.util.UUID
Expand Down Expand Up @@ -87,15 +88,14 @@ object PagingSelfAwareStructuredLogger {
private val pageSize = pageSizeK * 1024

private def pagedLogging(
logOpWithCtx: Map[String, String] => (=> String) => F[Unit],
logLevel: LogLevel,
ctx: Map[String, String],
logSplitId: String,
msg: String
): F[Unit] = {
val numOfPagesRaw = (msg.length - 1) / pageSize + 1
val numOfPages = Math.min(numOfPagesRaw, maxPageNeeded)
if (numOfPages <= 1)
logOpWithCtx(addPageCtx(msg, 1, 1, ctx))(msg)
if (numOfPages <= 1) sl.log(logLevel, addPageCtx(msg, 1, 1, ctx), msg)
else {
val logSplitIdPart1 = logSplitId.split('-').head
val pageHeaderTail = s"$numOfPages $logSplitIdPart1"
Expand All @@ -112,24 +112,21 @@ object PagingSelfAwareStructuredLogger {
|
|Page $pi/$pageFooterTail""".stripMargin

logOpWithCtx(addPageCtx(page, pi, numOfPages, ctx))(page)
sl.log(logLevel, addPageCtx(page, pi, numOfPages, ctx), page)
}
}
}

private def addMsgCtx(
msg: String,
ctx: Map[String, String]
): F[(String, Map[String, String])] =
private def addMsgCtx(msg: String, ctx: Map[String, String]): F[(String, Map[String, String])] =
randomUUID.map { uuid =>
val logSplitId = uuid.show
val msgLength = msg.length
val msgLength = s"${msg.length}"
(
logSplitId,
ctx
.updated(logSplitIdN, logSplitId)
.updated("page_size", s"$pageSizeK Kib")
.updated("whole_message_size_bytes", s"$msgLength")
.updated("whole_message_size_bytes", msgLength)
// The following is deprecated
.updated("log_size", s"$msgLength Byte")
)
Expand All @@ -146,38 +143,24 @@ object PagingSelfAwareStructuredLogger {
.updated("page_num", s"$pageNum")
.updated("log_size_bytes", s"${page.length}")

private def doLogging(
loggingLevelChk: => F[Boolean],
logOpWithCtx: Map[String, String] => (=> String) => F[Unit],
msg: => String,
ctx: Map[String, String] = Map()
): F[Unit] = {
loggingLevelChk.ifM(
{
// At this point we know we're going to log and/or interact
// with msg, so we materialize the message here so we don't
// materialize it multiple times
val materializedMsg = msg
addMsgCtx(materializedMsg, ctx).flatMap { case (logSplitId, newCtx) =>
pagedLogging(logOpWithCtx, newCtx, logSplitId, materializedMsg)
}
},
Applicative[F].unit
)
}
private def doLogging(logLevel: LogLevel, ctx: Map[String, String], msg: => String): F[Unit] =
sl.isEnabled(logLevel)
.ifM(
{
val cachedMsg = msg
addMsgCtx(cachedMsg, ctx).flatMap { case (logSplitId, newCtx) =>
pagedLogging(logLevel, newCtx, logSplitId, cachedMsg)
}
},
Applicative[F].unit
)

private def doLoggingThrowable(
loggingLevelChk: => F[Boolean],
logOpWithCtx: Map[String, String] => (=> String) => F[Unit],
logLevel: LogLevel,
ctx: Map[String, String],
t: Throwable,
msg: => String,
ctx: Map[String, String] = Map()
): F[Unit] = {
loggingLevelChk.ifM(
doLogging(loggingLevelChk, logOpWithCtx, s"$msg\n${getStackTrace(t)}", ctx),
Applicative[F].unit
)
}
msg: => String
): F[Unit] = doLogging(logLevel, ctx, s"$msg\n${getStackTrace(t)}")

def getStackTrace(t: Throwable): String = {
val sw = new StringWriter()
Expand All @@ -186,82 +169,23 @@ object PagingSelfAwareStructuredLogger {
sw.getBuffer.toString
}

override def isTraceEnabled: F[Boolean] = sl.isTraceEnabled

override def isDebugEnabled: F[Boolean] = sl.isDebugEnabled

override def isInfoEnabled: F[Boolean] = sl.isInfoEnabled

override def isWarnEnabled: F[Boolean] = sl.isWarnEnabled

override def isErrorEnabled: F[Boolean] = sl.isErrorEnabled

// Log message

override def trace(msg: => String): F[Unit] =
doLogging(isTraceEnabled, sl.trace, msg)

override def debug(msg: => String): F[Unit] =
doLogging(isDebugEnabled, sl.debug, msg)

override def info(msg: => String): F[Unit] =
doLogging(isInfoEnabled, sl.info, msg)
override def isEnabled(ll: LogLevel): F[Boolean] = sl.isEnabled(ll)

override def warn(msg: => String): F[Unit] =
doLogging(isWarnEnabled, sl.warn, msg)
override def log(ll: LogLevel, msg: => String): F[Unit] =
doLogging(ll, Map.empty, msg)

override def error(msg: => String): F[Unit] =
doLogging(isErrorEnabled, sl.error, msg)
override def log(ll: LogLevel, t: Throwable, msg: => String): F[Unit] =
doLoggingThrowable(ll, Map.empty, t, msg)

// Log message and throwable
override def log(ll: LogLevel, ctx: Map[String, String], msg: => String): F[Unit] =
doLogging(ll, ctx, msg)

override def trace(t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isTraceEnabled, sl.trace, t, msg)

override def debug(t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isDebugEnabled, sl.debug, t, msg)

override def info(t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isInfoEnabled, sl.info, t, msg)

override def warn(t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isWarnEnabled, sl.warn, t, msg)

override def error(t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isErrorEnabled, sl.error, t, msg)

// Log message, passing context

override def trace(ctx: Map[String, String])(msg: => String): F[Unit] =
doLogging(isTraceEnabled, sl.trace, msg, ctx)

override def debug(ctx: Map[String, String])(msg: => String): F[Unit] =
doLogging(isDebugEnabled, sl.debug, msg, ctx)

override def info(ctx: Map[String, String])(msg: => String): F[Unit] =
doLogging(isInfoEnabled, sl.info, msg, ctx)

override def warn(ctx: Map[String, String])(msg: => String): F[Unit] =
doLogging(isWarnEnabled, sl.warn, msg, ctx)

override def error(ctx: Map[String, String])(msg: => String): F[Unit] =
doLogging(isErrorEnabled, sl.error, msg, ctx)

// Log message and throwable, passing context

override def trace(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isTraceEnabled, sl.trace, t, msg, ctx)

override def debug(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isDebugEnabled, sl.debug, t, msg, ctx)

override def info(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isInfoEnabled, sl.info, t, msg, ctx)

override def warn(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isWarnEnabled, sl.warn, t, msg, ctx)

override def error(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] =
doLoggingThrowable(isErrorEnabled, sl.error, t, msg, ctx)
override def log(
ll: LogLevel,
ctx: Map[String, String],
t: Throwable,
msg: => String
): F[Unit] =
doLoggingThrowable(ll, ctx, t, msg)
}
}
Loading
Loading