Skip to content

Print Module

The print module provides enhanced output functionality that replaces Lua's standard print function. It captures all output in a structured format, supports various data types, and integrates with the application's logging system for debugging and monitoring.

Functions

print(...)

Enhanced print function with output capture and formatting capabilities.

Parameters:

  • ... (variadic): Any number of values of any type to print

Returns:

  • None (void function)

Behavior:

  • Captures all output in a private logs array
  • Automatically formats different data types
  • Concatenates multiple arguments with spaces
  • Maintains chronological order (newest first in logs array)
  • Accessible via Lua.get_private(state, :logs) in Elixir

Basic Examples:

lua
-- Simple string output
print("Hello, World!")

-- Multiple arguments
print("User:", username, "ID:", user_id)

-- Numbers and calculations
print("Result:", 42 * 3.14159)

-- Booleans and nil
print("Success:", true, "Error:", nil)

-- Output captured in logs array:
-- ["Success: true Error: nil", "Result: 131.94678", "User: john_doe ID: 123", "Hello, World!"]

Advanced Printing Features

Table Printing

lua
-- Tables are automatically formatted
local user = {
    name = "John Doe",
    email = "john@example.com",
    roles = {"admin", "user"},
    metadata = {
        created = "2024-01-15",
        verified = true
    }
}

print("User data:", user)
-- Output: User data: {name = "John Doe", email = "john@example.com", roles = {"admin", "user"}, metadata = {created = "2024-01-15", verified = true}}

-- Pretty printing helper
function print_table(t, indent)
    indent = indent or 0
    local spacing = string.rep("  ", indent)

    if type(t) ~= "table" then
        print(spacing .. tostring(t))
        return
    end

    for k, v in pairs(t) do
        if type(v) == "table" then
            print(spacing .. k .. ":")
            print_table(v, indent + 1)
        else
            print(spacing .. k .. ": " .. tostring(v))
        end
    end
end

print_table(user)
-- Output:
-- name: John Doe
-- email: john@example.com
-- roles:
--   1: admin
--   2: user
-- metadata:
--   created: 2024-01-15
--   verified: true

Debug Printing

lua
-- Debug helper with source location
function debug_print(label, value, show_type)
    local info = debug.getinfo(2, "Sl")
    local location = info.short_src .. ":" .. info.currentline

    if show_type then
        print(string.format("[DEBUG %s] %s = %s (type: %s)",
            location, label, tostring(value), type(value)))
    else
        print(string.format("[DEBUG %s] %s = %s",
            location, label, tostring(value)))
    end
end

-- Usage
local result = calculate_something()
debug_print("calculation result", result, true)
-- Output: [DEBUG script.lua:45] calculation result = 42.5 (type: number)

Formatted Output

lua
-- Printf-style formatting
function printf(format, ...)
    print(string.format(format, ...))
end

printf("Order #%d: %s - $%.2f", order_id, customer_name, total_amount)
-- Output: Order #12345: John Doe - $99.99

