Akka with org.apache.log4j.MDC

570 views
Skip to first unread message

Anindita Ghatak

unread,
Apr 27, 2015, 3:14:23 AM4/27/15
to akka...@googlegroups.com
Hi,
How can I use Akka with org.apache.log4j.MDC ?

Thanks & Regards,
Anindita

Viktor Klang

unread,
Apr 27, 2015, 3:30:47 AM4/27/15
to Akka User List
Hi Anindita,

It would be great for us to know what is lacking in the MDC sections of the Akka Logging documentation,
please don't hesitate to contribute.

Thanks!

--
>>>>>>>>>> Read the docs: http://akka.io/docs/
>>>>>>>>>> Check the FAQ: http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
---
You received this message because you are subscribed to the Google Groups "Akka User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to akka-user+...@googlegroups.com.
To post to this group, send email to akka...@googlegroups.com.
Visit this group at http://groups.google.com/group/akka-user.

For more options, visit https://groups.google.com/d/optout.



--
Cheers,

byse...@gmail.com

unread,
May 7, 2015, 4:27:59 AM5/7/15
to akka...@googlegroups.com
Hi √,

I want to have MDC logging so I can organize my logs, for example according to requestId. 


It works fine for a single actor, but If I'm sending a message to another Actor, the MDC values are not log on the other Actors.

class RequestHandler() extends Actor with akka.actor.DiagnosticActorLogging{

   override def mdc(currentMessage: Any): MDC = {
     currentMessage match {
       case req: TypeA => Map("requestId" -> req.id)
       case _ => Map()
     }
   }

   def receive: Receive = {
      case req: TypeA =>
         log.debug("RECEIVE req typeA")                 //logged with correct "[requestId -> someRandom]"
         val actorA = context.ActorOf(ActorA.props)
         actorA ! req                                   //logs in actorA are do not have value on requestId (how to log them?)
   }
}

class ActorA() extends Actor with akka.actor.DiagnosticActorLogging {...} //logs of helperActors called by ActorA are not also logged with correct requestId (how to log them?)

Is it possible with AKKA MDC to log all codeLogs (including logs from dependency libs) for a certain requestId with correct MDC map values?

   I also set akka logger to sl4fj on my application.conf

