sergeykolbasov on master
Update cats-effect to 2.3.3 (#2… (compare)
sergeykolbasov on master
Update sbt-mdoc to 2.2.18 (#253) (compare)
Essentially, in the case when implicit is missing it's automatically derived here:
https://github.com/valskalla/odin/blob/master/core/src/main/scala/io/odin/meta/Position.scala#L14-L24
using macro of sourcefile library
I modified the Position like thisobject Position {
implicit def derivePosition(
implicit fileName: sourcecode.File,
packageName: sourcecode.Pkg,
line: sourcecode.Line
): Position = io.odin.meta.Position(fileName.value, fileName.value, packageName.value, line.value)
}
and I got the className printed out as I want.
Thank you so much :)
Hi @bi0h4ck
You have couple of options.
One is to pass around Logger[IO]
(or any other effect) and call it with finalizing .unsafeRunSync()
(or async version) to evaluate right away. That way you might use the single instance of logger which would make your life easier.
Or use slf4j with LoggerBinder:
https://github.com/valskalla/odin#slf4j-bridge
F
as long as possible, because logging itself is a side-effect and it's way easier to i.e. test it when you have each call as a value
WriterTLogger[WriterTIO]
can't be assigned as a Logger[WriterTIO]
@kubukoz
Nope, there is no logstash integration. At Zalando we ship logs with a separate agent as a part of Kubernetes deployment, so it never was a priority for us to have such functionality in this lib.
However, it's not the first time I hear that question, so I'm open for contributions :)
@kubukoz
I think I solved that issue with StaticLoggerBinder
.
You need a plain Java class:
package org.slf4j.impl;
import oh.boi.ExternalLogger;
public class StaticLoggerBinder extends ExternalLogger {
public static String REQUESTED_API_VERSION = "1.7";
private static StaticLoggerBinder instance = new StaticLoggerBinder();
public static StaticLoggerBinder getSingleton() {
return instance;
}
}
together with a Scala one:
package oh.boi
import cats.effect.{ContextShift, Clock, Effect, IO, Timer}
import io.odin._
import io.odin.slf4j.OdinLoggerBinder
import scala.concurrent.ExecutionContext
class ExternalLogger extends OdinLoggerBinder[IO] {
val ec: ExecutionContext = scala.concurrent.ExecutionContext.global
implicit val timer: Timer[IO] = IO.timer(ec)
implicit val clock: Clock[IO] = timer.clock
implicit val cs: ContextShift[IO] = IO.contextShift(ec)
implicit val F: Effect[IO] = IO.ioEffect
val loggers: PartialFunction[String, Logger[IO]] = {
case "some.external.package.SpecificClass" =>
consoleLogger[IO](minLevel = Level.Warn) //disable noisy external logs
case _ => //if wildcard case isn't provided, default logger is no-op
consoleLogger[IO]()
}
}
package oh.boi
. I put wow.doge
in one of my projects.
val loggers: PartialFunction[String, Logger[IO]] = {
case "org.asynchttpclient.netty.channel.DefaultChannelPool" =>
consoleLogger[IO](minLevel = Level.Warn)
case _ => //if wildcard case isn't provided, default logger is no-op
consoleLogger[IO]()
}
Suppose I wanted to catch all classes under org.asynchttpclient under one case clause.2 - Is the logger instance supposed to be passed around, or should each class create it's own logger instance?
3 - I've been unable to integrate the Resource monad in my code that uses Monix Task, so I'm using the unsafe method for getting the logger instance -
val logger = consoleLogger().withAsyncUnsafe()
But I'm not sure what unsafe means here, does it refer to flushing the loggers buffer? Is it possible to flush the buffer in a shutdown hook thus making it safe?
Hey @rohan-sircar, thanks for the questions
val loggers: PartialFunction[String, Logger[IO]] = {
case asyncHttpClient if asyncHttpClient.startsWith("org.asynchttpclient.netty") =>
consoleLogger[IO](minLevel = Level.Warn)
case _ => //if wildcard case isn't provided, default logger is no-op
consoleLogger[IO]()
}
I'd suggest to have a single instance for everything, as it's just easier to manage. But you might have different needs for different classes, and in case you can't just route the message using stuff from io.odin.config._
to different loggers, just provide another instance
Resource
is hard to integrate for sure. What I could suggest is to have on the very top of you application (i.e. TaskApp.main
) a Resource
monad that you .use(_ => Task.never)
. Often you start multiple resources there, such as HTTP clients, some I/O channels like database connection etc etc. So instead of encapsulating all the I/O
initialization inside of implementations, making them side-effectful by that, you do that in the main. In Odin .unsafe
stuff doesn't use the shutdown hook sadly, but you could make it on your own though
Resource
that way, but without the .use(_ => Task.never)
part, it just free'd all the resources once the control reached the end of the Task
chain, and since I'm new to effect programming I was wondering what's up :)def makePrimaryStage(
backend: AppTypes.HttpBackend,
actorSystem: classic.ActorSystem
) = {
new PrimaryStage {
scene = new DefaultUI().scene
onCloseRequest = () => {
val f2 = actorSystem.terminate()
val f1 = backend.close().runToFuture
......
}
}
}
It's a JavaFX application. I was thinking I could close the logger here, but what method am I supposed to use? I couldn't find anything named stop, terminate or flush. Maybe I should go through the API docs.Can't you just extend i.e. TaskApp
or IOApp
in the main object of you application and run the resource allocation + .use
in the main
method?
it'll take care of the shutdown hook on its own for all the running resources.
Ie, actor system could be allocated as a resource as well, something like:
val acquire: Task[ActorSystem] = Task(createActorSystem)
def release(actorSystem: ActorSystem): Task[Unit] = Task(actorSystem.terminate())
Resource.make(acquire)(release) // Resource[Task, ActorSystem]
def init[F[_]: ConcurrentEffect: Timer: ContextShift]: Resource[F, Unit] =
for {
config <- Config.load[F]
logger <- InternalLogger.init[F](config.app)
oauthService <- OAuthService.init(config.oauth)
storageService = StorageService.noop[F]
contentService = ContentService.noop[F]
routes = Routes.all(storageService, contentService)
httpServer <- HttpServer.init(config.http, routes, logger)
_ <- httpServer.run
} yield {
()
}