-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #719 from morgen-peschke/add-writerT-structured-lo…
…gger Add structured equivalents of Writer & WriterT loggers
- Loading branch information
Showing
9 changed files
with
531 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
core/shared/src/main/scala/org/typelevel/log4cats/extras/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
Important notes about `Writer*Logger`s | ||
====================================== | ||
|
||
The loggers provided here backed by `Writer` and `WriterT` come with some important caveats that | ||
you should be aware of before using. | ||
|
||
General Notes | ||
------------- | ||
|
||
> **Note** | ||
> These loggers tie their logs to the lifecycle of the return value, so they're generally only useful | ||
when the logs have a similar lifecycle. | ||
|
||
> **Warning** | ||
> These loggers should not be used in situations where the logs would be needed if an error occurs (including timeouts). | ||
Basically, they're a way to use `Writer` or `WriterT` with the `log4cats` API. No additional guarantees | ||
are provided. Annotating the happy path is one example of a good use-case for these loggers. | ||
|
||
Better alternatives are provided by the `testing` module: | ||
- If a `SelfAwareLogger` is needed for test code, consider | ||
`org.typelevel.log4cats.testing.TestingLogger` over `WriterLogger` | ||
|
||
- If a `SelfAwareStructuredLogger` is needed for test code, consider | ||
`org.typelevel.log4cats.testing.StructuredTestingLogger` over `WriterStructuredLogger` | ||
|
||
`WriterLogger` / `WriterStructureLogger` | ||
---------------------------------------- | ||
|
||
> **Warning** | ||
> Expect to lose logs if an exception occurs | ||
These are built using `Writer`, which does not directly interact with effects, so expect to do a | ||
non-trivial amount of plumbing if you're planning on using them. Otherwise, if the logs don't matter | ||
in the presence of errors in the context you're using them, they're fine. | ||
|
||
`WriterTLogger` / `WriterTStructuredLogger` | ||
------------------------------------------- | ||
|
||
These are built using `WriterT`, and are much easier to use with effects. Running the `WriterT` | ||
instance will yield a value of type `F[(G[LogMessage], A)]`. | ||
|
||
> **Warning** | ||
> Logged messages can be materialized if *and only if* `F succeeds` | ||
Unfortunately, because of the way that cancellation (and thus timeouts) is handled by | ||
`cats.effect.IO`, in practice `WriterT` isn't a great fit for anything which can timeout. | ||
|
51 changes: 51 additions & 0 deletions
51
core/shared/src/main/scala/org/typelevel/log4cats/extras/StructuredLogMessage.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.typelevel.log4cats.extras | ||
|
||
import cats.Show | ||
import cats.syntax.all._ | ||
import org.typelevel.log4cats.StructuredLogger | ||
|
||
final case class StructuredLogMessage( | ||
level: LogLevel, | ||
context: Map[String, String], | ||
throwableOpt: Option[Throwable], | ||
message: String | ||
) | ||
object StructuredLogMessage { | ||
def log[F[_]](sm: StructuredLogMessage, l: StructuredLogger[F]): F[Unit] = sm match { | ||
case StructuredLogMessage(LogLevel.Trace, ctx, Some(t), m) => l.trace(ctx, t)(m) | ||
case StructuredLogMessage(LogLevel.Trace, ctx, None, m) => l.trace(ctx)(m) | ||
|
||
case StructuredLogMessage(LogLevel.Debug, ctx, Some(t), m) => l.debug(ctx, t)(m) | ||
case StructuredLogMessage(LogLevel.Debug, ctx, None, m) => l.debug(ctx)(m) | ||
|
||
case StructuredLogMessage(LogLevel.Info, ctx, Some(t), m) => l.info(ctx, t)(m) | ||
case StructuredLogMessage(LogLevel.Info, ctx, None, m) => l.info(ctx)(m) | ||
|
||
case StructuredLogMessage(LogLevel.Warn, ctx, Some(t), m) => l.warn(ctx, t)(m) | ||
case StructuredLogMessage(LogLevel.Warn, ctx, None, m) => l.warn(ctx)(m) | ||
|
||
case StructuredLogMessage(LogLevel.Error, ctx, Some(t), m) => l.error(ctx, t)(m) | ||
case StructuredLogMessage(LogLevel.Error, ctx, None, m) => l.error(ctx)(m) | ||
} | ||
|
||
implicit val structuredLogMessageShow: Show[StructuredLogMessage] = | ||
Show.show[StructuredLogMessage] { l => | ||
show"StructuredLogMessage(${l.level},${l.context},${l.throwableOpt.map(_.getMessage)},${l.message})" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
core/shared/src/main/scala/org/typelevel/log4cats/extras/WriterStructuredLogger.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.typelevel.log4cats.extras | ||
|
||
import cats.data.Writer | ||
import cats.syntax.all._ | ||
import cats.{~>, Alternative, Applicative, Foldable, Id} | ||
import org.typelevel.log4cats.{SelfAwareStructuredLogger, StructuredLogger} | ||
|
||
/** | ||
* A `SelfAwareStructuredLogger` implemented using `cats.data.Writer`. | ||
* | ||
* >>> WARNING: READ BEFORE USAGE! <<< | ||
* https://github.com/typelevel/log4cats/blob/main/core/shared/src/main/scala/org/typelevel/log4cats/extras/README.md | ||
* >>> WARNING: READ BEFORE USAGE! <<< | ||
* | ||
* If a `SelfAwareStructuredLogger` is needed for test code, the `testing` module provides a better | ||
* option: `org.typelevel.log4cats.testing.StructuredTestingLogger` | ||
*/ | ||
object WriterStructuredLogger { | ||
def apply[G[_]: Alternative]( | ||
traceEnabled: Boolean = true, | ||
debugEnabled: Boolean = true, | ||
infoEnabled: Boolean = true, | ||
warnEnabled: Boolean = true, | ||
errorEnabled: Boolean = true | ||
): SelfAwareStructuredLogger[Writer[G[StructuredLogMessage], *]] = | ||
WriterTStructuredLogger[Id, G]( | ||
traceEnabled, | ||
debugEnabled, | ||
infoEnabled, | ||
warnEnabled, | ||
errorEnabled | ||
) | ||
|
||
def run[F[_]: Applicative, G[_]: Foldable]( | ||
l: StructuredLogger[F] | ||
): Writer[G[StructuredLogMessage], *] ~> F = | ||
new (Writer[G[StructuredLogMessage], *] ~> F) { | ||
def apply[A](fa: Writer[G[StructuredLogMessage], A]): F[A] = { | ||
val (toLog, out) = fa.run | ||
toLog.traverse_(StructuredLogMessage.log(_, l)).as(out) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.