-- Table formatting
function print_table_formatted(headers, rows)
    -- Calculate column widths
    local widths = {}
    for i, header in ipairs(headers) do
        widths[i] = #header
        for _, row in ipairs(rows) do
            widths[i] = math.max(widths[i], #tostring(row[i]))
        end
    end

    -- Print header
    local header_line = ""
    local separator = ""
    for i, header in ipairs(headers) do
        header_line = header_line .. string.format("%-" .. widths[i] .. "s  ", header)
        separator = separator .. string.rep("-", widths[i]) .. "  "
    end
    print(header_line)
    print(separator)

    -- Print rows
    for _, row in ipairs(rows) do
        local row_line = ""
        for i, value in ipairs(row) do
            row_line = row_line .. string.format("%-" .. widths[i] .. "s  ", tostring(value))
        end
        print(row_line)
    end
end

-- Usage
print_table_formatted(
    {"ID", "Name", "Status", "Amount"},
    {
        {1, "Order A", "Pending", "$50.00"},
        {2, "Order B", "Complete", "$125.50"},
        {3, "Order C", "Shipped", "$75.25"}
    }
)
-- Output:
-- ID  Name     Status    Amount
-- --  ------   --------  -------
-- 1   Order A  Pending   $50.00
-- 2   Order B  Complete  $125.50
-- 3   Order C  Shipped   $75.25

Logging Patterns

Progress Tracking

lua
function print_progress(current, total, label)
    local percentage = (current / total) * 100
    local bar_width = 30
    local filled = math.floor((current / total) * bar_width)
    local bar = string.rep("█", filled) .. string.rep("░", bar_width - filled)

    print(string.format("%s: [%s] %.1f%% (%d/%d)",
        label, bar, percentage, current, total))
end

-- Usage in a loop
for i = 1, 100 do
    -- Process item
    process_item(i)

    -- Print progress every 10 items
    if i % 10 == 0 then
        print_progress(i, 100, "Processing")
    end
end
-- Output examples:
-- Processing: [███░░░░░░░░░░░░░░░░░░░░░░░░░░░] 10.0% (10/100)
-- Processing: [██████░░░░░░░░░░░░░░░░░░░░░░░░] 20.0% (20/100)
-- Processing: [███████████████░░░░░░░░░░░░░░░] 50.0% (50/100)

Status Messages

lua
-- Status message helpers
function print_success(message)
    print("✅ SUCCESS: " .. message)
end

function print_error(message)
    print("❌ ERROR: " .. message)
end

function print_warning(message)
    print("⚠️  WARNING: " .. message)
end

function print_info(message)
    print("ℹ️  INFO: " .. message)
end

-- Usage
print_info("Starting batch processing")
print_success("Connected to database")
print_warning("Low memory available")
print_error("Failed to connect to API")

Timing and Performance

lua
-- Timer utility
function timed_operation(label, func)
    print("Starting: " .. label)
    local start_time = os.clock()

    local success, result = pcall(func)

    local elapsed = os.clock() - start_time

    if success then
        print(string.format("✅ %s completed in %.3f seconds", label, elapsed))
        return result
    else
        print(string.format("❌ %s failed after %.3f seconds: %s",
            label, elapsed, result))
        return nil
    end
end

-- Usage
local result = timed_operation("Database query", function()
    return database.query("main-db", "SELECT * FROM large_table", {})
end)
-- Output:
-- Starting: Database query
-- ✅ Database query completed in 0.245 seconds

Integration with Application Logging

Structured Logging

lua
function log_event(event_type, data)
    local log_entry = {
        timestamp = os.date("%Y-%m-%d %H:%M:%S"),
        event = event_type,
        data = data
    }

    -- Print for debugging
    print("EVENT:", encoding.json(log_entry))

    -- Also store in database
    database.query(
        "logs-db",
        "INSERT INTO event_log (timestamp, event_type, data_json) VALUES (?, ?, ?)",
        {log_entry.timestamp, event_type, encoding.json(data)}
    )
end

-- Usage
log_event("order_processed", {
    order_id = "12345",
    customer_id = "cust_789",
    total = 99.99,
    items_count = 3
})

Audit Trail

lua
function audit_log(action, entity_type, entity_id, changes)
    local audit_entry = string.format(
        "AUDIT: [%s] %s %s:%s | Changes: %s",
        os.date("%Y-%m-%d %H:%M:%S"),
        action,
        entity_type,
        entity_id,
        encoding.json(changes)
    )

    print(audit_entry)

    -- Store in audit log
    database.query(
        "audit-db",
        "INSERT INTO audit_log (action, entity_type, entity_id, changes, timestamp) VALUES (?, ?, ?, ?, NOW())",
        {action, entity_type, entity_id, encoding.json(changes)}
    )
end

-- Usage
audit_log("UPDATE", "order", "ord_12345", {
    status = {from = "pending", to = "processing"},
    updated_by = "user_123"
})

Best Practices

  1. Use descriptive prefixes: Make logs searchable and filterable

    lua
    print("[OrderProcessor] Starting batch processing")
    print("[OrderProcessor] Processed order:", order_id)
    print("[OrderProcessor] Batch complete:", processed_count, "orders")
  2. Include context in error messages: Aid debugging

    lua
    function safe_operation(data)
        if not data.required_field then
            print("ERROR: Missing required_field in data:", encoding.json(data))
            return false
        end
        -- Process data
    end
  3. Avoid printing sensitive data: Redact or mask sensitive information

    lua
    function print_user_info(user)
        print("User:", user.name, "Email:", mask_email(user.email))
    end
    
    function mask_email(email)
        local parts = string.match(email, "^(..).*(@.*)$")
        if parts then
            return parts[1] .. "****" .. parts[2]
        end
        return "****"
    end
  4. Implement log levels: Control verbosity

    lua
    local LOG_LEVEL = {
        ERROR = 1,
        WARN = 2,
        INFO = 3,
        DEBUG = 4
    }
    
    local current_level = LOG_LEVEL.INFO
    
    function log(level, ...)
        if level <= current_level then
            local prefix = ""
            for k, v in pairs(LOG_LEVEL) do
                if v == level then
                    prefix = "[" .. k .. "]"
                    break
                end
            end
            print(prefix, ...)
        end
    end
    
    -- Usage
    log(LOG_LEVEL.ERROR, "Critical error occurred")
    log(LOG_LEVEL.DEBUG, "Debug info:", debug_data)  -- Won't print if level is INFO

Output Capture

  • All print statements are captured in memory
  • Logs are accessible from the host application
  • Output is preserved even if script fails
  • Maximum log size is limited to prevent memory issues

Performance

  • Print operations are buffered for efficiency
  • Large data structures are truncated automatically
  • Circular buffer prevents unlimited memory growth
  • Consider using conditional logging in performance-critical sections

Connect. Combine. Collaborate.
The pioneering open integration platform, dedicated to transforming connectivity in the printing industry.