akka {
  loggers = ["akka.event.slf4j.Slf4jLogger"]
     logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"


 

nil...@gmail.com

unread,
May 8, 2015, 2:31:03 AM5/8/15
to akka...@googlegroups.com
I have struggled a lot with the same challenge. The MDC feature of the logging frameworks need a little more "manual" intervention when operating in an asynchronous runtime like Akka. MDC is tightly coupled to a thread, but communication with and between Actors happen in different threads. Hence, the MDC is not propagated automatically when sending messages between your actors. Basically, you need to pass the context you wish to log along with the messages in your system. So if you want to trace a requestId in a conversation between your actors, you need to pass the requestId along in the message, and then put it back in the MDC in the receiving actor. 

Regards,

Nils-Helge Garli Hegvik

byse...@gmail.com

unread,
May 12, 2015, 2:18:22 AM5/12/15
to akka...@googlegroups.com
Thanks Nils, I am planning to use this approach now, but how do you keep context between actors if you are using third party lib actors? I can't modify them to include the context in their messages.


class RequestHandler() extends Actor with akka.actor.DiagnosticActorLogging{

  override def mdc(currentMessage: Any): MDC = {
    currentMessage match {
      case req: TraitWithRequestId => Map("requestId" -> req.id)
      case _ => Map()
    }
  }

   def receive: Receive = {
     case req: TypeA =>                                //TypeA extends TraitWithRequestId

        log
.debug("RECEIVE req typeA")                 //logged with correct "[requestId -> someRandom]"
        val thirdPartyA = context.ActorOf(thirdPartyA.props)
        thirdPartyA ! req  

     
case thirdPartyResponse =>                       //cant modify 3rd party to extend TraitWithRequestId
         log.debug("RECEIVE third party response")    //no value "[requestId -> ]
         val actorA = context.ActorOf(ActorA.props)
         val messageToActorA = (thirPartyResponse.param1, requestId)  
         actorA ! messageToActorA                                    
       

   
}
}

Yann Simon

unread,
May 12, 2015, 2:30:39 AM5/12/15
to akka...@googlegroups.com
another alternative is to use http://kamon.io/

Use a trace context instead of MDC, and the trace context is propagated: http://kamon.io/integrations/akka/automatic-trace-context-propagation/
Then, in your log configuration, use converter to access the data you put in the trace context: http://kamon.io/integrations/logback/trace-token-converter/

Message has been deleted

byse...@gmail.com

unread,
May 12, 2015, 6:39:15 AM5/12/15
to akka...@googlegroups.com
Thanks a lot Yann! This is super cool :D. I'm still browsing the documents, would it be possible to use other conversionRule aside from traceToken


<conversionRule conversionWord="traceToken" converterClass="kamon.trace.logging.LogbackTraceTokenConverter"/>

I am planning to put my requestId in the metaData of the traceContext but I can't find the docs on how to modify the logback.xml for rules other than "traceToken"


class RequestHandler() extends Actor with ActorLogging {

   def receive: Receive = {
    case req: TypeA =>   
        val context = Kamon.tracer.newContext(req.requestId)
        context.addMetada("requestId", req.requestId)
        context.addMetada("otherKeyId", req.otherKeyId) 
                         
        Tracer.withNewContext(context){

          log.debug("RECEIVE req typeA")                
          val thirdPartyA = context.ActorOf(thirdPartyA.props)
          thirdPartyA ! req  
        }
     

   
}
}

logback
.xml

<configuration>
    <conversionRule conversionWord="requestId" converterClass="kamon.trace.logging.LogbackTraceTokenConverter"/>
    <conversionRule conversionWord="otherKeyId" converterClass="kamon.trace.logging.LogbackTraceTokenConverter"/>
   
 <conversionRule conversionWord="name" converterClass="kamon.trace.logging.LogbackTraceTokenConverter"/>

 
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                 <level>DEBUG</level>
           </filter>
              <encoder>
                      <pattern>%date{ISO8601} [%-5level] [%requestId] [%otherKeyId] [%name] %logger{36} %X{sourceThread} - %msg%n</pattern>
               </encoder>
  </appender>

...

</configuration>

 


[%requestId] [%otherKeyId] [%name] still output the data for traceToken instead of the value I assigned for the name and metadata of the context

Also, have you encountered this while using Kamon during startup?
[error] objc[5646]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/bin/java and /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/libinstrument.dylib. One of the two will be used. Which one is undefined.

Yann Simon

unread,
May 12, 2015, 7:07:36 AM5/12/15
to akka...@googlegroups.com
Le mar. 12 mai 2015 à 12:31, <byse...@gmail.com> a écrit :
Thanks a lot Yann! This is super cool :D. I'm still browsing the documents, would it be possible to use other conversionRule aside from traceToken


<conversionRule conversionWord="traceToken" converterClass="kamon.trace.logging.LogbackTraceTokenConverter"/>

Yes, I think so but I have not checked it myself.
But it seems quite easy: just write your own converter.
 
I am planning to put my requestId in the metaData of the traceContext but I can't find the docs on how to modify the logback.xml for rules other than "traceToken"


class RequestHandler() extends Actor with ActorLogging {

   def receive: Receive = {
    case req: TypeA =>  
        val context
= Kamon.tracer.newContext(req.requestId)

        context
.addMetada("requestId", req.requestId)
        context
.addMetada("otherKeyId", req.otherKeyId)
                         
        Tracer.withNewContext(context){

          log.debug("RECEIVE req typeA")                
          val thirdPartyA = context.ActorOf(thirdPartyA.props)
          thirdPartyA ! req  
       
}
     

   
}
}

logback
.xml

<configuration>
    <conversionRule conversionWord="requestId" converterClass="kamon.trace.logging.LogbackTraceTokenConverter"/>
    <conversionRule conversionWord="otherKeyId" converterClass="kamon.trace.logging.LogbackTraceTokenConverter"/>
   
<conversionRule conversionWord="name" converterClass="kamon.trace.logging.LogbackTraceTokenConverter"/>

 
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                 <level>DEBUG</level>
           </filter>
              <encoder>

                      <pattern>%date{ISO8601} [%-5level] [%messageId] [%iccid] [%name] %logger{36} %X{sourceThread} - %msg%n</pattern>
               </encoder>
  </appender>

...

</configuration>

 


[%messageId] [%iccid] [%name] still output the data for traceToken instead of the value I assigned for the name and metadata of the context

I personally put the request ID in the context token, as the existing converter is implemented only for the context token.
If we want to access different data, you need a different converter class for each of your conversionWord.


Also, have you encountered this while using Kamon during startup?
[error] objc[5646]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/bin/java and /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/libinstrument.dylib. One of the two will be used. Which one is undefined.
Yes, I see that. It has to do with Aspectj.
I can recommend you to post your questions on the kamon users mailing list, they are very friendly and reactive. 


On Tuesday, May 12, 2015 at 2:30:39 PM UTC+8, Yann Simon wrote:

Ivan Topolnjak

unread,
May 12, 2015, 8:28:54 AM5/12/15
to akka...@googlegroups.com
Hello guys!

As Yann said, the only Kamon thing that "officially" can be put in your log patterns is the trace token via the provided converter, but, Kamon has something called "TraceLocal Storage" which is basically a map where you can put info and retrieve later on wherever you have access to the same TraceContext, including support for providing key/value pairs that should be available to MDC. We did not include any documentation on that feature because it's API is a bit ugly and certainly not Java-friendly and we will work on improving that soon [1], but if you want to give a try to the current implementation then take a look at the hidden docs on that [2].

Kamil Korzekwa

unread,
Jan 27, 2016, 3:19:44 AM1/27/16
to Akka User List
Hi Ivan,

Thanks for sharing it, I gave it a try. :) I wanted to propagate mdc in akka-http service all the way from the endpoint to actors and futures. Here's my current proof of concept: https://github.com/kamkor/akka-http-mdc-logging-kamon

Kamon context and mdc propagation works well to actors and futures, but is problematic in akka-http directives. However, I am akka-http and kamon noob and only gave it very little time, so hopefully with some more work I can get it to work nicely in all places. I am starting short holiday tomorrow, but I will get back to it after the weekend and try to improve my solution.  

Also please document that actor instrumentation does withMdc { } by itself. :) Would be nice if it was done in Future instrumentation too.
...
Reply all
Reply to author
Forward
0 new messages