Is this logging blocking? - scala

I have read that AkkaLogging is async and directly using slf4j is blocking. Consider the below code:
application.conf:
akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
}
logback.xml:
<configuration>
<appender name="InfoFile" class="ch.qos.logback.core.FileAppender">
<file>Testing.log</file>
<append>true</append>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="InfoFile" />
</root>
</configuration>
Log statements:
log.info("Sample logging")
Will this logging code be still blocking?If so, does it mean there is no way to achieve non blocking file logging in akka?

Akka logging is asynchronous.
But from this document, it seems "logging operations will block while the underlying infrastructure writes the log statements".
However, you can use nonblocking appenders AsyncAppender in your logback.xml to avoid this.

Related

How to 'censor' some data in libraries self-generated logs?

I am using the library scalikejdbc for my Application. And when i read the logs, that scalikejdbc generates itself, it gives me something like that:
06:02:16.891 [main] DEBUG scalikejdbc.ConnectionPool$ - Registered connection pool : ConnectionPool(url:jdbc:sqlserver://foo.bar:8080;databaseName=foobar;user=user;password=**PASSWORD**;...
So scalike itself throws my database user's password into the logs, which is inappropriate.
I was thinking about filtering those logs at all with something like https://logback.qos.ch/manual/filters.html , but I do need the odd information from those logs from scalike, so I cannot filter them fully.
What do I have now:
06:02:16.891 [main] DEBUG scalikejdbc.ConnectionPool$ - Registered connection pool : ConnectionPool(url:jdbc:sqlserver://foo.bar:8080;databaseName=foobar;user=user;password=somepassword;
What am I try to get:
06:02:16.891 [main] DEBUG scalikejdbc.ConnectionPool$ - Registered connection pool : ConnectionPool(url:jdbc:sqlserver://foo.bar:8080;databaseName=foobar;user=user;password=CENSORED;
Consider using replace conversion word in logback.xml configuration, for example,
%replace(%msg){"password=.*", "password=CENSORED"}
which given
logger.debug("password=123456")
should output something like
[debug] application - password=CENSORED
Docs state
replace(p){r, t} Replaces occurrences of 'r', a regex, with its
replacement 't' in the string produces by the sub-pattern 'p'. For
example, "%replace(%msg){'\s', ''}" will remove all spaces contained
in the event message.
Here is an example of my logback.xml:
<configuration>
<conversionRule conversionWord="coloredLevel" converterClass="play.api.libs.logback.ColoredLevel" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%coloredLevel %logger{15} - %replace(%msg){"password=.*", "password=CENSORED"}%n %xException{10}</pattern>
</encoder>
</appender>
<logger name="play" level="INFO" />
<logger name="application" level="DEBUG" />
<logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF" />
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>

Play 2.6x Scala app does not log despite logback configuration

I'm trying to enable logging in my Play 2.6 Scala app.
I've basically implemented this logging filter to log each request processed by the server: https://www.playframework.com/documentation/2.6.x/ScalaHttpFilters.
I cannot for the life of me get the logs to show in either the console OR the specific log/ directory. I've tried everything from playing around with my logback.xml file and my app config.
Does anyone have any ideas where I'm going wrong? The filter I wrote is being processed on request (I set a breakpoint to ensure).
logback.xml:
<!-- https://www.playframework.com/documentation/latest/SettingsLogger -->
<configuration>
<conversionRule conversionWord="coloredLevel" converterClass="play.api.libs.logback.ColoredLevel" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${application.home:-.}/logs/application.log</file>
<encoder>
<pattern>%date [%level] from %logger in %thread - %message%n%xException</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%coloredLevel %logger{15} - %message%n%xException{10}</pattern>
</encoder>
</appender>
<appender name="ASYNCFILE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<appender name="ASYNCSTDOUT" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="STDOUT" />
</appender>
<logger name="play" level="INFO" />
<logger name="application" level="DEBUG" />
<!-- Off these ones as they are annoying, and anyway we manage configuration ourselves -->
<logger name="com.avaje.ebean.config.PropertyMapLoader" level="OFF" />
<logger name="com.avaje.ebeaninternal.server.core.XmlConfigLoader" level="OFF" />
<logger name="com.avaje.ebeaninternal.server.lib.BackgroundThread" level="OFF" />
<!--<logger name="org.jdbcdslog.StatementLogger" level="DEBUG" /> <!– Will log all statements –>-->
<!--<logger name="slick.jdbc.JdbcBackend.statement" level="DEBUG" />-->
<logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF" />
<root level="INFO">
<appender-ref ref="ASYNCFILE" />
<appender-ref ref="ASYNCSTDOUT" />
</root>
</configuration>
LoggingFilter.scala:
package filters
import javax.inject.Inject
import akka.stream.Materializer
import play.api.Logger
import play.api.mvc._
import scala.concurrent.{ExecutionContext, Future}
class LoggingFilter #Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter {
def apply(nextFilter: RequestHeader => Future[Result])
(requestHeader: RequestHeader): Future[Result] = {
Logger.debug("Hi!") // Why you no work!!
val startTime = System.currentTimeMillis
nextFilter(requestHeader).map { result =>
val endTime = System.currentTimeMillis
val requestTime = endTime - startTime
Logger.info(s"${requestHeader.method} ${requestHeader.uri} took ${requestTime}ms and returned ${result.header.status}")
result.withHeaders("Request-Time" -> requestTime.toString)
}
}
}
Well, after several hours of debugging, I finally found the cause (could be helpful for others so posting here).
Cause:
The root cause of the issue is a conflicting logging dependency bundled in play-silhoutte. My app was using play-silhoutte version 5.0.3 which apparently contains a bug causing the Play logger to be overwritten by a logging configuration in a sub-dependency of play-silhoutte(com.typesafe.play:play-openid_2.12:2.6.9).
Diagnosis:
Unfortunately this took a long time to debug - combination of reading Play source code + debugging the stack frames as the Logger gets initialized. I noticed that the Logger was being initialized twice - once for the actual Play logger (with correct configuration as specified in my logback.xml then again as a result of play-silhoutte (effectively overwriting the good logger).
Looking at the initialization params, it was reading its logback.xml from /Users/.../.ivy2/cache/com.typesafe.play/play-openid_2.12/jars/play-openid_2.12-2.6.9.jar!logback.xml. I expanded the jar and found the following logging config:
<!--
~ Copyright (C) 2009-2017 Lightbend Inc. <https://www.lightbend.com>
-->
<configuration>
<!-- Suppress logback complaining about multiple logback-test.xml files -->
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- We use short exception stack trace logging to limit output for travis. -->
<!-- Change to full if you need to do further debugging, but never commit that. -->
<pattern>%level %logger{15} - %message%n%ex{short}</pattern>
</encoder>
</appender>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>
At this point I knew it was a conflicting depedency issue.
Solution:
After doing some digging, I actually found a bug open on Github describing my issue. I was running play-silhoutte version 5.0.3 so bumping it to 5.0.4 fixed the issue.

Change log level for phantom embedded Cassandra

In my Scala project I'am using the phantom-sbt plugin in order to start embedded Cassandra. The problem is, this plugin is pretty verbose - all cassandra logs will be written to stdout.
I've seen on phantom github page, they are using log4j to configure all loggers. But it seems not to work (at least for me). I've set all loggers in the log4j.xml on 'ERROR', but it has no effect.
How should I change log level for all cassandra loggers?
You need a logback-test.xml inside /src/test/resources wherever you are running the embedded Cassandra. Then you can easily turn off individual loggers or set them to the appropriate levels.
Take this as an example configuration:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
<logger name="com.datastax.driver.core" level="ERROR"/>
<logger name="io.netty" level="ERROR"/>
<logger name="org.cassandraunit" level="ERROR"/>

Changing the log level for a running Actor system ?

My resources/application.conf looks like the following.
akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = "DEBUG"
}
I'm creating an fat jar using $sbt assembly that contains this application.conf and deploying this jar to run my actors. Now can I change the log level (from DEBUG to INFO) of my program at runtime without brining down my actor system? If yes, how ?
My logback.xml looks like the following:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder>
<pattern>%X{akkaTimestamp} %-5level[%thread] %logger{0} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/myjobs.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>logs/myjobs.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>5</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%date{yyyy-MM-dd} %X{akkaTimestamp} %-5level[%thread] %logger{1} - %msg%n</pattern>
</encoder>
</appender>
<logger name="akka" level="INFO" />
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
You can set the Akka loglevel using system.eventStream.setLogLevel() (within an Actor you would use context.system as the starting point).

Set logging level in Akka

I have developed a financial data distribution server with Akka, and I want to set logging level for the application. The documentation at akka.io is sketchy at the best; they say there is no more "logging" in Akka and logging is defined through event handlers now. There is also an example of event handler configuration, including logging level:
akka {
event-handlers = ["akka.event.EventHandler$DefaultListener"]
event-handler-level = "INFO"
}
I did that, but though akka.conf is successfully loaded, logging still appears to be at "DEBUG" level. What can be the problem there?
It appears that Akka uses slf4j/logback logging with default configuration. So the (never documented) solution would be to put e.g. the following logback.xml in your classpath:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" debug="false">
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%4p] [%d{ISO8601}] [%t] %c{1}: %m%n</pattern>
</encoder>
</appender>
<!-- you can also drop it completely -->
<logger name="se.scalablesolutions" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="stdout"/>
</root>
</configuration>