Jump to content

Module:ErrorHandling: Difference between revisions

// via Wikitext Extension for VSCode
Tag: Reverted
// via Wikitext Extension for VSCode
Tag: Manual revert
Line 10: Line 10:
     PROPERTY_ERROR = "<!-- Error processing semantic properties -->"
     PROPERTY_ERROR = "<!-- Error processing semantic properties -->"
     -- Add more standard messages as needed
     -- Add more standard messages as needed
}
-- Add configuration options
ErrorHandling.CONFIG = {
    STACK_TRACE_ENABLED = true,    -- Enable/disable stack traces completely
    TRACE_VERBOSITY = "detailed",  -- "minimal", "standard", or "detailed"
    INCLUDE_LINE_NUMBERS = true,    -- Include line numbers in call chains
    MAX_TRACE_DEPTH = 10            -- Maximum number of stack frames to include
}
}


Line 81: Line 73:
         divAttributes['data-error-' .. err.id .. '-source'] = err.source
         divAttributes['data-error-' .. err.id .. '-source'] = err.source
         divAttributes['data-error-' .. err.id .. '-msg'] = err.message
         divAttributes['data-error-' .. err.id .. '-msg'] = err.message
       
        -- Add more structured details with stack trace information
         if err.details and err.details ~= "" then
         if err.details and err.details ~= "" then
             divAttributes['data-error-' .. err.id .. '-details'] = err.details
             divAttributes['data-error-' .. err.id .. '-details'] = err.details
           
            -- New: Add stack trace as separate attribute for developer tools
            if err.details:match("stack traceback:") then
                divAttributes['data-error-' .. err.id .. '-stack'] = "1"
               
                -- Extract function names from stack trace for better debugging
                local functions = {}
                for fnName in err.details:gmatch("in function '([^']+)'") do
                    table.insert(functions, fnName)
                end
               
                if #functions > 0 then
                    divAttributes['data-error-' .. err.id .. '-call-chain'] =
                        table.concat(functions, " → ")
                end
            end
         end
         end
     end
     end
Line 146: Line 120:
-- @return The result of the function, or fallback value on error
-- @return The result of the function, or fallback value on error
function ErrorHandling.protect(context, funcName, func, fallback, ...)
function ErrorHandling.protect(context, funcName, func, fallback, ...)
    local args = {...}
     local success, result = pcall(func, ...)
     local success, result = xpcall(
        function() return func(unpack(args)) end,
        debug.traceback
    )
      
      
     if not success then
     if not success then
        local errorMessage = "Function execution failed"
        local stackTrace = result -- xpcall returns the traceback as the result
       
        -- Extract the error message from the first line of the stack trace
        local firstLine = stackTrace:match("^([^\n]+)")
       
         ErrorHandling.addError(
         ErrorHandling.addError(
             context,
             context,
             funcName,
             funcName,
             errorMessage,
             "Function execution failed",
             stackTrace, -- Use full stack trace as details
             tostring(result),
             false
             false
         )
         )

Revision as of 23:11, 7 June 2025

Documentation for this module may be created at Module:ErrorHandling/doc

-- Module:ErrorHandling
-- A centralized error handling system for templates that provides error tracking, reporting, and graceful failure recovery. It formats errors as data attributes for the debug monitor to display in the browser console.

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,
        ERRORS = {},
        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

-- 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

-- 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