KivaKit — Logging isn’t Special.
--
In KivaKit, the Logger interface isn’t special. It is just a Listener that handles status messages, the same as any other listener.
To learn in detail how messaging works in KivaKit, see the Medium article
Why Messaging is a Better Way to Report Status in Java.
Here is the essence of Logger:
interface Logger extends Listener
{
void log(Message message);
default void onMessage(Message message)
{
log(message);
}
}
When Logger receives a message in onMessage(), it calls log(). The log() method creates a LogEntry from the message, adds context information, and then routes the entry to one or more Logs:
class LogEntry { [...] }interface Log
{
void log(LogEntry);
}
In a Java, most of us have been using (approximately) this idiom for logging for a long time:
Logger logger = Logger.getLogger(getClass()); [...]logger.error("Failed to launch invasion of {}", planet);
A logger instance is created as a field (static or not), and methods are called on the logger to record status information during method calls.
In a KivaKit Java application, you could construct a Logger by hand, but there’s no reason to if you’re a Component (components are Repeaters, and therefore Broadcasters as well). Instead, we simply transmit status messages to any interested listener(s) like this:
class Army extends BaseComponent
{
[...] problem("Failed to launch invasion of $", planet);
}
Here, the problem() method is a convenience method provided by a super-interface (Transceiver). This method will create a Problem (which is a Message) and transmit it to whatever object(s) are listening to Army.
In our example here, CommandCenter is listening:
class CommandCenter extends BaseComponent
{
void attack()
{
var army = listenTo(new Army()); [...]
}
}class InvasionApplication extends Application
{
CommandCenter commandCenter = listenTo(new CommandCenter()); [...]
}
When CommandCenter receives our Problem message, it will repeat that message to its listeners, and those listeners will repeat it, and so on, until our message reaches the end of the listener chain. In our case, the terminal listener is a Logger instance belonging to InvasionApplication:
Army ==> CommandCenter ==> [...] ==> InvasionApplication ==> Logger
In Application.run(), the Logger listens to the application like this:
class Application extends BaseComponent
{
Logger LOGGER = LoggerFactory.newLogger(); [...] void run(String[] arguments)
{
LOGGER.listenTo(this); [...]
}
}
The logger logs the message, and that’s the end of the story.
Conclusion
Loggers in KivaKit are not special. A Logger is just a kind of Listener. We can, of course, make new Loggers, and new Logs, as in existing logging frameworks. But the key advantage in KivaKit is:
The status messages that Loggers receive can also be processed in other ways, by other kinds of Listeners.
To illustrate, here are just a few other Listeners:
Listener Purpose
------------------- ----------------------------------------------
MessageList Capture messages in a list
MutableCount Count messages
MessageAlarm Trigger an alarm if there are too many problems
StatusPanel Show status messages in a Swing panel
ValidationIssues Capture problems during validation
Converter Broadcast conversion errors
ThrowingListener Throws an exception when a problem is received
ConsoleWriter Writes status messages directly to the console
MicroservletRequest Captures microservlet request handling errors
Notes
KivaKit has a few Logs to choose from:
- ConsoleLog
- FileLog
- EmailLog
and it is easy to create new ones (see ConsoleLog to see how easy).
Loggers and Logs can be configured at runtime by defining system properties, as described here.