Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • Feb 27 02:01
    scala-steward opened #256
  • Feb 27 02:01
    scala-steward opened #255
  • Feb 20 15:55
    codecov[bot] commented #249
  • Feb 20 15:54
    codecov[bot] commented #251
  • Feb 20 15:54
    codecov[bot] commented #251
  • Feb 20 15:50
    codecov[bot] commented #251
  • Feb 20 15:50
    scala-steward synchronize #251
  • Feb 20 15:49
    scala-steward synchronize #249
  • Feb 20 14:45

    sergeykolbasov on master

    Update cats-effect to 2.3.3 (#2… (compare)

  • Feb 20 14:45
    sergeykolbasov closed #252
  • Feb 20 14:45

    sergeykolbasov on master

    Update sbt-mdoc to 2.2.18 (#253) (compare)

  • Feb 20 14:45
    sergeykolbasov closed #253
  • Feb 19 03:56
    scala-steward closed #236
  • Feb 19 03:56
    scala-steward commented #236
  • Feb 19 03:56
    scala-steward opened #254
  • Feb 17 21:04
    codecov[bot] commented #253
  • Feb 17 21:04
    codecov[bot] commented #253
  • Feb 17 20:59
    scala-steward closed #243
  • Feb 17 20:59
    scala-steward commented #243
  • Feb 17 20:59
    scala-steward opened #253
Sergey Kolbasov
@sergeykolbasov
Okay, got it. Then I suggest to modify a signature of your wrapper function a bit to include an implicit parameter of type https://github.com/valskalla/odin/blob/master/core/src/main/scala/io/odin/meta/Position.scala
then it'll be propagated to logger call as implicit parameter as well
so it would be something like
def logAndReturn(e: SomeError)(implicit pos: io.odin.meta.Position): F[SomeError]

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

Which means that correct position will be derived in the place of invocation of your wrapper method
Diem
@bi0h4ck
I see. I was trying to modify Position in LoggerMessage but sourcecode.File gives me File.type so I couldn’t access value
Anyway, I will try out your suggestion. Thanks so much for the help.
Sergey Kolbasov
@sergeykolbasov
glad to help :)
Diem
@bi0h4ck

I modified the Position like this
object 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 :)

By the way, our team loves your awesome logging algebra Odin
Sergey Kolbasov
@sergeykolbasov
Thanks for your kind words 😌
Diem
@bi0h4ck
Hi @sergeykolbasov, Do you think it’s a bad idea to have slf4j logging for some non-algebra places and odin for some places with effect F? If it is, what is the better way to work around this?
Sergey Kolbasov
@sergeykolbasov

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

Generally, I'd suggest to stick to 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
slf4j bridge is rather a workaround for libraries you depend upon that use corresponding API and you wish to use Odin as a backend for it
Diem
@bi0h4ck
I thought about having slf4j and then pull in the slf4j bridge since we don’t define the effect until the end, so we can’t evaluate it right away. Thanks for response!!! :)
Jamal CHAQOURI
@redarqas_twitter
hi @sergeykolbasov is there an API to change the logging level at runtime ?
Sergey Kolbasov
@sergeykolbasov

Hi @redarqas_twitter

Sadly you can't mutate existing logger, but create a copy of one using withMinLevel syntax

Jamal CHAQOURI
@redarqas_twitter
thank you @sergeykolbasov
Ivano Pagano
@ivanopagano
hello, I hope this is a good place to ask questions... I'm following the example to test with the WriterTLogger provided with the library
I stumble into an unexpected issue with upcasting of the instance to a more general Logger[WriterTIO]
the compiler keeps telling me that WriterTLogger[WriterTIO] can't be assigned as a Logger[WriterTIO]
has anyone ever found this unexpected behaviour?
Ivano Pagano
@ivanopagano
nevermind, I managed to figure out the signatures
Sergey Kolbasov
@sergeykolbasov

out of curiosity, and for the sake of future generations

@ivanopagano what was the problem & solution for you? :)

Ivano Pagano
@ivanopagano
late to reply, ooops
I messed the type parameter as you can see from my example above, putting WriterTLogger[WriterTIO] (WriterTIO being the alias to my WriterT[IO, A, B])
while the type expected the actual effect F[_] without wrapping, i.e. WriterTLogger[IO]
Jakub Kozłowski
@kubukoz
Hey, I'm not sure the slf4j integration is correctly described - slf4j doesn't seem to be able to see the var with the version
if you look at the generated bytecode you'll see that it's not even a field, a getter is generated instead
Jakub Kozłowski
@kubukoz
btw. is there a logstash integration somewhere? I guess it would mostly be decoding the logs to logstash's format as JSON and sending them with a http client like sttp
Sergey Kolbasov
@sergeykolbasov

slf4j doesn't seem to be able to see the var with the version

damn Java >_>

@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 :)

Sergey Kolbasov
@sergeykolbasov
Hm, I don't think it's possible to define java-compatible static field
Jakub Kozłowski
@kubukoz
Yeah, I tried to do it in Java and refer to the Scala sources but hit a bug in bloop
Can't remember what sbt did...
Well, it compiled but I'm not sure it worked.
Sergey Kolbasov
@sergeykolbasov

@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]()
  }
}
I'll update documentation later
Jakub Kozłowski
@kubukoz
that's what I tried to do ;)
Sergey Kolbasov
@sergeykolbasov
well, works for me
Rohan Sircar
@rohan-sircar
Hi, if you don't mind me asking, how is scala.js support coming along?
Sergey Kolbasov
@sergeykolbasov

Hello

Sorry, didn't test it yet. In theory there is a PR opened, but have to check it out

Rohan Sircar
@rohan-sircar
Will be awesome when it comes out. But take your time.
Nice package name btw - package oh.boi . I put wow.doge in one of my projects.
Sergey Kolbasov
@sergeykolbasov
such wov, very amaze :)
Rohan Sircar
@rohan-sircar
very logging, much functional, many thanks
I had a few questions, I went through the documentation and couldn't find the answers there but maybe I missed it.
  1. In the Slf4j bridge, is there a built in way to do a catch all of all the classes under a package, or would I need to use something like a regex?
    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.
Rohan Sircar
@rohan-sircar

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?

Sergey Kolbasov
@sergeykolbasov

Hey @rohan-sircar, thanks for the questions

  1. It is a partial function, therefore you could use all the nice things that Scala gives you, including if guards, that are quite handy and much more powerful then just a wildcards:
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]()
}
  1. 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

  2. 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

Rohan Sircar
@rohan-sircar
Thanks for the quick response actually :)
  1. Oh, right, I should have thought of that
  2. I had a feeling that the instance is supposed to be passed around. I'm coming from OO-land and there loggers are instantiated per class so I asked. Wouldn't this make it a little boilerplate-y, since almost every class would need to have the logger in it's constructor?
  3. I did try to use 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 :)
    I do have an application shutdown method like -
    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.
    A little off the note - I was thinking of using a thin cake module to provide the http backend, actor system and logger to reduce the boilerplate, but that's discouraged in fp I suppose?
Sergey Kolbasov
@sergeykolbasov

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]