Adding an Appender does not work - scala

I am trying to get an logger and add a specific appender to it. My code is very simple but I am not sure how to get this working.
val loggerInstance = LoggerFactory.getLogger("FOO.class")
var fileAppender = new FileAppender()
// fileAppender .setFile , sietPattern etc..the parameters i want
loggerInstance.addAppender(fileAppender)
I get an error here
Multiple markers at this line
- type mismatch; found : ch.qos.logback.core.FileAppender[Nothing] required:
ch.qos.logback.core.Appender[ch.qos.logback.classic.spi.ILoggingEvent] Note: Nothing <:
ch.qos.logback.classic.spi.ILoggingEvent, but Java-defined trait Appender is invariant in type E. You may wish to investigate a
wildcard type such as `_ <: ch.qos.logback.classic.spi.ILoggingEvent`. (SLS 3.2.10)
- Line breakpoint:loggerchange [line: 76] - addAppender
I have no clue what this error means and how to solve it. Can someone help me?
EDIT :
I tried to do what was told by drexin. I was not able to extend the interface and define the functions . There were only three functions , setName,getName, and doAppend. i am not sure as how to define these functions. Meanwhile i tried something and removed the errors. Please look into the code and let me know if what i am doing makes any sense .
val encoder = new PatternLayoutEncoder()
encoder2.setContext(context)
encoder2.setPattern("%msg%")
fileAppender.setAppend(true)
fileAppender.setContext(context)
fileAppender.setEncoder(encoder2.asInstanceOf[Encoder[Nothing]])
loggerInstance.asInstanceOf[Logger].addAppender(fileAppender
.asInstanceOf[Appender[ILoggingEvent]])
I know using asInstanceOf is not a smart way of coding but for now i want to make this work . When i execute this code i am getting the file in which i want to log but there is no logs inside it. I have checked for level errors , but that's not the case. i believe there is something wrong with encoder/layout. I am not sure how to fix it. Can someone show me how to either extend the class and apply the functions or what is wrong in this new code .

The FileAppender has a type parameter class FileAppender[E], but you instantiate it without providing such a parameter, so scala decides to put Nothing in as type. addAppender expects an appender of type Appender[ILoggingEvent], that is what the error says. What you have to do now, is to either use an existing, or create your own subclass of ILoggingEvent and provide it as type param:
class MyLoggingEvent extends ILoggingEvent {
// implement ALL the methods
}
val fileAppender = new FileAppender[MyLoggingEvent]()

My second approach worked . I am able to see the logs now. I was making the mistake in the pattern. It just a small tweak .
val encoder = new PatternLayoutEncoder()
encoder.setContext(context)
encoder.setPattern("%msg%n")
fileAppender.setAppend(true)
fileAppender.setContext(context)
fileAppender.setEncoder(encoder.asInstanceOf[Encoder[Nothing]])
loggerInstance.asInstanceOf[Logger].addAppender(fileAppender
.asInstanceOf[Appender[ILoggingEvent]])
I will summarize all my efforts towards logging/appending automatically and put it up soon. Hope that will help others too .

Related

Scala type inference not working with play json

I am writing an http client and this is my signature:
def post[Req, Resp](json: Req)(implicit r: Reads[Resp], w: Writes[Req]): Future[Resp]
Using play json behind the scenes.
When I use it like this
def create(req: ClusterCreateRequest): Future[ClusterCreateResponse] = endpoint.post(req)
I get the following error
diverging implicit expansion for type play.api.libs.json.Reads[Resp]
The following works
def create(req: ClusterCreateRequest): Future[ClusterCreateResponse] = endpoint.post[ClusterCreateRequest, ClusterCreateResponse](req)
Why is type inference not working as expected? What can I do for this?
diverging implicit expansion for type play.api.libs.json.Reads[Resp]
means that Resp has few JSON serializers that are not shadowed one by another.
It's not possible to pinpoint root cause the issue and say fix X and everything will work from the infrmation given in post.
But you can try to "debug" implicit search. Consider checking the implicit search order:
Where does Scala look for implicits? Enabling implicit parameter expansion in idea might help to check which implicits(Ctrl+Shift+=) cause a clash.
General advice for type class instances - hold them organized and declared, put them to companion object or to specially dedicated object.

