2016-05-31 4 views
0

Ich habe eine Java Akka Anwendung und ich möchte für jede Nachricht einen separaten MDC Kontext setzen auf Informationen in der Nachricht basierend Handhabung, zum Beispiel habe ich die folgende Basis-Interface für alle Nachrichten:Schauspieler MDC Kontext in aroundReceive Methode

public interface IdMessage { 
    String getId(); 
} 

auch habe ich die folgende Basis Schauspieler für alle Akteure:

public class BaseActor extends AbstractActor { 

    private final DiagnosticLoggingAdapter log = Logging.apply(this); 

    @Override 
    public void aroundReceive(PartialFunction<Object, BoxedUnit> receive, Object msg) { 
     if (msg instanceof IdMessage) { 
      final Map<String, Object> originalMDC = log.getMDC(); 
      log.setMDC(ImmutableMap.of("id", ((IdMessage) msg).getId())); 
      try { 
       super.aroundReceive(receive, msg); 
      } finally { 
       if (originalMDC != null) { 
        log.setMDC(originalMDC); 
       } else { 
        log.clearMDC(); 
       } 
      } 
     } else { 
      super.aroundReceive(receive, msg); 
     } 
    } 
} 

Und die eigentliche Schauspieler Umsetzung:

public class SomeActor extends BaseActor { 
    SomeActor() { 
    receive(ReceiveBuilder 
        .match(SomeMessage.class, message -> { 
         ... 
        } 
    } 
} 

Ich möchte sicherstellen, dass alle Log-Einträge innerhalb SomeActor#receive() MDC-Kontext in der BaseActor gesetzt haben. Um dies zu tun SomeActor#receice() müssen im selben Thread wie BaseActor#aroundReceive() Methode ausgeführt werden.

Ich habe keine Informationen über das Verhalten von aroundReceive gefunden - wird das immer im selben Thread wie die tatsächliche receive Methode ausgeführt werden? Basierend auf meinen Tests wird es immer im selben Thread ausgeführt.

Antwort

1

Ich war in der Lage, die richtige Implementierung von mir selbst herauszufinden und möchte es teilen, falls jemand mit dem gleichen Problem konfrontiert wird.

Die aroundReceive wird im selben Thread wie receive ausgeführt werden, so ist dies der richtige Ort, um MDC-Kontext zu setzen.

I org.slf4j.MDC zur Einstellung des MDC Kontext verwendet, hier ist der vollständige Code:

import java.util.Map; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.slf4j.MDC; 

import com.google.common.collect.ImmutableMap; 

import akka.actor.AbstractActor; 
import scala.PartialFunction; 
import scala.runtime.BoxedUnit; 

public class BaseActor extends AbstractActor { 

    private final Logger log = LoggerFactory.getLogger(BaseActor.class); 

    @Override 
    public void aroundReceive(PartialFunction<Object, BoxedUnit> receive, Object msg) { 
     if (msg instanceof IdMessage) { 
      final Map<String, Object> originalMDC = log.getMDC(); 
      MDC.setContextMap(ImmutableMap.of("id", ((IdMessage) msg).getId())); 
      try { 
       super.aroundReceive(receive, msg); 
      } finally { 
       if (originalMDC != null) { 
        MDC.setContextMap(originalMDC); 
       } else { 
        MDC.clear(); 
       } 
      } 
     } else { 
      super.aroundReceive(receive, msg); 
     } 
    } 
} 

Mit dieser Implementierung BaseActor alle Log-Einträge in receive mit einem geeigneten MDC Kontext angemeldet sind. Weitere Informationen finden Sie in diesem interessanten Blog post (mit Scala-Implementierung).

Hinweis: Ich konnte die gleiche Funktionalität nicht mit Akka DiagnosticLoggingAdapter erreichen, obwohl es Methoden zum Festlegen des MDC-Kontextes gibt.

+0

Die Gründe, warum 'DiagnosticLoggingAdapter' nicht den Trick macht, werden in [diese Antwort] erklärt (http://stackoverflow.com/a/31237679/843660). Wie beim Überschreiben von "aroundReceive" wird dies als interne API markiert und funktioniert nur, wenn Sie Ihre Klasse in das Paket "akka.actor" stellen, was ein Hack ist. Siehe [diese Antwort] (http://stackoverflow.com/a/26723452/843660). Ich bin mir nicht sicher, was man in Java machen kann. – dskrvk