Skip to main content

Overview

The logger global provides formatted logging functions for debugging and monitoring your scripts. All logging uses the fmt library for powerful string formatting.

Log Levels

GTProxy supports four log levels:
logger.info
function
Log informational messages (default level)
logger.info("Server connected")
logger.debug
function
Log debug information (only visible in debug builds)
logger.debug("Packet size: {} bytes", size)
logger.warn
function
Log warning messages
logger.warn("Invalid packet received")
logger.error
function
Log error messages
logger.error("Failed to connect: {}", error_msg)

Formatting

All logger functions support fmt-style formatting using {} placeholders:

Basic Formatting

local player_name = "Player123"
logger.info("Player {} joined", player_name)
-- Output: Player Player123 joined

Multiple Arguments

local x, y = 10.5, 20.3
logger.info("Position: ({}, {})", x, y)
-- Output: Position: (10.5, 20.3)

Mixed Types

local count = 5
local name = "items"
local active = true

logger.info("Found {} {} (active: {})", count, name, active)
-- Output: Found 5 items (active: true)

Complex Data

local player = world:get_local_player()
if player then
    logger.info("Player {}: {} at ({}, {})",
        player.net_id,
        player.name,
        player.position.x,
        player.position.y
    )
end
-- Output: Player 12345: MyName at (50, 30)

Examples

Event Logging

event.on("server:Connect", function(ctx)
    logger.info("Connected to game server")
end)

event.on("server:Disconnect", function(ctx)
    logger.warn("Disconnected from game server")
end)

Packet Debugging

event.on("server:SendToServer", function(ctx)
    if ctx:has_packet() then
        local pkt = ctx:parse()
        logger.debug("Outgoing packet type: {}", pkt.type)
    else
        local data = ctx:get_data()
        logger.debug("Raw packet: {} bytes", #data)
    end
end)

Command Feedback

command.register("test", "Test command", function(ctx)
    logger.info("Test command executed by user")
    
    if #ctx.args > 0 then
        logger.debug("Arguments: {}", table.concat(ctx.args, ", "))
    else
        logger.warn("No arguments provided")
    end
    
    return true
end)

Error Handling

command.register("spawn", "Spawn item", function(ctx)
    if #ctx.args < 1 then
        logger.error("Usage: /spawn <item_id>")
        return false
    end
    
    local item_id = tonumber(ctx.args[1])
    if not item_id then
        logger.error("Invalid item ID: {}", ctx.args[1])
        return false
    end
    
    logger.info("Spawning item {}", item_id)
    return true
end)

Scheduler Logging

local tick_count = 0

scheduler.schedule_periodic(1000, function()
    tick_count = tick_count + 1
    logger.info("Tick #{}", tick_count)
    
    if tick_count >= 10 then
        logger.info("Stopping after {} ticks", tick_count)
        return false
    end
    
    return true
end)

Best Practices

Use appropriate log levels
  • info - General information about script operation
  • debug - Detailed diagnostic information
  • warn - Warning about potential issues
  • error - Error conditions that prevent normal operation
Format strings are more readable than concatenation
-- Good
logger.info("Player {} at position ({}, {})", name, x, y)

-- Harder to read
logger.info("Player " .. name .. " at position (" .. x .. ", " .. y .. ")")
Avoid logging sensitive informationDon’t log passwords, tokens, or other sensitive data:
-- Bad - logs password
logger.info("Login: user={}, pass={}", username, password)

-- Good - omit sensitive data
logger.info("Login attempt: user={}", username)

See Also

Events API

Use logger with event handlers

Commands API

Log command execution