Fluent API
A fluent builder interface is an API that relies heavily on method chaining to build up an expression.
The fluent API has an immediate advantage in that there’s less overloading in the API, and there’s more room to chain.
Accessing Fluent Logger
The easiest way of getting a fluent logger is to use the LoggerFactory
and then call the fluent
method:
sourceimport com.tersesystems.blindsight.fluent.FluentLogger
val logger = com.tersesystems.blindsight.LoggerFactory.getLogger(getClass)
val fluentLogger: FluentLogger = logger.fluent
The fluent logger builds up the logging statement by calling marker
, argument
, message
, cause
, and then finally log()
or logWithPlaceholders()
to execute the statement. You may call each of these methods repeatedly, and they build up markers, arguments, or a concatenated message (note that exceptions are, well, an exception). You may call them in any order. If you call log()
then the statement is executed exactly as written. If you call logWithPlaceholders()
then the number of arguments is counted and “{}” format placeholders are appended to the statement’s message so that all arguments are visible.
sourcefluentLogger.info
.marker(someMarker)
.message("some message {}")
.argument(args)
.message("exception {}")
.cause(exception)
.log()
Markers
You can log with a marker alone and then log:
sourceimport net.logstash.logback.marker.{Markers => LogstashMarkers}
import org.slf4j.Marker
val someMarker: Marker = LogstashMarkers.append("user", "will")
fluentLogger.info.marker(someMarker).log()
This will write out an empty string as the message, and a logstash marker.
All markers are in a Markers
instance internally, but are not accessible from the builder.
Message
A message is the part of the statement that is written out as a string.
fluentLogger.message("some message").log()
Messages can be concatenated together by calling message
repeatedly:
fluentLogger.message("hello").message("world").log()
You can pass in your own custom instances, of course.
case class Person(name: String)
implicit val personToMessage: ToMessage[Person] = ToMessage { person =>
Message(s"My name is ${person.name}")
}
fluentLogger.message(person).log()
Arguments
Arguments can be added with argument
. You do not need to define a message, but you should call logWithPlaceholders
in that case so the argument is visible.
fluentLogger.argument(someArgument).logWithPlaceholders()
Custom formatting is done with ToArgument
.
case class Person(name: String)
implicit val personToArgument: ToArgument[Person] = ToArgument { person =>
Argument(bobj("person" -> person.name))
}
val person = Person("Felix")
fluentLogger.message("I was talking to {}").argument(person).log()
Exceptions
Exceptions are added using cause
. Exceptions are of type Throwable
, just like the SLF4J API.
fluentLogger.message("the exception was {}").cause(someException).log()
Technically, an exception is the last argument in a logging statement. Exception does not compose, and if you call exception
repeatedly, the last exception will be set.