{"id":45782254,"url":"https://github.com/escapepz/zul","last_synced_at":"2026-03-10T12:03:14.552Z","repository":{"id":334705854,"uuid":"1139972803","full_name":"escapepz/zul","owner":"escapepz","description":"A unified logging framework for Project Zomboid mods with multi-level logging support, child loggers, and configurable sandbox options.","archived":false,"fork":false,"pushed_at":"2026-02-24T19:18:30.000Z","size":703,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-26T14:46:30.455Z","etag":null,"topics":["lua","projectzomboid","zomboid"],"latest_commit_sha":null,"homepage":"https://steamcommunity.com/sharedfiles/filedetails/?id=3653948326","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/escapepz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-22T16:56:18.000Z","updated_at":"2026-02-26T02:02:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/escapepz/zul","commit_stats":null,"previous_names":["escapepz/zul"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/escapepz/zul","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escapepz%2Fzul","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escapepz%2Fzul/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escapepz%2Fzul/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escapepz%2Fzul/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/escapepz","download_url":"https://codeload.github.com/escapepz/zul/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escapepz%2Fzul/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30332868,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T05:25:20.737Z","status":"ssl_error","status_checked_at":"2026-03-10T05:25:17.430Z","response_time":106,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lua","projectzomboid","zomboid"],"created_at":"2026-02-26T11:11:09.523Z","updated_at":"2026-03-10T12:03:14.540Z","avatar_url":"https://github.com/escapepz.png","language":"Lua","funding_links":["https://ko-fi.com/escapepz"],"categories":[],"sub_categories":[],"readme":"# ZUL - Zomboid Unified Logging\n\n[![Version](https://img.shields.io/badge/version-1.2.0-blue.svg)](https://github.com/escapepz/zul)\n[![Project Zomboid](https://img.shields.io/badge/Project%20Zomboid-42-orange.svg)](https://projectzomboid.com/)\n[![zread](https://img.shields.io/badge/Ask_Zread-_.svg?style=flat\u0026color=00b0aa\u0026labelColor=000000\u0026logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K\u0026logoColor=ffffff)](https://zread.ai/escapepz/zul)\n[![DeepWiki](https://img.shields.io/badge/DeepWiki-_.svg?style=flat\u0026color=6a0dad\u0026labelColor=000000\u0026logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI%2BPHBhdGggZD0iTTEyIDJMMiA3bDEwIDUgMTAtNS0xMC01eiIvPjxwYXRoIGQ9Ik0yIDE3bDEwIDUgMTAtNXBNMiAxMmwxMCA1IDEwLTUiLz48L3N2Zz4%3D\u0026logoColor=ffffff)](https://deepwiki.com/escapepz/zul)\n\nA unified logging framework for Project Zomboid mods with multi-level logging support, child loggers, and configurable sandbox options.\n\n## 📚 Documentation\n\n- [Quick Start Guide](docs/QUICKSTART.md) — Get started in 5 minutes\n- [Usage Examples](docs/EXAMPLES.md) — Practical code snippets and patterns\n- [Architectural Decisions](docs/ARCHITECT_ZUL.md) — Design philosophy and lifecycle details\n- [Sandbox Testing Guide](docs/SANDBOX_TESTING.md) — How to verify settings in-game\n- [Integration Test Protocol](docs/TEST.md) — Step-by-step verification flow\n- [Benchmark Results](benchmark/RESULT.md) — Performance benchmarks and stress test results\n- [Implementation Summary](docs/IMPLEMENTATION_SUMMARY.md) — Technical overview of the framework\n\n## Features\n\n- **Multi-Level Logging**: TRACE, DEBUG, INFO, WARN, ERROR, FATAL (via `writeLog`)\n- **Direct Console Logging**: `.log()` method for immediate `print()` output\n- **Sandbox Options**: Configure global log levels and mod filtering via in-game settings\n- **Server/Client Identification**: Automatic side detection in logs ([SERVER], [CLIENT], [SINGLE_PLAYER])\n- **Structured Logging**: Support for context objects and details\n- **Include/Exclude Lists**: Fine-grained control over which mods use ZUL settings\n- **Pino/Winston-inspired API**: Familiar logging patterns for JavaScript developers\n\n## Installation\n\n1. Subscribe to the mod on Steam Workshop (or download manually)\n2. Enable the mod in your Project Zomboid mod list\n3. Configure sandbox options (optional) before starting a new game\n\n## Quick Start\n\n### Basic Usage\n\n```lua\nlocal ZUL = require(\"zul\")\nlocal logger = ZUL.new(\"MyMod\")\n\n-- Simple logging\nlogger:info(\"Player entered RV\")\nlogger:warn(\"Low health detected\")\nlogger:error(\"Failed to load config\")\n\n-- Logging with context\nlogger:debug(\"Processing player data\", { playerId = 123, health = 50 })\n\n-- Structured logging with action:phase pattern\nlogger:trace(\"Database\", \"Query\", { table = \"players\", rows = 10 })\n```\n\n### Log Levels\n\nZUL supports 6 log levels (from most to least verbose):\n\n- **TRACE** (10): Very detailed debugging information\n- **DEBUG** (20): Debugging information\n- **INFO** (30): Informational messages (default)\n- **WARN** (40): Warning messages\n- **ERROR** (50): Error messages\n- **FATAL** (60): Fatal error messages\n\n### Setting Log Levels Programmatically\n\n```lua\nlocal ZUL = require(\"zul\")\n\n-- Set log level for a specific mod\nZUL.setLevel(\"MyMod\", \"DEBUG\")\nZUL.setLevel(\"AnotherMod\", ZUL.Level.TRACE)\n\n-- Using child logger\nlocal logger = ZUL.new(\"MyMod\")\nlogger:setLevel(\"WARN\") -- Only show WARN and above\n\n-- Disable logging entirely for a mod\nZUL.setLevel(\"NoisyMod\", ZUL.Level.NONE)\n```\n\n## Sandbox Options\n\nZUL provides three sandbox options for server administrators:\n\n### Global Log Level\n\nSets the default log level for all mods using ZUL. This applies to:\n\n- All mods if the Include list is empty\n- Only mods in the Include list (if specified)\n- Does NOT apply to mods in the Exclude list\n\n**Options**: TRACE, DEBUG, INFO (default), WARN, ERROR, FATAL\n\n### Include Mods\n\nComma-separated list of mod names to apply ZUL sandbox settings to.\n\n**Example**: `MyMod,AnotherMod,ThirdMod`\n\n**Behavior**:\n\n- If empty: Settings apply to ALL mods (except excluded ones)\n- If specified: Settings apply ONLY to listed mods\n\n### Exclude Mods\n\nComma-separated list of mod names to exclude from ZUL sandbox settings.\n\n**Example**: `NoisyMod,DebugMod`\n\n**Behavior**:\n\n- These mods will use their own programmatically set log levels\n- Exclude list takes precedence over Include list\n\n## Advanced Usage\n\n### Custom Log Implementation\n\nYou can override how ZUL handles level-specific logging (TRACE, DEBUG, INFO, WARN, ERROR, FATAL). Note that `.log()` bypasses this and always uses `print()`.\n\n```lua\nlocal ZUL = require(\"zul\")\n\n-- Custom log handler (e.g., send to external service)\nfunction ZUL.logImpl(modName, fullMessage)\n    -- Default behavior\n    writeLog(modName, fullMessage)\n\n    -- Custom behavior\n    sendToExternalLogger(modName, fullMessage)\nend\n```\n\n### Checking Effective Log Level\n\n```lua\nlocal logger = ZUL.new(\"MyMod\")\n\nlocal currentLevel = logger:getEffectiveLevel()\nprint(\"Current log level: \" .. currentLevel)\n\n-- Check if a specific level is enabled\nif ZUL.isLoggingEnabled(\"MyMod\", ZUL.Level.DEBUG) then\n    -- Perform expensive debug operation\n    local debugData = collectDebugInfo()\n    logger:debug(\"Debug info\", debugData)\nend\n```\n\n### Structured Logging\n\n```lua\nlocal logger = ZUL.new(\"MyMod\")\n\n-- Log with context object\nlogger:info(\"Player action\", {\n    action = \"craft\",\n    item = \"axe\",\n    duration = 5.2\n})\n\n-- Log with action:phase:details pattern\nlogger:debug(\"RV\", \"Entry\", {\n    vehicleId = \"rv_001\",\n    playerId = 123,\n    roomId = 5\n})\n```\n\n## Migration from SharedLogger\n\nIf you were using the previous `SharedLogger` module:\n\n1. Update your require statement:\n\n    ```lua\n    -- Old\n    local SharedLogger = require \"utils/SharedLogger\"\n\n    -- New\n    local ZUL = require(\"zul\")\n    ```\n\n2. Update all references from `SharedLogger` to `ZUL`:\n\n    ```lua\n    -- Old\n    local logger = SharedLogger.new(\"MyMod\")\n\n    -- New\n    local logger = ZUL.new(\"MyMod\")\n    ```\n\n3. The API is fully backward compatible - no other changes needed!\n\n## API Reference\n\n### Module Constants\n\n- `ZUL.Level` - Table containing numeric log levels:\n    - `TRACE` (10)\n    - `DEBUG` (20)\n    - `INFO` (30)\n    - `WARN` (40)\n    - `ERROR` (50)\n    - `FATAL` (60)\n    - `NONE` (100) - Disables logging for the mod\n\n### Module Functions\n\n- `ZUL.new(modName)` - Create a child logger for a mod\n- `ZUL.setLevel(modName, level)` - Set log level for a mod\n- `ZUL.getEffectiveLevel(modName)` - Get current log level for a mod\n- `ZUL.isLoggingEnabled(modName, level)` - Check if logging is enabled for a mod\n- `ZUL.shouldApplySandboxSettings(modName)` - Check if sandbox logic applies to a mod\n- `ZUL.resolveLevel(level)` - Convert level name to numeric value\n- `ZUL.loadSandboxOptions(force)` - Load/refresh sandbox options (called automatically)\n- `ZUL.serialize(val)` - Convert tables/values to strings for logging (internal helper)\n\n### Child Logger Methods\n\n- `logger:trace(message, context, details)` - Log at TRACE level\n- `logger:debug(message, context, details)` - Log at DEBUG level\n- `logger:info(message, context, details)` - Log at INFO level\n- `logger:warn(message, context, details)` - Log at WARN level\n- `logger:error(message, context, details)` - Log at ERROR level\n- `logger:fatal(message, context, details)` - Log at FATAL level\n- `logger:log(message, context, details)` - Log directly to console via `print()` (bypasses `ZUL.logImpl`)\n- `logger:setLevel(level)` - Set log level for this logger's mod\n- `logger:getEffectiveLevel()` - Get effective log level\n- `logger:isLoggingEnabled(level)` - Check if a specific level is enabled for this logger\n\n## Examples\n\n### Example 1: Basic Mod Logging\n\n```lua\nlocal ZUL = require(\"zul\")\nlocal logger = ZUL.new(\"MyAwesomeMod\")\n\nlocal function onPlayerDeath(player)\n    logger:warn(\"Player died\", {\n        username = player:getUsername(),\n        location = player:getX() .. \",\" .. player:getY()\n    })\nend\n\nEvents.OnPlayerDeath.Add(onPlayerDeath)\n```\n\n### Example 2: Conditional Debug Logging\n\n```lua\nlocal ZUL = require(\"zul\")\nlocal logger = ZUL.new(\"PerformanceMod\")\n\nlocal function expensiveOperation()\n    if ZUL.isLoggingEnabled(\"PerformanceMod\", ZUL.Level.DEBUG) then\n        local startTime = os.clock()\n        -- ... do work ...\n        local elapsed = os.clock() - startTime\n        logger:debug(\"Operation completed\", { duration = elapsed })\n    end\nend\n```\n\n### Example 3: Multi-Module Logging\n\n```lua\n-- ModuleA.lua\nlocal ZUL = require(\"zul\")\nlocal logger = ZUL.new(\"MyMod\")\n\nfunction ModuleA.init()\n    logger:info(\"Module A initialized\")\nend\n\n-- ModuleB.lua\nlocal ZUL = require(\"zul\")\nlocal logger = ZUL.new(\"MyMod\")\n\nfunction ModuleB.init()\n    logger:info(\"Module B initialized\")\nend\n\n-- Both modules share the same log level settings\n```\n\n## Initialization Lifecycle\n\nProject Zomboid's Lua environment initializes in stages. ZUL uses a multi-stage approach to ensure settings are captured as early as possible while remaining robust in multiplayer environments where sandbox settings are synchronized after boot.\n\n### Stage 1: Boot (Early)\n\nDuring `OnGameBoot`, ZUL makes its first attempt to load settings. This catches mods that initialize very early. In Multiplayer, this will likely use local/default settings.\n\n### Stage 2: Sync (Mid)\n\nZUL hooks into `OnInitGlobalModData`, `OnInitWorld`, `OnGameStart`, and `OnServerStarted` with a **force refresh** flag.\n\n- As soon as the server sends the synchronized `SandboxVars` packet, ZUL will update its internal configuration.\n- You will see `ZUL sandbox settings refreshed (Server Sync)` in the TRACE logs when this occurs.\n\n### Effect on Early Logging\n\nMods that log _during_ the early boot sequence (before Stage 2) may use the framework's default levels (`INFO`) or local settings until the server sync completes. Once synchronized, all subsequent logs will respect the server's sandbox configuration.\n\n## Support\n\n- **Ko-fi**: [https://ko-fi.com/escapepz](https://ko-fi.com/escapepz)\n- **GitHub**: [https://github.com/escapepz](https://github.com/escapepz)\n\n## License\n\nCreated by eScape (@escapepz)\n\n## Changelog\n\n### Version 1.2.0 Breaking Changes\n\n- Update folder structure to match new game version (42.14).\n- Rename files to lowercase for cross-platform compatibility. Now use require(\"zul\") instead, **NOT** \"ZUL\"\n\n### Version 1.1.0\n\n- **Multiplayer Sync Fix**: Implemented staged initialization with forced re-syncs to handle server Sandbox synchronization lag.\n- **Improved Logging**: Added state-aware initialization logs (First-time vs Refresh).\n- **Hardened Events**: Added `OnGameStart` and `OnServerStarted` as final initialization safety nets.\n- **Enhanced Guarding**: Improved state management during the complex PZ startup sequence.\n\n### Version 1.0.0\n\n- Initial release\n- Multi-level logging support\n- Child logger API\n- Sandbox options integration\n- Include/exclude mod filtering\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fescapepz%2Fzul","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fescapepz%2Fzul","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fescapepz%2Fzul/lists"}