can`t bind[SttpBackend[Try, Nothing]]

I want to use sttp library with guice(with scalaguice wrapper) in my app. But seems it is not so easy to correctly bind things like SttpBackend[Try, Nothing]
SttpBackend.scala
Try[_] and Try[AnyRef] show some other errors, but still have no idea how it should be done correctly
the error I got:
kinds of the type arguments (scala.util.Try) do not conform to the expected kinds of the type parameters (type T).
[error] scala.util.Try's type parameters do not match type T's expected parameters:
[error] class Try has one type parameter, but type T has none
[error] bind[SttpBackend[Try, Nothing]].toProvider[SttpBackendProvider]
[error] ` ^
SttpBackendProvider looks like:
def get: SttpBackend[Try, Nothing] = TryHttpURLConnectionBackend(opts)
complete example in scastie
interesting that version scalaguice 4.1.0 show this error, but latest 4.2.2 shows error inside it with converting Nothing to JavaType
I believe you hit two different bugs in the Scala-Guice one of which is not fixed yet (and probably even not submitted yet).
To describe those issues I need a fast intro into how Guice and Scala-Guice work. Essentially what Guice do is have a mapping from type onto the factory method for an object of that type. To support some advanced features types are mapped onto some internal "keys" representation and then for each "key" Guice builds a way to construct a corresponding object. Also it is important that generics in Java are implemented using type erasure. That's why when you write something like:
bind(classOf[SttpBackend[Try, Nothing]]).toProvider(classOf[SttpBackendProvider])
in raw-Guice, the "key" actually becomes something like "com.softwaremill.sttp.SttpBackend". Luckily Guice developers have thought about this issue with generics and introduced TypeLiteral[T] so you can convey the information about generics.
Scala type system is more reach than in Java and it has some better reflection support from the compiler. Scala-Guice exploits it to map Scala-types on those more detailed keys automatically. Unfortunately it doesn't always work perfectly.
The first issue is the result of the facts that the type SttpBackend is defined as
trait SttpBackend[R[_], -S]
so it uses it expects its first parameter to be a type constructor; and that originally Scala-Guice used the scala.reflect.Manifest infrastructure. AFAIU such higher-kind types are not representable as Manifest and this is what the error in your question really says.
Luckily Scala has added a new scala.reflect.runtime.universe.TypeTag infrastructure to tackle this issue in a better and more consistent way and the Scala-Guice migrated to its usage. That's why with the newer version of Scala-Guice the compiler error goes away. Unfortunately there is another bug in the Scala-Guice that makes the code fail in runtime and it is a lack of handling of the Nothing Scala type. You see, the Nothing type is a kind of fake one on the JVM. It is one of the things where the Scala type system is more reach than the Java one. There is no direct mapping for Nothing in the JVM world. Luckily there is no way to create any value of the type Nothing. Unfortunately you still can create a classOf[Nothing]. The Scala-to-JVM compiler handles it by using an artificial scala.runtime.Nothing$. It is not a part of the public API, it is implementation details of specifically Scala over JVM. Anyway this means that the Nothing type needs additional handling when converting into the Guice TypeLiteral and there is none. There is for Any the cousin of Nothing but not for Nothing (see the usage of the anyType in TypeConversions.scala).
So there are really two workarounds:
Use raw Java-based syntax for Guice instead of the nice Scala-Guice one:
bind(new TypeLiteral[SttpBackend[Try, Nothing]]() {})
.toInstance(sttpBackend) // or to whatever
See online demo based on your example.
Patch the TypeConversions.scala in the Scala-Guice as in:
private[scalaguice] object TypeConversions {
private val mirror = runtimeMirror(getClass.getClassLoader)
private val anyType = typeOf[Any]
private val nothingType = typeOf[Nothing] // added
...
def scalaTypeToJavaType(scalaType: ScalaType): JavaType = {
scalaType.dealias match {
case `anyType` => classOf[java.lang.Object]
case `nothingType` => classOf[scala.runtime.Nothing$] //added
...
I tried it locally and it seems to fix your example. I didn't do any extensive tests so it might have broken something else.

How to resolve ambiguous method reference in scala

Here is the specific issue I am encountering. I am using SLF4J Logger (The type of the variable logger below)
//After adding to a map
logger debug ("Adding {} = {}", key, value)
Here is what mouse hover in eclipse (and the compiler) tell me.
ambiguous reference to overloaded definition, both method debug in trait Logger of type (x$1: String, x$2: Object*)Unit and method debug in trait Logger of type (x$1: String, x$2: Any, x$3: Any)Unit match argument types (String,String,String)
I understand why they are ambiguous. I am certainly not arguing with the compiler :). I want to simply know how seasoned programmers solve this issue.
Here are the alternatives I can use
Create and array , and ride the Object* definition
logger debug ("Adding {} = {}", Array(key, value):_*)
Cast to Any
logger debug ("Adding {} = {}", key.asInstanceOf[Any], value.asInstanceOf[Any])
Neither approach is particularly appealing. Does the community have a better approach or suggestions for me?
Many thanks!
I would use
logger.debug("Adding {} = {}", key, value: Any)
Alternatively following can be used:
logger.debug("Adding {} = {}", Array(key, value):_*)
Please pay attention to :_*. Should you omit these symbols and it will call Object* method providing only 1 argument, which will be an array.
First off a nod of credit to #Shadowlands, #ArneClaassen and #OlgeRudenko.
As mentioned in the comments, this does seem to be known issue. That stopped me from trying to "solve" it. The next thing to do was to find a good work around that did not break Scala idioms.
With these constraints in mind I chose to go with String Interpolation as suggested above. I also switched to scala-logging. Quoting from their GitHub/README,
Scala Logging is a convenient and performant logging library wrapping SLF4J. It's convenient, because you can simply call log methods without checking whether the respective log level is enabled:
logger.debug(s"Some $expensive message!")
It's performant, because thanks to Scala macros the check-enabled-idiom is applied, just like writing this more involved code:
if (logger.isDebugEnabled) logger.debug(s"Some $expensive message!")
Thanks all! As far as I am concerned, this is resolved. If the commentators can post their answers, I will be happy to acknowledge them.
As always, feels good to be standing on the shoulders of friendly giants!
PS: I just verified that there is no execution cost to String interpolation if you are using scala-logging. My verification method was crude but effective.
log.debug{
{
throw new IllegalAccessException("This should not have been called with debug off!")
}
s"Added Header ${name}:${headerValue}"
}
Sure enough, when I set my log to DEBUG, the exception is thrown, as expected , but vanishes when I set it to a level higher.
And yes, I have already removed the IllegalAccessException part :).

Strange type mismatch error

I have a table column errorFixed of type TableColumn[Error, Boolean] inside a TableView[Error]. My Error class has a val fixed: Boolean which I try to put into this table column.
I tried
errorFixed.cellValueFactory = features =>
ReadOnlyBooleanWrapper(features.value.fixed)
but it fails with
type mismatch;
found : scalafx.beans.property.ReadOnlyBooleanWrapper
required: scalafx.beans.value.ObservableValue[Boolean,Boolean]
which I really don't understand as ObservableValue[Boolean,Boolean] is a supertype of ReadOnlyBooleanWrapper according to the documentation.
If I cast it myself using .asInstanceOf[ObservableValue[Boolean, Boolean]] it seems to work. What is going on here?
Gist with stripped down project to reproduce
Short answer is: instead of
errorFixed.cellValueFactory = features =>
ReadOnlyBooleanWrapper(features.value.fixed)
you should use
errorFixed.cellValueFactory = features =>
ObjectProperty[Boolean](features.value.fixed)
or ReadOnlyObjectWrapper[Boolean].
A brief version of long answer: there are certain "frictions" between Scala and Java when working with primitive Java types, like boolean or int. This inconvenience shows up in property binding in ScalaFX. Not everything is inherited in an intuitive way. In this case
ReadOnlyBooleanWrapper
is a subclass of
ObservableValue[scala.Boolean, java.lang.Boolean]
but scala.Boolean is not a subclass of java.lang.Boolean which internally, in ScalaFX this leads to complications. Interesting thing is that the casting .asInstanceOf[ObservableValue[scala.Boolean, scala.Boolean]] works, though type parameters do not match at compile time.
Thanks for positing a full code example (gist) this really helps in clarifying the question.

Scala manifest and instances

I'm using Jerkson, and I need to check if a given class can be serialized. The java version just needs a class, but jerkson does this:
def canSerialize[A](implicit mf: Manifest[A]) = mapper.canSerialize(mf.erasure)
Given that I have an instance, how can I call this? I pretty much tried
canSerialize[ClassManifest.fromClass(foo)]
But its not working. I wonder why the guys at jerkson could not make it simpler by just making this: canSerialize(Class[_]) ...
Any ideas on how can I invoke this?
Edit:
I fixed this by using:
canSerilialize(Manifest.classType(foo.getClass))
How about this:
canSerialize[Foo]
Compiler can automatically generate manifest for you (if it has enough type information in context)
Since Scala 2.8.0 canSerialize can be written via context bound. See more
If you don't know the class in advance, you can always pass the manifest as a parameter, i.e. this should work: canSerialize( Manifest.classType( foo.getClass ) ).