pero on anything

More fun with LUA – Introducing Log4LUA

UPDATE Log4LUA 0.2 released. Go to the project page.

After the dust about backend connection handling in MySQL Proxy had settled, I begun working on other parts of HSCALE again. (I’ll return to the backend handling later after talking to Jan Kneschke about their plans.)

One of the issues that bothered me the most was logging. Until now I just had used a simple debug function that could be enabled or disabled using an environment variable. The pretty straightforward approach a lot of people use. But there is more to logging than to print debug messages. So I searched the web and of course I found LuaLogging. It is more flexible, has different levels (DEBUG, INFO, WARN, ERROR, FATAL) and appenders. You can print your log messages to the screen, write them to a file or send an email.

After working with other logging frameworks in other languages (mostly Log4X like Log4J) I got used to a number of features missing in LuaLogging:

  • There are no means of external configuration. The logger is configured inside the code. You cannot use different configurations for development and production out of the box. This is crucial in my opinion, since during development it is handy to enable all log levels and print everything on screen, while in production you want to log to a file and disable at least the DEBUG level.
  • You get no information where the log event came from. Log messages are more meaningful if you know where the log message has been created, i.e. in which source file and line position. This makes it easier to track down bugs and errors.
  • There is no log category concept. Having different log categories makes it easier to group log messages either by source or by meaning. As an example you could have one log category called “core” where all core messages go to and another “connection” where all connection related log messages go to.
  • You cannot use multiple appenders. In production you might want to log all messages to a file and get an email for all errors.

Since adding all this functionality to LuaLogging would in fact mean to re-write it, I wrote yet another logging facility and called it: Log4LUA. I choose the name because this package behaves almost like Log4X.

Download Log4LUA

A detailed documentation on how to use it can be found here (luadoc).


  • External configuration. Configure your logging system via a config file.
  • Logger categories Configure different categories for different logging tasks.
  • Detailed information available: source file, line, function or the whole stack trace
  • Console, file and smtp (email) appenders
  • Multiple appenders per category Log to a file and get the worst errors by email
  • Level threshold for smtp appender. Don’t get every log message by mail only the ones above a defined level.
  • Log file rotations for file appender. Based on a date pattern

Basic usage

First write a configuration file (say log4lua.conf.lua):

local logger = require("optivo.common.log4lua.logger")
local console = require("optivo.common.log4lua.appenders.console")
local file = require("optivo.common.log4lua.appenders.file")
local smtp = require("optivo.common.log4lua.appenders.smtp")

local config = {}

-- Create a default smtp appender sending message of level WARN or higher.
local defaultSmtp =
headers = {
from = "",
to = "",
subject = "%LEVEL: %MESSAGE"
body = "Hi, an error occurred at %FILE:%LINE.\n\nLevel: %LEVEL\nMessage: %MESSAGE\n"

-- ROOT category must be configured.
-- For the root category we use console and smtp appender
config["ROOT"] ={, defaultSmtp}, "ROOT", logger.INFO)

-- Category "core" uses rotating file appender and the default message pattern
config["core"] ="core-%s.log", "%Y-%m-%d"), "core", logger.INFO)

-- The config table must be returned.
return config

In your lua source (say mycode.lua) use the following to get a logger:

local logger = require("optivo.common.log4lua.logger")

-- I would prefer calling the main logger LOG.
-- This will return a logger for category "ROOT" since there is no category "mycode".
local LOG = logger.getLogger("mycode")

-- This will return a logger of the category "core" since the category name "core.moduleA" starts with "core"
local LOG_CORE = logger.getLogger("core.moduleA")

-- Log something
LOG:info("My first log message")

-- Tables are converted to string for you.
LOG:warn({name = "Paul", age = "22"})

-- Log to different category.
LOG_CORE:fatal("System error")

Start your program with:

LOG4LUA_CONFIG_FILE = "log4lua.conf.lua" lua mycode.lua

For more examples look at the HSCALE sources. Log4LUA is used everywhere.

Please feel free to comment on this. Or send usage or bug reports. I will setup a project page later on.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>