0

I am looking for a way or best/better design decision for a logging problem. I am using Akka actors in clusters for my back-end services and Play in the front-end to accept HTTP requests. My question is kind of extended from the old question of having the whole application log to be identifiable to the same HTTP requests, which simply use the MDC that exists in most current logging frameworks by generating a UUID in the beginning and put in the context.

An example of our data flow might look like:

"Http Request/System A" -> "Actor1/Cluster B" -> "Actor2/Cluster C" -> "Reply to System A and complete request"

This means there are at least 3 separate systems involved in the process. All my log goes to Logstash. I can generate a UUID from the start of the request from System A. However, I wish the UUID can be carried over/piggy-backed to all sub-systems, which all uses Protobuf serialisation to communicate with each other, processing jobs of belonged to the same http request.

I know I can always add an id field to all my messages, but this is very ugly.

I am wondering if there is a better way to or better mechanism to carry the information to all other calling Akka system without introducing too much noise to my business logic processing?

lunaspeed
  • 165
  • 1
  • 11
  • "ugly" option is the only one I'm aware of. – vvg Jan 24 '17 at 12:37
  • You could make your id generation deterministic based on the httprequest, this way all your systems will simply generate the same id when they log ? – C4stor Jan 24 '17 at 14:10
  • @C4stor I am not quite sure how that could be done. In fact, except the entry point on System A, all other actor systems receive using protobuf serialization and I guess I wasn't clear on that, sorry about that. – lunaspeed Jan 27 '17 at 06:32
  • I they receive the same payload, you can make it a hash on significant payload fields (bonus points if there's a time based field, to avoid collisions). Of course, without actual insights into your workflow, it's just a wild guess. – C4stor Jan 27 '17 at 08:47
  • @C4stor I see your point. However that won't be the case. The payloads may be different form to different services/Actor clusters which represents a microservice. – lunaspeed Jan 28 '17 at 23:58

1 Answers1

1

You can wrap your original messages into a container with UUID:

case class MessageWithUuid(message: Any, uuid: UUID)

Then, in your receive you should unwrap it and preserve uuid for logging:

var uuid: Option[UUID] = 
def receive: Receive = {
  case MessageWithUuid(message, uuid) =>
    this.uuid = Some(uuid)
    //TODO: put UUID to MDC logging context
    receive(message)
    //TODO: remove UUID from MDC
    this.uuid = None
  case ... => 
}

And whenever you send a message to another actor, you need to wrap it with UUID:

def send(actor: ActorRef, message: Any) = actor ! MessageWithUuid(message, uuid.get)
Arseniy Zhizhelev
  • 2,381
  • 17
  • 21
  • this seem similar to the "id everywhere", but indeed a better solution. however I forgot to mention that I am using Protobuf serialisation, and edited my post, which seems troubling for this kind of approach. – lunaspeed Jan 28 '17 at 08:13