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

Helpers for creating simple/stub versions of SelfAwareStructuredLogger and LoggerFactory #682

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ trait ErrorLogger[F[_]] {
}

object ErrorLogger {
trait Fallback[F[_]] extends ErrorLogger[F] { _: MessageLogger[F] =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a hard time understanding what the purpose of Fallback[F[_]] is.

Is it a convenience mixin that ignores any Throwables in the signatures of ErrorLogger[F[_]]?

Is it intended for end-users?

Seeing how it's used here I'd rather have it just be an implementation detail of the SelfAwareStructuredLogger.liftF because otherwise it increases the API surface area of the library quite a lot 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I feel like it's a useful thing to have available but I don't have a very strong, specific answer to your question so yes, probably best to leave it as an implementation detail for now at least.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer making all the helper traits private[log4cats] (or whatever is best for bincompat, I am really bad at this part 😬). We can always make them public later with little effort and in a minor version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private[log4cats] doesn't really protect bincompat, but it protects source compat, and we can grant a MiMa waiver if we're sure nothing else used it.

In multi-repo projects like http4s, it's unsafe to waive away package privates, because usages could be in another repo. Here, it's probably safe: nothing other than this project should be in the log4cats namespace.

Either way, I'd agree locking it down is safer for libraries this low in the hierarchy.

final def error(t: Throwable)(message: => String): F[Unit] = error(message)
final def warn(t: Throwable)(message: => String): F[Unit] = warn(message)
final def info(t: Throwable)(message: => String): F[Unit] = info(message)
final def debug(t: Throwable)(message: => String): F[Unit] = debug(message)
final def trace(t: Throwable)(message: => String): F[Unit] = trace(message)
Comment on lines +32 to +36
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't decide whether these should be final, and in fact wasn't consistent within this PR. Any thoughts?

Copy link
Member

@armanbilge armanbilge Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's much reason to make these final. These are just convenient implementation mix-ins, there shouldn't be libraries out in the wild that specifically ask for a Fallback and expect it to work a certain way...

}

def apply[F[_]](implicit ev: ErrorLogger[F]): ErrorLogger[F] = ev

private def mapK[G[_], F[_]](f: G ~> F)(logger: ErrorLogger[G]): ErrorLogger[F] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@

package org.typelevel.log4cats

import cats.Functor
import cats.{~>, Applicative, Functor}
import cats.syntax.functor._
import cats.~>

import scala.annotation.implicitNotFound

Expand All @@ -36,6 +35,18 @@ trait LoggerFactory[F[_]] extends LoggerFactoryGen[F] {
object LoggerFactory extends LoggerFactoryGenCompanion {
def apply[F[_]: LoggerFactory]: LoggerFactory[F] = implicitly

def const[F[_]](
logger: SelfAwareStructuredLogger[F]
)(implicit F: Applicative[F]): LoggerFactory[F] = new LoggerFactory[F] {
override def getLoggerFromName(name: String): SelfAwareStructuredLogger[F] = logger

override def fromName(name: String): F[SelfAwareStructuredLogger[F]] = F.pure(logger)
}

def liftF[F[_]: Applicative](f: String => F[Unit]): LoggerFactory[F] = const(
SelfAwareStructuredLogger.liftF(f)
)

private def mapK[F[_]: Functor, G[_]](fk: F ~> G)(lf: LoggerFactory[F]): LoggerFactory[G] =
new LoggerFactory[G] {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ trait SelfAwareLogger[F[_]] extends Logger[F] {
object SelfAwareLogger {
def apply[F[_]](implicit ev: SelfAwareLogger[F]): SelfAwareLogger[F] = ev

trait Stubbed[F[_]] extends SelfAwareLogger[F] {
protected def F: Applicative[F]
def isTraceEnabled: F[Boolean] = F.pure(true)
def isDebugEnabled: F[Boolean] = F.pure(true)
def isInfoEnabled: F[Boolean] = F.pure(true)
def isWarnEnabled: F[Boolean] = F.pure(true)
def isErrorEnabled: F[Boolean] = F.pure(true)
}

private def mapK[G[_], F[_]](f: G ~> F)(logger: SelfAwareLogger[G]): SelfAwareLogger[F] =
new SelfAwareLogger[F] {
def isTraceEnabled: F[Boolean] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,27 @@ trait SelfAwareStructuredLogger[F[_]] extends SelfAwareLogger[F] with Structured
}

object SelfAwareStructuredLogger {
trait Simple[F[_]]
extends SelfAwareStructuredLogger[F]
with StructuredLogger.Fallback[F]
with ErrorLogger.Fallback[F]
with SelfAwareLogger.Stubbed[F]

def liftF[F[_]: Applicative](f: String => F[Unit]): SelfAwareStructuredLogger[F] =
new SelfAwareStructuredLogger.Simple[F] {
override protected def F: Applicative[F] = implicitly

override def error(message: => String): F[Unit] = f(message)

override def warn(message: => String): F[Unit] = f(message)

override def info(message: => String): F[Unit] = f(message)

override def debug(message: => String): F[Unit] = f(message)

override def trace(message: => String): F[Unit] = f(message)
}

def apply[F[_]](implicit ev: SelfAwareStructuredLogger[F]): SelfAwareStructuredLogger[F] = ev

def withContext[F[_]](
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ trait StructuredLogger[F[_]] extends Logger[F] {
}

object StructuredLogger {
trait Fallback[F[_]] extends StructuredLogger[F] {
def trace(ctx: Map[String, String])(msg: => String): F[Unit] = trace(msg)
def trace(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] = trace(t)(msg)
def debug(ctx: Map[String, String])(msg: => String): F[Unit] = debug(msg)
def debug(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] = debug(t)(msg)
def info(ctx: Map[String, String])(msg: => String): F[Unit] = info(msg)
def info(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] = info(t)(msg)
def warn(ctx: Map[String, String])(msg: => String): F[Unit] = warn(msg)
def warn(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] = warn(t)(msg)
def error(ctx: Map[String, String])(msg: => String): F[Unit] = error(msg)
def error(ctx: Map[String, String], t: Throwable)(msg: => String): F[Unit] = error(t)(msg)
}

def apply[F[_]](implicit ev: StructuredLogger[F]): StructuredLogger[F] = ev

def withContext[F[_]](sl: StructuredLogger[F])(ctx: Map[String, String]): StructuredLogger[F] =
Expand Down