Search code examples
javalogstashslf4jlogstash-logback-encoder

Logstash logback: combine multiple structured arguments in log message


In our domain we provide structured arguments to a log message in order to describe an object ID. If for instance an object SomeObject has three ID fields used to identify the object:


class SomeObject {
  SomeObjectId id;
  ....
}

class SomeObjectId {
  LocalDate date;
  int objectId;
  String objectLocation;
}

We use the following log set up to log all ID fields:

import static net.logstash.logback.argumentStructuredArgument.v;

log.info("Some object with ID {} {} {} is processed on {}.",
  v("date", objectId.getDate()),
  v("objectId", objectId.getObjectId()),
  v("location", objectId.getObjectLocation()),
  "someOtherArgument" // etc
);

Which generates the following JSON log output:

{
  "@timestamp": "2022-11-30T12:34:56.000+00:00",
  "@version": "1",
  "message": "Some object with ID 2022-11-30 123 NL is processed on someOtherArgument",
  "thread_name": "main",
  "level": "INFO",

  // Notice the structured arguments
  "date": "2022-11-30",
  "objectId": "123",
  "location": "NL"
}

It feels a bit cumbersome to explicitly mention every ID field at every log section in the code. Is there a way to combine the fields into the log string while at the same time having all the structured arguments added to the log message? For instance (in pseudo code) :

log.info("Some object with ID {} is processed on {}.",
  LogTags.fromId(someObjectId),
  "someOtherArgument" // etc
);

class LogTags {
  static String fromId(SomeObjectId id) {
     LogTags.forCurrentLogMessage.appendValue("date", id.getDate());
     LogTags.forCurrentLogMessage.appendValue("objectId", id.getObjectId());
     LogTags.forCurrentLogMessage.appendValue("location", id.getLocation());

     return id.getDate() + " " + id.getObjectId() + " " + id.getLocation();
  }
}


Solution

  • Yes, use StructuredArguments.fields(object) (or its abbreviated form StructuredArguments.f(object))

    For example:

    class SomeObjectId {
      LocalDate date;
      int objectId;
      String objectLocation;
    
      public String toString() {
        return date + " " + objectId + " " + location;
      }
    }
    
    
    log.info("Some object with ID {} is processed on {}.",
      StructuredArguments.fields(someObjectId),
      "someOtherArgument" // etc
    );