-
Notifications
You must be signed in to change notification settings - Fork 3
Allow logging to be disabled for individual classes #3
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
base: main
Are you sure you want to change the base?
Changes from all commits
0e315cd
31c9742
26eb391
a313cf9
818ddf9
04c46a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,69 +16,157 @@ | |
|
|
||
| package com.permutive.logging.dynamic.odin | ||
|
|
||
| import cats.effect.unsafe.IORuntime | ||
| import cats.effect.{IO, Resource} | ||
| import com.permutive.logging.dynamic.odin.DynamicOdinConsoleLogger.{ | ||
| LevelConfig, | ||
| RuntimeConfig | ||
| } | ||
| import com.permutive.logging.odin.testing.OdinRefLogger | ||
| import io.odin.{Level, LoggerMessage} | ||
| import io.odin.formatter.Formatter | ||
| import io.odin.meta.Position | ||
| import io.odin.{Level, LoggerMessage} | ||
| import munit.{CatsEffectSuite, ScalaCheckEffectSuite} | ||
| import org.scalacheck.Arbitrary | ||
| import org.scalacheck.effect.PropF | ||
|
|
||
| import scala.collection.immutable.Queue | ||
| import scala.concurrent.duration._ | ||
|
|
||
| class DynamicOdinLoggerSpec extends CatsEffectSuite with ScalaCheckEffectSuite { | ||
|
|
||
| implicit val runtime: IORuntime = IORuntime.global | ||
| private implicit val arbPosition: Arbitrary[Position] = | ||
| Arbitrary( | ||
| Arbitrary | ||
| .arbitrary[(String, String, String, Int)] | ||
| .map((Position.apply _).tupled) | ||
| ) | ||
|
|
||
| test("record a message") { | ||
| PropF.forAllF { (message: String) => | ||
| val messages = runTest(_.info(message)) | ||
| private val defaultConfig = RuntimeConfig(Level.Info) | ||
|
|
||
| messages.map(_.map(_.message.value).toList).assertEquals(List(message)) | ||
| test("record only messages at the min level") { | ||
| PropF.forAllF { (debugMessage: String, infoMessage: String) => | ||
| val messages = runTest()(logger => | ||
| logger.debug(debugMessage) >> | ||
| logger.info(infoMessage) | ||
| ) | ||
|
|
||
| messages | ||
| .map(_.map(_.message.value).toList) | ||
| .assertEquals(List(infoMessage)) | ||
| } | ||
| } | ||
|
|
||
| test("update global log level") { | ||
| PropF.forAllF { (message1: String, message2: String) => | ||
| val messages = runTest { logger => | ||
| logger.info(message1) >> IO.sleep(10.millis) >> logger.update( | ||
| DynamicOdinConsoleLogger.RuntimeConfig(Level.Warn) | ||
| ) >> logger.info( | ||
| message2 | ||
| test("disable logging for a particular enclosure") { | ||
| PropF.forAllF { | ||
| ( | ||
| pos1Msg: String, | ||
| pos2Msg: String, | ||
| position1: Position | ||
| ) => | ||
| val position2 = | ||
| position1.copy(enclosureName = position1.enclosureName + "2") | ||
| val messages = runTest( | ||
| RuntimeConfig( | ||
| Level.Info, | ||
| Map( | ||
| position2.enclosureName -> LevelConfig.Off | ||
| ) | ||
| ) | ||
| )(logger => | ||
| logger.info(pos1Msg)(implicitly, position1) >> | ||
| logger.error(pos2Msg)(implicitly, position2) | ||
| ) | ||
|
|
||
| messages | ||
| .map(_.map(_.message.value).toList) | ||
| .assertEquals(List(pos1Msg)) | ||
| } | ||
| } | ||
|
|
||
| test("raises default-level config") { | ||
| PropF.forAllF { (messageBeforeChange: String, messageAfterChange: String) => | ||
| val messages = runTest() { logger => | ||
| logger.info(messageBeforeChange) >> | ||
| IO.sleep(10.millis) >> | ||
| logger.update(RuntimeConfig(defaultLevel = Level.Warn)) >> | ||
| logger.info(messageAfterChange) | ||
| } | ||
| messages | ||
| .map(_.map(_.message.value).toList) | ||
| .assertEquals(List(messageBeforeChange)) | ||
| } | ||
| } | ||
|
|
||
| test("lowers default-level config") { | ||
| PropF.forAllF { (messageBeforeChange: String, messageAfterChange: String) => | ||
| val messages = runTest() { logger => | ||
| logger.info(messageBeforeChange) >> | ||
| IO.sleep(10.millis) >> | ||
| logger.update(RuntimeConfig(defaultLevel = Level.Debug)) >> | ||
| logger.debug(messageAfterChange) | ||
| } | ||
| messages.map(_.map(_.message.value).toList).assertEquals(List(message1)) | ||
| messages | ||
| .map(_.map(_.message.value).toList) | ||
| .assertEquals(List(messageBeforeChange, messageAfterChange)) | ||
| } | ||
|
Comment on lines
+100
to
111
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would have failed in the old implementation as the logging level of The knock on effect of this change is covered in this comment |
||
| } | ||
|
|
||
| test("update enclosure log level") { | ||
| PropF.forAllF { (message1: String, message2: String, message3: String) => | ||
| val messages = runTest { logger => | ||
| logger.info(message1) >> IO.sleep(10.millis) >> logger.update( | ||
| DynamicOdinConsoleLogger.RuntimeConfig( | ||
| Level.Info, | ||
| Map("com.permutive" -> Level.Warn) | ||
| ) | ||
| ) >> logger.info( | ||
| message2 | ||
| ) >> logger.warn(message3) | ||
| test("overrides default level for a certain package") { | ||
| PropF.forAllF { (message: String) => | ||
| val messages = runTest( | ||
| RuntimeConfig( | ||
| defaultLevel = Level.Warn, | ||
| Map("com.permutive.logging.dynamic.odin" -> LevelConfig.Debug) | ||
| ) | ||
| ) { logger => | ||
| logger.debug(message) | ||
| } | ||
| messages | ||
| .map(_.map(_.message.value).toList) | ||
| .assertEquals(List(message1, message3)) | ||
| .assertEquals(List(message)) | ||
| } | ||
| } | ||
|
|
||
| test("update enclosure log level") { | ||
| PropF.forAllNoShrinkF { | ||
| ( | ||
| infoMsg1Pos1: String, | ||
| infoMsg2Pos1: String, | ||
| warnMsg1Pos1: String, | ||
| infoMsg2Pos2: String, | ||
| position1: Position, | ||
| position2: Position | ||
| ) => | ||
| val messages = runTest() { logger => | ||
| val positionWhichChangesLevel = | ||
| position1.copy(enclosureName = position1.enclosureName + "changes") | ||
| logger.info(infoMsg1Pos1)(implicitly, positionWhichChangesLevel) >> | ||
| IO.sleep(10.millis) >> | ||
| logger.update( | ||
| RuntimeConfig( | ||
| Level.Info, | ||
| Map(positionWhichChangesLevel.enclosureName -> LevelConfig.Warn) | ||
| ) | ||
| ) >> | ||
| logger.info(infoMsg2Pos1)(implicitly, positionWhichChangesLevel) >> | ||
| logger.warn(warnMsg1Pos1)(implicitly, positionWhichChangesLevel) >> | ||
| logger.info(infoMsg2Pos2)(implicitly, position2) | ||
| } | ||
| messages | ||
| .map(_.map(_.message.value).toList) | ||
| .assertEquals(List(infoMsg1Pos1, warnMsg1Pos1, infoMsg2Pos2)) | ||
| } | ||
| } | ||
|
|
||
| def runTest( | ||
| private def runTest(initialConfig: RuntimeConfig = defaultConfig)( | ||
| useLogger: DynamicOdinConsoleLogger[IO] => IO[Unit] | ||
| ): IO[Queue[LoggerMessage]] = (for { | ||
| testLogger <- Resource.eval(OdinRefLogger.create[IO]()) | ||
| dynamic <- DynamicOdinConsoleLogger.create[IO]( | ||
| DynamicOdinConsoleLogger | ||
| .Config(formatter = Formatter.default, asyncTimeWindow = 0.nanos), | ||
| DynamicOdinConsoleLogger.RuntimeConfig(Level.Info) | ||
| )(config => testLogger.withMinimalLevel(config.minLevel)) | ||
| initialConfig | ||
| )(config => testLogger.withMinimalLevel(config.defaultLevel)) | ||
| _ <- Resource.eval(useLogger(dynamic)) | ||
| } yield testLogger) | ||
| .use { testLogger => | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a bit dangerous. With this being set, we will inspect the
refon every log message, but it does at least mean that the level mapping can override the default level setting.I.e. every trace/debug message will hit the ref via the
submitimplementation below, rather than being filtered beforehand by this level here.I'm wondering whether we should re-introduce
minLevelwhich is the "absolute" minimum and cannot be lowered at runtime to prevent trace messages from hitting the ref.