Error and Warning Messages

Introduction

The lulu.messages module has functions for emitting informational, warning, and fatal error messages. These messages are used throughout Lulu.

Messages can have arbitrary payloads decorated with a tag (a string like INFO, WARNING, or FATAL ERROR) and the source code location where the message was emitted.

Methods

If you have imported the module as

local msg = require 'lulu.messages'

then you have access to the following methods:

Module Function Description
msg.message(arg, ...) Prints a message to stdout without any tag but with source location information.
msg.info(arg, ...) Prints a message to stdout decorated with an “INFO” tag and source location information.
msg.warn(arg, ...) Prints a message to stdout decorated with a “WARNING” tag and source location information.
msg.fatal(arg, ...) Prints prominent fatal error message to stderr and then exits the program.
msg.source_info(offset) Returns the function name, file basename, and the line number of the corresponding Lua stack location.
: {.bordered .striped .hover .responsive tbl-colwidths=“[25,75]”}

The purpose of the first four methods above is obvious. The source_info method is discussed in more detail below.

Sample Outputs

Given a file test.lua with the following Lua code:

local msg = require 'lulu.messages'
local function test_message(x) msg.message("x = %d", x)  end
local function test_info(x)    msg.info("x = %d", x)     end
local function test_warn(x)    msg.warn("x = %d", x)     end
local function test_fatal(x)   msg.fatal("x = %d", x)    end

Adding a line test_message(42) to that file will give the output:

'test_message' (test.lua:2): x = 42

Changing that to test_info(42) yields

[INFO] from 'test_info' (test.lua:3): x = 42

Changing that to test_warn(42) yields

[WARNING] from 'test_info' (test.lua:4): x = 42

Finally, changing that to test_fatal(42) yields a more prominent message on stderr

===================================================================================================
[FATAL ERROR] from 'test_fatal' (test.lua:5): x = 42
The program will now exit ...
===================================================================================================

A program exit follows this.

The source code location is the filename, followed by a colon :, and then a line number within the file (e.g. test.lua:5) Our source_info method strips the full file path to the basename only as this is generally adequate for tracing purposes, and printing the full path often overwhelms the valuable content in the message payload.

Library Usage

If you’re writing a library, having the source code location of the error point up the stack to where the library user made an erroneous call is often helpful. While the error is detected in a library function, it is often the caller of that function you want to highlight to the user.

This is best illustrated with an example.

Suppose we have the following incredibly clever and important library function in a file called double.lua:

local messages = require("messages")
local function double(x)
    if type(x) ~= 'number' then
        messages.warn("Expected arg to be a number not a '%s'", type(x))
        return nil
    end
    return 2*x
end
return double

The library has a function that takes a number and returns the double of that number. It also checks that the argument passed to the function is indeed a number and raises a warning if it is not (we’re ignoring Lua’s ability to turn some strings into numbers here).

Here is a program main.lua that calls that function:

local putln = require('scribe').putln
local double = require('double')
local x = 42
local y = double(x)
if y then putln("2*%d = %d", x, y) end

When run, this prints the line “2*42 = 84” to your screen.

Suppose we had a typo in main.lua and replaced the line local x = 42 with local x = 'hallo'. Then, when we run the program, we will get the warning message:

[WARNING] from 'double' (double.lua:4): Expected arg to be a number not a 'string'

The message is clear, but the source code location information (double.lua:4) points to a file and line number inside our library, which is the location where the error was detected. Pointing to the source code location where the library’s caller made the error instead would be preferable.

The messages module makes this easy!

All the message methods can be passed an extra numeric first argument that tells the source_info method to grab the appropriate location information from a point further up the Lau stack.

The designer of the ambitious double library can write very slightly different code:

local messages = require("messages")
local function double(x)
    if type(x) ~= 'number' then
        messages.warn(1, "Expected arg to be a number not a '%s'", type(x))
        return nil
    end
    return 2*x
end
return double

where you should note that extra first argument in the messages.warn(1, ...) call.

Now, if the user’s code is in main.lua looks like

local putln = require('scribe').putln
local double = require('double')
local x = 'hallo'
local y = double(x)
if y then putln("2*%d = %d", x, y) end

Then, on running, the following error message appears:

[WARNING] from 'main' (main.lua:4): Expected arg to be a number not a 'string'

The payload for the warning message has not changed, but the source of the error now clearly points to the point (line 4 of main.lua) where the user called the library function incorrectly.

The module’s source_info method uses Lua’s introspection facilities, particularly debug.getinfo. That standard library function takes a stack location as its first argument.

See Also

lulu.scribe
lulu.paths

Back to top