Module:ErrorHandling
Appearance
Documentation for this module may be created at Module:ErrorHandling/doc
--[[
* Name: ErrorHandling
* Author: Mark W. Datysgeld
* Description: Centralized error handling system for templates with error tracking, reporting, and graceful failure recovery
* Notes: Formats errors as data attributes for debug monitor display in browser console; creates error contexts; provides protected function execution; supports emergency fallback displays; completely deprecates mw.log usage
]]
local ErrorHandling = {}
-- Standard error messages for common scenarios
ErrorHandling.STANDARD_MESSAGES = {
TEMPLATE_RENDER_ERROR = "<!-- Error rendering template -->",
MODULE_LOAD_ERROR = "<!-- Error loading module -->",
PROPERTY_ERROR = "<!-- Error processing semantic properties -->"
-- Add more standard messages as needed
}
-- Creates a new error context for a template
-- @param templateName The name of the template (for error reporting)
-- @return A new error context table
function ErrorHandling.createContext(templateName)
return {
TEMPLATE_NAME = templateName or "UnknownTemplate",
START_TIME = os.clock() * 1000, -- Store in milliseconds
ERROR_COUNT = 0,
STATUS_COUNT = 0,
ERRORS = {},
STATUSES = {},
HAS_CRITICAL_ERROR = false
}
end
-- Add an error to the context
-- @param context The error context
-- @param source The source of the error (function name)
-- @param message The error message
-- @param details Optional additional details about the error
-- @param isCritical Whether this is a critical error (default: false)
-- @return The error context (for chaining)
function ErrorHandling.addError(context, source, message, details, isCritical)
-- Increment the error count
context.ERROR_COUNT = context.ERROR_COUNT + 1
-- Add the error to the list
table.insert(context.ERRORS, {
id = context.ERROR_COUNT,
source = source or "unknown",
message = message or "Unknown error",
details = details or "",
isCritical = isCritical or false
})
-- Update critical error flag if needed
if isCritical then
context.HAS_CRITICAL_ERROR = true
end
-- Return the error context for chaining
return context
end
-- Add a status message to the context
-- @param context The error context
-- @param source The source of the status message (function name)
-- @param message The status message
-- @param details Optional additional details
-- @return The context (for chaining)
function ErrorHandling.addStatus(context, source, message, details)
-- Increment the status count
context.STATUS_COUNT = (context.STATUS_COUNT or 0) + 1
-- Add the status to the list
table.insert(context.STATUSES, {
id = context.STATUS_COUNT,
source = source or "unknown",
message = message or "Unknown status",
details = details or ""
})
-- Return the context for chaining
return context
end
-- Format the error context for debugging output using data attributes
-- @param context The error context
-- @return HTML string with data attributes containing error information
function ErrorHandling.formatOutput(context)
-- If no errors, return empty string
if context.ERROR_COUNT == 0 then
return ""
end
-- Build minimal data attribute div
local divAttributes = {
['data-template-error'] = "1",
['data-error-count'] = tostring(context.ERROR_COUNT)
}
-- Add individual error attributes with minimal naming
for _, err in ipairs(context.ERRORS) do
divAttributes['data-error-' .. err.id .. '-source'] = err.source
divAttributes['data-error-' .. err.id .. '-msg'] = err.message
if err.details and err.details ~= "" then
divAttributes['data-error-' .. err.id .. '-details'] = err.details
end
end
-- Build attribute string efficiently
local attrStr = ""
for k, v in pairs(divAttributes) do
attrStr = attrStr .. ' ' .. k .. '="' .. v .. '"'
end
-- Create hidden div with minimal footprint
return string.format('<div style="display:none"%s></div>', attrStr)
end
-- Format the status context for debugging output using data attributes
-- @param context The error context
-- @return HTML string with data attributes containing status information
function ErrorHandling.formatStatusOutput(context)
-- If no statuses, return empty string
if not context.STATUSES or #context.STATUSES == 0 then
return ""
end
-- Build minimal data attribute div
local divAttributes = {
['data-template-status'] = "1",
['data-status-count'] = tostring(#context.STATUSES)
}
-- Add individual status attributes with minimal naming
for _, stat in ipairs(context.STATUSES) do
divAttributes['data-status-' .. stat.id .. '-source'] = stat.source
divAttributes['data-status-' .. stat.id .. '-msg'] = stat.message
if stat.details and stat.details ~= "" then
divAttributes['data-status-' .. stat.id .. '-details'] = stat.details
end
end
-- Build attribute string efficiently
local attrStr = ""
for k, v in pairs(divAttributes) do
attrStr = attrStr .. ' ' .. k .. '="' .. v .. '"'
end
-- Create hidden div with minimal footprint
return string.format('<div style="display:none"%s></div>', attrStr)
end
-- Formats and combines both error and status outputs
-- @param context The context object
-- @return A string containing HTML for both errors and statuses
function ErrorHandling.formatCombinedOutput(context)
local errorOutput = ErrorHandling.formatOutput(context)
local statusOutput = ErrorHandling.formatStatusOutput(context)
-- Simply concatenate the two outputs. If one is empty, it won't affect the other.
-- A newline is added to ensure they are on separate lines in the HTML source.
if errorOutput ~= "" and statusOutput ~= "" then
return errorOutput .. "\n" .. statusOutput
elseif errorOutput ~= "" then
return errorOutput
else
return statusOutput
end
end
-- Create emergency display for catastrophic failures with minimal markup
-- @param args The template arguments
-- @param errorSource The source of the error
-- @param errorMessage The error message
-- @param templateType The type of template (e.g. "Event", "Person")
-- @return HTML string with a minimal template display and error data
function ErrorHandling.createEmergencyDisplay(args, errorSource, errorMessage, templateType)
-- Extract critical information for minimal display
local title = args.name or ("Unnamed " .. (templateType or "Item"))
-- Create minimal fallback with error data attributes
return string.format(
'{| class="template-table" cellpadding="2"\n' ..
'|-\n| class="template-title template-title-%s" | <span>%s</span>\n' ..
'|}\n' ..
'<div style="display:none" data-template-error="1" data-critical="1" ' ..
'data-error-1-source="%s" data-error-1-msg="%s"></div>',
string.lower(templateType or "generic"),
title,
errorSource,
errorMessage
)
end
-- Protect a function with pcall and error handling
-- @param context The error context
-- @param funcName Name of the function (for error reporting)
-- @param func The function to protect
-- @param fallback Value to return if the function fails
-- @param ... Arguments to pass to the function
-- @return The result of the function, or fallback value on error
function ErrorHandling.protect(context, funcName, func, fallback, ...)
local success, result = pcall(func, ...)
if not success then
ErrorHandling.addError(
context,
funcName,
"Function execution failed",
tostring(result),
false
)
return fallback
end
return result
end
-- Get a standard error message with fallback
-- @param messageKey The key of the standard message to retrieve
-- @param defaultMessage Optional fallback message if the key is not found
-- @return The standard message or the default message
function ErrorHandling.getMessage(messageKey, defaultMessage)
if ErrorHandling.STANDARD_MESSAGES and ErrorHandling.STANDARD_MESSAGES[messageKey] then
return ErrorHandling.STANDARD_MESSAGES[messageKey]
end
return defaultMessage or "<!-- Error -->"
end
-- Protected module loading
-- @param context The error context
-- @param moduleName Name of the module to load
-- @param isCritical Whether failure to load is critical (default: false)
-- @return The loaded module, or an empty table on failure
function ErrorHandling.safeRequire(context, moduleName, isCritical)
local success, module = pcall(require, moduleName)
if not success then
-- Record the error
ErrorHandling.addError(
context,
"ModuleLoading",
"Failed to load module",
moduleName,
isCritical or false
)
-- Return an empty table as fallback
return {}
end
return module
end
return ErrorHandling