1162 lines
44 KiB
Lua
1162 lines
44 KiB
Lua
--[[
|
|
DebugLib - An embedded library which works as a higher layer for nLog,
|
|
by providing easier usage of debugging features.
|
|
Version: 5.9.4961 (WhackyWallaby)
|
|
Revision: $Id: DebugLib.lua 275 2010-10-03 14:00:39Z kandoko $
|
|
URL: http://auctioneeraddon.com/dl/
|
|
|
|
Manual:
|
|
This manual is a basic introduction to this library and gives examples
|
|
about how to use it. For a more detailed description, refer to each
|
|
function's documentation.
|
|
|
|
>>>What the library is designed for<<<
|
|
DebugLib is designed to help developers to add structured error handling
|
|
to their code. That is being done by providing developers with three
|
|
new functions: DebugPrint(), Assert() and Dump().
|
|
The library was designed with the idea in mind that each function
|
|
returns two new error values: An error code and a descriptive error
|
|
message. These two additional values could then be used by the caller to
|
|
check and, in case an error occured, handle the function's outcome.
|
|
|
|
Even if the developer does not use this feature, each error will be
|
|
recorded using nLog. The benefit of DebugLib is that it's working even
|
|
without nLog being installed at all (which is most likely the case for
|
|
any user of your addon, since he does not want being bothered with
|
|
having to install a debug addon, he does not need at all).
|
|
On the other side developers can install nLog and at once have access to
|
|
all the debug messages without having to change anything in the code.
|
|
If you want to know more about nLog, please refer to the nLog
|
|
documentation and www.auctioneeraddon.com.
|
|
|
|
>>>Installation Requirements<<<
|
|
Any addon which uses debugLib should add nLog as an optional dependancy.
|
|
That way it is made sure that nLog is being loaded before debugLib is so
|
|
debugLib's initialization code works as intended.
|
|
|
|
>>>Integrating the Template Functions in an Addon<<<
|
|
This library provides 2 template functions. These are designed to get
|
|
local counterparts in an addon and should not be used directly.
|
|
Instead the suggestion is to define local functions in your addon equal
|
|
to the following reference implementation:
|
|
|
|
METHOD 1:
|
|
local addonName = "MyAddon"
|
|
local DebugLib = LibStub("DebugLib")
|
|
|
|
local function debugPrint(message, category, title, errorCode, level, ...)
|
|
return DebugLib.DebugPrint(addonName, message, category, title, errorCode, level, ...)
|
|
end
|
|
|
|
local function assert(test, ...)
|
|
return DebugLib.Assert(addonName, test, ...)
|
|
end
|
|
|
|
METHOD 2:
|
|
local DebugLib = LibStub("DebugLib")
|
|
local debug, assert = DebugLib("MyAddon")
|
|
|
|
debug(message, category, title, errorCode, level, ...)
|
|
assert(test, ...)
|
|
|
|
Refer to the assert() and debugPrint() functions in this file to see a
|
|
more detailed example.
|
|
|
|
>>>Common Syntax/Usage of Main Functions<<<
|
|
There are 3 main functions provided by this library:
|
|
DebugLib.DebugPrint(), DebugLib.Assert() and DebugLib.Dump()
|
|
The following examples show how these functions could be used in your own
|
|
addon. For these examples it is expected that your addon provides the
|
|
local counterparts for DebugLib.DebugPrint() and DebugLib.Assert() as
|
|
described above.
|
|
|
|
debugPrint("An error occured while processing the data.",
|
|
"data processor", 5)
|
|
This is the normal usage for defined errors using debugPrint.
|
|
|
|
debugPrint("Defaulting the parameters...",
|
|
"scan", "Defaulting", DebugLib.Level.Notice)
|
|
This generates a notice message.
|
|
|
|
assert(v < 5, "The given value is too big.")
|
|
Simple usage of the assert function.
|
|
|
|
debugPrint("Corrupt tempTable: "..DebugLib.Dump(tempTable),
|
|
"general", 5)
|
|
Creating an error message and dumping the content of a table.
|
|
|
|
if type(a) == "string" then
|
|
return debugPrint("The given parameter must not be a string.",
|
|
"testPattern()", 22)
|
|
end
|
|
This demonstrates the usage debugPrints()'s return value. In this case
|
|
the function returns 22, "The given parameter must not be a string."
|
|
which the calling function can use to handle the error.
|
|
|
|
if not assert(isValidParameter(a1), "a1 is invalid abording...") then
|
|
return
|
|
end
|
|
Example usage of the assert return value.
|
|
|
|
For a more detailed description of possible syntaxes for these functions
|
|
refer to the specific function's description.
|
|
|
|
License:
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program(see GPL.txt); if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
Note:
|
|
This AddOn's source code is specifically designed to work with
|
|
World of Warcraft's interpreted AddOn system.
|
|
You have an implicit license to use this AddOn with these facilities
|
|
since that is its designated purpose as per:
|
|
http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
|
|
]]
|
|
|
|
local LIBRARY_VERSION_MAJOR = "DebugLib"
|
|
local LIBRARY_VERSION_MINOR = 1
|
|
|
|
--[[-----------------------------------------------------------------
|
|
|
|
LibStub is a simple versioning stub meant for use in Libraries.
|
|
See <http://www.wowwiki.com/LibStub> for more info.
|
|
LibStub is hereby placed in the Public Domain.
|
|
Credits:
|
|
Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
|
|
|
|
--]]-----------------------------------------------------------------
|
|
do
|
|
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2
|
|
local LibStub = _G[LIBSTUB_MAJOR]
|
|
|
|
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
|
|
LibStub = LibStub or {libs = {}, minors = {} }
|
|
_G[LIBSTUB_MAJOR] = LibStub
|
|
LibStub.minor = LIBSTUB_MINOR
|
|
|
|
function LibStub:NewLibrary(major, minor)
|
|
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
|
|
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
|
|
|
|
local oldminor = self.minors[major]
|
|
if oldminor and oldminor >= minor then return nil end
|
|
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
|
|
return self.libs[major], oldminor
|
|
end
|
|
|
|
function LibStub:GetLibrary(major, silent)
|
|
if not self.libs[major] and not silent then
|
|
error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
|
|
end
|
|
return self.libs[major], self.minors[major]
|
|
end
|
|
|
|
function LibStub:IterateLibraries() return pairs(self.libs) end
|
|
setmetatable(LibStub, { __call = LibStub.GetLibrary })
|
|
end
|
|
end
|
|
--[End of LibStub]---------------------------------------------------
|
|
|
|
local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR)
|
|
if not lib then return end
|
|
|
|
LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/libs/trunk/DebugLib/DebugLib.lua $","$Rev: 275 $","5.1.DEV.", 'auctioneer', 'libs')
|
|
|
|
if not lib.private then
|
|
lib.private = {}
|
|
end
|
|
local private = lib.private
|
|
|
|
local debug
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Error Codes
|
|
-------------------------------------------------------------------------------
|
|
-- 1 = invalid argument (at least one of the arguments passed to the function
|
|
-- is invalid)
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Enumerations
|
|
-------------------------------------------------------------------------------
|
|
-- Lookup list of all nLog levels. It should correspond with the list in nLog.
|
|
if not nLog then
|
|
private.levelLookupList = {
|
|
["Critical"] = 1,
|
|
["Error"] = 2,
|
|
["Warning"] = 3,
|
|
["Notice"] = 4,
|
|
["Info"] = 5,
|
|
["Debug"] = 6
|
|
}
|
|
else
|
|
-- if nLog exists, we can use its list to make 100% sure, that the content
|
|
-- is the same
|
|
private.levelLookupList = {}
|
|
for index, levelString in ipairs(nLog.levels) do
|
|
private.levelLookupList[levelString] = index
|
|
end
|
|
end
|
|
|
|
-- The different supported debug levels.
|
|
private.levelList = {
|
|
-- Critical = "Critical",
|
|
-- Error = "Error",
|
|
-- Warning = "Warning",
|
|
-- Notice = "Notice",
|
|
-- Info = "Info",
|
|
-- Debug = "Debug"
|
|
}
|
|
for stringLevel in pairs(private.levelLookupList) do
|
|
private.levelList[stringLevel] = stringLevel
|
|
end
|
|
|
|
lib.Level = private.levelList
|
|
|
|
|
|
-- these variables are to limit the dump() function to prevent crashes, hour long waits, etc.
|
|
|
|
-- Complex tables will cause dump() to overflow the stack, and trigger a Lua error
|
|
-- so we limit recursion
|
|
private.dumpCurrentRecursionDepth = 0
|
|
private.dumpRecursionLimit = 20
|
|
|
|
-- Sometimes, the output string gets so large that it crashes WoW
|
|
-- so we need to stop the string before it gets too large
|
|
private.dumpStringLimit = 4*1024*1024
|
|
|
|
-- And we need to limit the length of tables dumped to prevent WoW from spinning it's wheels
|
|
private.dumpTableLengthLimit = 100
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Function definitions
|
|
-------------------------------------------------------------------------------
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Prints the specified message to nLog.
|
|
-- This is the version used for lib.DebugPrint, if nLog is installed and
|
|
-- enabled.
|
|
--
|
|
-- syntax:
|
|
-- errorCode, message = lib.DebugPrint(addon[, message][, category][, title][, errorCode][, level] |
|
|
-- addon, message, category, title, errorCode, level, ...)
|
|
--
|
|
-- parameters:
|
|
-- addon - (string) the name of the addon
|
|
-- message - (string) the error message
|
|
-- nil, no error message specified
|
|
-- category - (string) the category of the debug message
|
|
-- nil, no category specified
|
|
-- title - (string) the title for the debug message
|
|
-- nil, no title specified
|
|
-- errorCode - (number) the error code
|
|
-- nil, no error code specified
|
|
-- level - (string) nLog message level
|
|
-- Any nLog.levels string is valid.
|
|
-- nil, no level specified
|
|
-- ... - (any) additional data which will be appended to the error
|
|
-- message in nLog
|
|
--
|
|
-- returns:
|
|
-- errorCode - (number) errorCode, if one is specified
|
|
-- nil, otherwise
|
|
-- message - (string) message, if one is specified
|
|
-- nil, otherwise
|
|
--
|
|
-- remarks:
|
|
-- >>>EXAMPLES<<<
|
|
-- Here are examples of all valid syntaxes for this function. The list is
|
|
-- ordered by how common the usage of the specific syntax is.
|
|
-- 1) Common usage to quickly create debug messages in your working copy:
|
|
-- lib.DebugPrint("DebugLib", "Entered the function.")
|
|
-- This results in the message being written as a debug message to
|
|
-- nLog and, if enabled in nLog, to the chat channel as well. These
|
|
-- message types are normally used for local debugging, only.
|
|
--
|
|
-- 2) Complete specified error entry with default title:
|
|
-- lib.DebugPrint("DebugLib", "Error while processing the frame.",
|
|
-- "UI", 6)
|
|
-- This is the preferred syntax for specifying errors, if you don't
|
|
-- want to specify your own title.
|
|
--
|
|
-- 3) Complete specified error entry with specified title:
|
|
-- lib.DebugPrint("DebugLib", "Error while processing the frame.",
|
|
-- "UI", "Process error", 6)
|
|
-- This is the preferred syntax for specifying errors, if you want to
|
|
-- specify your own title.
|
|
--
|
|
-- 4) Complete specified debug warning entry with a title:
|
|
-- lib.DebugPrint("DebugLib", "Entering failsafe mode.", "Backend",
|
|
-- "Failsafe", DebugLib.Level.Warning)
|
|
-- This is the preferred syntax for any message type except errors.
|
|
--
|
|
-- 5) Complete specified debug warning entry with default title:
|
|
-- lib.DebugPrint("DebugLib", "Entering failsafe mode.", "Backend",
|
|
-- DebugLib.Level.Warning)
|
|
-- This is another syntax for any message type except errors, though
|
|
-- this time the default title is used.
|
|
--
|
|
-- 6) Fully specified debug message with default title:
|
|
-- lib.DebugPrint("DebugLib", "Critical error in function call.",
|
|
-- "Scan", 6, DebugLib.Level.Critical)
|
|
-- This is the full syntax except the title is missing. It is the
|
|
-- suggested syntax for specifying log entries which return error
|
|
-- codes, have a different level than DebugLib.Level.Error and do not
|
|
-- need their own title.
|
|
--
|
|
-- 7) Fully specified debug message:
|
|
-- lib.DebugPrint("DebugLib", "Critical error in function call.",
|
|
-- "Scan", "Invalid function call", 6,
|
|
-- DebugLib.Level.Critical)
|
|
-- This is the full syntax. It is the suggested syntax for specifying
|
|
-- log entries which return error codes, have a different level than
|
|
-- DebugLib.Level.Error and require their own title.
|
|
--
|
|
-- 8) Fully specified debug message with additional output data:
|
|
-- lib.DebugPrint("DebugLib", "Critical error in function call.",
|
|
-- "Scan", "Invalid function call", 6,
|
|
-- DebugLib.Level.Critical,
|
|
-- "table = ", table1,
|
|
-- "variable = ", variable1)
|
|
-- This is the full syntax including additional data for the generated
|
|
-- message. It is the suggested syntax for specifying log entries, if
|
|
-- the developer wants to dump additional variables to the generated
|
|
-- error message.
|
|
-- Note, that the additional data will only be added to the message
|
|
-- displayed within nLog. It will not be appended to the error message
|
|
-- which is returned by debugPrint(). If you want the additional
|
|
-- data to also be included in the function's return value, generate
|
|
-- the message yourself first and then pass it to debugPrint() using
|
|
-- syntax style no 7. For instance:
|
|
-- Instead of using:
|
|
-- DebugPrint("DebugLib", "Message", [...], "variable1=", variable1)
|
|
-- use the following method:
|
|
-- DebugPrint("DebugLib", "Message".." variable1="..DebugLib.Dump(variable1))
|
|
--
|
|
-- 9) Quick specified error entry, with only basic information:
|
|
-- lib.DebugPrint("DebugLib", "Failed to read data.", 5)
|
|
-- This creates a new error entry in nLog. This syntax can be used, if
|
|
-- you have to add this message quickly but don't want to specify the
|
|
-- category yet.
|
|
--
|
|
-- 10) Quick specified debug entry, with only basic information:
|
|
-- lib.DebugPrint("DebugLib", "Executing unsafe code.",
|
|
-- DebugLib.Level.Notice)
|
|
-- This creates a new notice entry in nLog. It's basically used, if
|
|
-- you have to quickly add some debug information but don't want to
|
|
-- specify the category yet.
|
|
--
|
|
-- 11) Partly specified error entry:
|
|
-- lib.DebugPrint("DebugLib", "Fatal error in command handler.", 9,
|
|
-- DebugLib.Level.Critical)
|
|
-- This syntax is possible, but quite uncommon. Instead of using this,
|
|
-- one should prefer the fully specified debug message.
|
|
-- It generates a new log entry for critical errors with no category.
|
|
--
|
|
-- 12) Empty log entry:
|
|
-- lib.DebugPrint("DebugLib")
|
|
-- This unusual usage will generate an empty debug log entry in nLog.
|
|
--
|
|
-- 13) Empty log entry with defined log level:
|
|
-- lib.DebugPrint("DebugLib", DebugLib.Level.Info)
|
|
-- Though valid, this syntax is also not very common. It creates an
|
|
-- empty notice in nLog.
|
|
--
|
|
-- >>>SPECIAL FUNCTION HANDLING<<<
|
|
-- Since the level parameter is a string representation, be aware of how the
|
|
-- function handles the following ambiguous calls.
|
|
--
|
|
-- Second, third or fourth parameter is a string found in DebugLib.Level:
|
|
-- lib.DebugPrint("DebugLib", "Error")
|
|
-- This will be interpreted as a type 12 syntax.
|
|
-- message = nil
|
|
-- category = "unspecified"
|
|
-- title = "Errorcode: unspecified"
|
|
-- level = DebugLib.Level.Error
|
|
--
|
|
-- lib.DebugPrint("DebugLib", "Ambiguous call.", "Warning")
|
|
-- This will be interpreted as a type 9 syntax.
|
|
-- message = "Ambiguous call."
|
|
-- category = "unspecified"
|
|
-- title = "Warning"
|
|
-- level = DebugLib.Level.Warning
|
|
--
|
|
-- >>lib.DebugPrint("DebugLib", "Error occured.", "Invalid function call.", "Critical")
|
|
-- This will be interpreted as a type 5 syntax.
|
|
-- message = "Error occured."
|
|
-- category = "Invalid functioncall."
|
|
-- title = "Errorcode: unspecified"
|
|
-- level = DebugLib.Level.Critical
|
|
--
|
|
-- Two out of the second, third and fourth parameters are strings found in
|
|
-- DebugLib.Level:
|
|
-- lib.DebugPrint("DebugLib", "Warning", "Notice")
|
|
-- This will be interpreted as a type 9 syntax.
|
|
-- message = "Warning"
|
|
-- category = "unspecified"
|
|
-- title = "Notice"
|
|
-- level = DebugLib.Level.Notice
|
|
--
|
|
-- lib.DebugPrint("DebugLib", "Info", "Engine", "Warning")
|
|
-- This will be interpreted as a type 5 syntax.
|
|
-- message = "Info"
|
|
-- category = "Engine"
|
|
-- title = "Warning"
|
|
-- level = DebugLib.Level.Warning
|
|
--
|
|
-- lib.DebugPrint("DebugLib", "Invalid type", "Error", "Critical")
|
|
-- This will be interpreted as a type 5 syntax.
|
|
-- message = "Invalid type"
|
|
-- category = "Error"
|
|
-- title = "Errorcode: unspecified"
|
|
-- level = DebugLib.Level.Critical
|
|
--
|
|
-- Second and/or third and/or fourth and fifth parameter are strings found
|
|
-- in DebugLib.Level:
|
|
-- lib.DebugPrint("DebugLib", "Warning", "Debug", "Error", "Critical")
|
|
-- This will be interpreted as a type 4 syntax.
|
|
-- message = "Warning"
|
|
-- category = "Debug"
|
|
-- title = "Error"
|
|
-- level = DebugLib.Level.Critical
|
|
--
|
|
-- >>>OPTIONAL PARAMETERS<<<
|
|
-- The examples above show all allowed syntaxes for this function and
|
|
-- explain the outcome. The following is a more code driven explanation of
|
|
-- what the default behaviour is, if one or more of the optional parameters
|
|
-- are missing.
|
|
--
|
|
-- category:
|
|
-- If no category is specified, the generated nLog message will print
|
|
-- "unspecified" as the category.
|
|
--
|
|
-- level:
|
|
-- If no level and no error code is specified, the level will be defaulting
|
|
-- to DebugLib.Level.Debug.
|
|
-- If no level is specified but an error code is given, the level will be
|
|
-- defaulting to DebugLib.Level.Error.
|
|
--
|
|
-- title:
|
|
-- If no title is specified it will be automatically generated based on the
|
|
-- errorCode and level.
|
|
-- If an errorCode is specified, the title says: "Errorcode: x".
|
|
-- If no errorCode is present and the level is either
|
|
-- DebugLib.Level.Critical or DebugLib.Level.Error, then the title says:
|
|
-- "Errorcode: unspecified".
|
|
-- If no errorCode is given and the level is neither DebugLib.Level.Critical
|
|
-- nor DebugLib.Level.Error, the title is the same as the level (for
|
|
-- instance: "Notice").
|
|
--
|
|
-- >>>ERROR HANDLING<<<
|
|
-- If any error occurs, the error will be written to nLog, if nLog is
|
|
-- enabled. Whether or not nLog is installed, processing the debug
|
|
-- message will continue.
|
|
-- To continue processing, invalid parameters will be ignored and in cases
|
|
-- of invalid ambiguous function calls, a decision is made about which value
|
|
-- will be used. The generated error message in nLog will explain, what was
|
|
-- wrong.
|
|
-- For a list of possible errorcodes, refer to the "Error Codes" section.
|
|
--
|
|
-- >>>TEMPLATE FUNCTION<<<
|
|
-- This function is not designed to be called directly. Instead it is meant
|
|
-- to have a local counterpart in each file, which automatically specifies
|
|
-- the addon parameter and then calls this function.
|
|
-- Refer to the local debugPrint() function for the reference implementation.
|
|
-------------------------------------------------------------------------------
|
|
function lib.DebugPrint(addon, message, category, title, errorCode, level, ...)
|
|
addon, message, category, title, errorCode, level = private.normalizeParameters(addon, message, category, title, errorCode, level)
|
|
|
|
if not nLog then return end
|
|
|
|
-- nLog.AddMessage() uses select() to check if any message is there.
|
|
-- Since select() will count even passed nil values, nLog would create "NIL"
|
|
-- as the message rather than an empty string. Therefore we need to take
|
|
-- care of this behavior and handle it by ourself.
|
|
local textMessage = message or ""
|
|
|
|
nLog.AddMessage(addon, category, private.levelLookupList[level], title, textMessage, ...)
|
|
|
|
-- We explicitly do not append any additional passed data to the returned
|
|
-- errormessage.
|
|
-- Doing so, would require us to call the dump/format functions for each
|
|
-- vararg parameter, causing a big performance loss, which is normally
|
|
-- unwanted.
|
|
-- If the developer wants the additional data to be added to the returned
|
|
-- errormessage, he'd have to concatenate it first and instead of adding
|
|
-- each single variable to the function's parameter list, pass the
|
|
-- concatenated string right into the message parameter.
|
|
return errorCode, message
|
|
end
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Prints the specified message to nLog.
|
|
-- This is the version used for lib.DebugPrintQuick, if nLog is installed and
|
|
-- enabled.
|
|
--
|
|
-- syntax:
|
|
-- errorCode, message = lib.DebugPrintQuick(...)
|
|
--
|
|
-- parameters:
|
|
-- ... - (any) data which will be appended to the error
|
|
-- message in nLog
|
|
--
|
|
-- returns:
|
|
-- nothing
|
|
--
|
|
-------------------------------------------------------------------------------
|
|
function lib.DebugPrintQuick(...)
|
|
if not nLog then return end
|
|
nLog.AddSimpleMessage(...)
|
|
end
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- The function does not do anything but processing the parameters and returning
|
|
-- the errorCode and message.
|
|
-- This is the version used for lib.DebugPrint, if nLog is not installed or
|
|
-- disabled.
|
|
--
|
|
-- syntax:
|
|
-- errorCode, message = lib.SimpleDebugPrint(addon[, message][, category][, title][, errorCode][, level] |
|
|
-- addon, message, category, title, errorCode, level, ...)
|
|
--
|
|
-- parameters:
|
|
-- addon - (string) the name of the addon
|
|
-- message - (string) the error message
|
|
-- nil, no error message specified
|
|
-- category - (string) the category of the debug message
|
|
-- nil, no category specified
|
|
-- title - (string) the title for the debug message
|
|
-- nil, no title specified
|
|
-- errorCode - (number) the error code
|
|
-- nil, no error code specified
|
|
-- level - (string) nLog message level
|
|
-- Any nLog.levels string is valid.
|
|
-- nil, no level specified
|
|
-- ... - (any) additional data which will be appended to the error
|
|
-- message in nLog
|
|
--
|
|
-- returns:
|
|
-- errorCode - (number) errorCode, if one is specified
|
|
-- nil, otherwise
|
|
-- message - (string) message, if one is specified
|
|
-- nil, otherwise
|
|
--
|
|
-- remarks:
|
|
-- Refer to the description of lib.DebugPrint() to see a more detailed
|
|
-- explanation about this function.
|
|
-------------------------------------------------------------------------------
|
|
function lib.SimpleDebugPrint(addon, message, category, title, errorCode, level, ...)
|
|
_, message, _, _, errorCode = private.normalizeParameters(addon, message, category, title, errorCode, level)
|
|
|
|
return errorCode, message
|
|
end
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- The function does not do anything.
|
|
-- This is the version used for lib.DebugPrintQuick, if nLog is not installed or
|
|
-- disabled.
|
|
--
|
|
-- syntax:
|
|
-- errorCode, message = lib.SimpleDebugPrintQuick(...)
|
|
--
|
|
-- parameters:
|
|
-- ... - (any) data which will be appended to the error
|
|
-- message in nLog
|
|
--
|
|
-- returns:
|
|
-- nothing
|
|
--
|
|
-------------------------------------------------------------------------------
|
|
function lib.SimpleDebugPrintQuick(...)
|
|
end
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Analyses and rearanges the given parameters, if necessary. Any invalidity
|
|
-- will cause a debug message, but the function will continue by automatically
|
|
-- handling these cases.
|
|
--
|
|
-- syntax:
|
|
-- addon, message, category, title, errorCode, level = normalizeParameter(addon[, message][, category][, title][, errorCode][, level])
|
|
--
|
|
-- parameters:
|
|
-- addon - (string) the name of the addon
|
|
-- message - (string) the error message
|
|
-- nil, no error message specified
|
|
-- category - (string) the category of the debug message
|
|
-- nil, no category specified
|
|
-- title - (string) the title for the debug message
|
|
-- nil, no title specified
|
|
-- errorCode - (number) the error code
|
|
-- nil, no error code specified
|
|
-- level - (string) nLog message level
|
|
-- Any nLog.levels string is valid.
|
|
-- nil, no level specified
|
|
--
|
|
-- returns:
|
|
-- addon - (string) the name of the addon
|
|
-- "unspecified", if there was no addon name
|
|
-- message - (string) message, if one is specified
|
|
-- nil, otherwise
|
|
-- title - (string) the title for the debug message
|
|
-- category - (string) category
|
|
-- "unspecified", if no valid category was specified
|
|
-- errorCode - (number) error code
|
|
-- nil, if no valid error code was specified
|
|
-- level - (string) debug level
|
|
-- One of the levelList values.
|
|
--
|
|
-- remarks:
|
|
-- This is a helper function for lib.DebugPrint and manages its complex
|
|
-- syntax by correctly ordering and handling the specified parameters.
|
|
-- Refer to the documentation about lib.DebugPrint() to read in detail how
|
|
-- the parameter list is handled.
|
|
-------------------------------------------------------------------------------
|
|
function private.normalizeParameters(addon, message, category, title, errorCode, level)
|
|
-- return values
|
|
local retAddon, retMessage, retCategory, retTitle, retErrorCode, retLevel
|
|
|
|
-- process the addon parameter
|
|
if addon == nil then
|
|
-- addon is not defined
|
|
debug("No addon specified! Defaulting to \"unspecified\" and continue with processing the debug message.",
|
|
"debug",
|
|
1)
|
|
elseif type(addon) ~= "string" then
|
|
-- addon is of an invalid type
|
|
debug("Invalid addon parameter! The type "..type(addon).." is not supported. Defaulting to \"unspecified\" and continue with processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- addon content is valid
|
|
retAddon = addon
|
|
end
|
|
|
|
-- process the level parameter
|
|
if level ~= nil then
|
|
-- The level parameter is present. It should contain the level content.
|
|
if type(level) ~= "string" then
|
|
-- It's not a string, therefore it's invalid.
|
|
debug("Invalid level parameter. The type "..type(level).." is not supported. Removing the value and continue with processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- It's a string and should be one of the valid levels.
|
|
if not isDebugLevel(level) then
|
|
-- It's not one of the valid levels, therefore the content is
|
|
-- invalid.
|
|
debug("Invalid level parameter. The given string is no valid debug level. Removing the value and continue with processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- level content is valid
|
|
retLevel = level
|
|
end
|
|
end
|
|
end
|
|
|
|
-- process the errorCode parameter
|
|
if errorCode ~= nil then
|
|
-- The errorCode parameter is present. It should contain either the
|
|
-- errorCode or level content.
|
|
if (type(errorCode) == "string") and (level == nil) then
|
|
-- errorCode could contain the level parameter
|
|
if not isDebugLevel(errorCode) then
|
|
-- It does not contain a valid level, therefore the parameter is
|
|
-- invalid.
|
|
debug("Invalid errorCode parameter. The given string is no valid debug level. Removing the value and continue with processing the debug message.",
|
|
"debug",
|
|
1)
|
|
elseif retLevel ~= nil then
|
|
-- ErrorCode contains a valid level parameter, but level parameter
|
|
-- is set, too.
|
|
debug("Multiple level parameters specified. Ignoring the one in errorCode and continue processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- ErrorCode contains the valid level parameter and it's the only
|
|
-- one.
|
|
retLevel = errorCode
|
|
end
|
|
elseif type(errorCode) ~= "number" then
|
|
-- errorCode contains an invalid parameter
|
|
debug("Invalid errorCode type. The type "..type(errorCode).." is not supported. Removing the value and continue with processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- errorCode content is valid
|
|
retErrorCode = errorCode
|
|
end
|
|
end
|
|
|
|
-- process the title parameter
|
|
if title ~= nil then
|
|
-- The title parameter is present. It should contain either the
|
|
-- title, errorCode or level content.
|
|
if (type(title) == "number") and (level == nil) then
|
|
-- It's the error code. Make sure that it's the only one.
|
|
if retErrorCode then
|
|
-- errorCode is already present, ignore the one in title
|
|
debug("Multiple error codes specified! Ignoring the one in title and continue processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- we got the error code, so safe it in the right place
|
|
retErrorCode = title
|
|
end
|
|
elseif type(title) ~= "string" then
|
|
-- title contains invalid content
|
|
debug("Invalid title type. The type "..type(title).." is not supported. Removing the value and continue with processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- It's either the title or the level content.
|
|
if (errorCode == nil) and (level == nil) and isDebugLevel(title) then
|
|
-- it's the level content
|
|
retLevel = title
|
|
else
|
|
-- it's the title
|
|
retTitle = title
|
|
end
|
|
end
|
|
end
|
|
|
|
-- process the category parameter
|
|
if category ~= nil then
|
|
-- The category parameter is present. It should contain either the
|
|
-- category, errorCode or level content.
|
|
if (type(category) == "number") and (errorCode == nil) and (level == nil) then
|
|
-- It's the error code. Make sure that it's the only one.
|
|
if retErrorCode then
|
|
-- errorCode is already present, ignore the one in category
|
|
debug("Multiple error codes specified! Ignoring the one in category and continue processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- we got the error code, so safe it in the right place
|
|
retErrorCode = category
|
|
end
|
|
elseif type(category) ~= "string" then
|
|
-- category contains invalid content
|
|
debug("Invalid category type. The type "..type(category).." is not supported. Removing the value and continue with processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- It's either the category or the level content.
|
|
if (title == nil) and (errorCode == nil) and (level == nil) and isDebugLevel(category) then
|
|
-- it's the level content
|
|
retLevel = category
|
|
else
|
|
-- it's the category
|
|
retCategory = category
|
|
end
|
|
end
|
|
end
|
|
|
|
-- process the message parameter
|
|
if message ~= nil then
|
|
-- The message parameter is present. It should contain either the
|
|
-- message, errorCode or level content.
|
|
if (type(message) == "number") and (title == nil) and (errorCode == nil) and (level == nil) then
|
|
-- It's the error code. Make sure that it's the only one.
|
|
if retErrorCode then
|
|
-- errorCode is already present, ignore the one in message
|
|
debug("Multiple error codes specified! Ignoring the one in message and continue processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- we got the error code, so safe it in the right place
|
|
retErrorCode = message
|
|
end
|
|
elseif type(message) ~= "string" then
|
|
-- message contains invalid content
|
|
debug("Invalid message type. The type "..type(message).." is not supported. Removing the value and continue with processing the debug message.",
|
|
"debug",
|
|
1)
|
|
else
|
|
-- It's either the message or the level content.
|
|
if (category == nil) and (title == nil) and (errorCode == nil) and (level == nil) and isDebugLevel(message) then
|
|
-- it's the level content
|
|
retLevel = message
|
|
else
|
|
retMessage = message
|
|
end
|
|
end
|
|
end
|
|
|
|
-- defaulting return values, for unspecified ones
|
|
retAddon = retAddon or "unspecified"
|
|
retCategory = retCategory or "unspecified"
|
|
if not retLevel then
|
|
if retErrorCode then
|
|
retLevel = private.levelList.Error
|
|
else
|
|
retLevel = private.levelList.Debug
|
|
end
|
|
end
|
|
if not retTitle then
|
|
retTitle = private.generateTitle(retLevel, retErrorCode)
|
|
end
|
|
|
|
return retAddon, retMessage, retCategory, retTitle, retErrorCode, retLevel
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Checks the given level to see if it's a valid debug level.
|
|
--
|
|
-- syntax:
|
|
-- validLevel = isDebugLevel(level)
|
|
--
|
|
-- parameters:
|
|
-- level - (string) the level parameter to be checked
|
|
--
|
|
-- returns:
|
|
-- validLevel - (boolean) true, if the level string represents a valid
|
|
-- debug level
|
|
-- false, otherwise
|
|
-------------------------------------------------------------------------------
|
|
function isDebugLevel(level)
|
|
if type(level) ~= "string" then
|
|
return false -- it's not a string, so it can't be a valid level
|
|
end
|
|
|
|
for _, levelString in pairs(private.levelList) do
|
|
if levelString == level then
|
|
return true -- it's a valid level
|
|
end
|
|
end
|
|
|
|
return false -- level is not in the level list, therefore it's invalid
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Takes the debug level and error code and returns a title for the log entry
|
|
-- according to these parameters.
|
|
--
|
|
-- syntax:
|
|
-- title = private.generateTitle(level[, errorCode])
|
|
--
|
|
-- parameters:
|
|
-- level - (string) the debug level
|
|
-- errorCode - (number) the error code
|
|
-- nil, if no error code is specified
|
|
--
|
|
-- returns:
|
|
-- title - (string) the generated title
|
|
--
|
|
-- remarks:
|
|
-- Refer to the documentation about lib.DebugPrint() to see which titles are
|
|
-- generated.
|
|
-------------------------------------------------------------------------------
|
|
function private.generateTitle(level, errorCode)
|
|
if errorCode then
|
|
return "Errorcode: "..errorCode
|
|
elseif (level == lib.Level.Error) or (level == lib.Level.Critical) then
|
|
return "Errorcode: unspecified"
|
|
else
|
|
return level
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Used to make sure that conditions are met within functions.
|
|
-- If test is false, the error message will be written to nLog and the user's
|
|
-- default chat channel.
|
|
-- This is the version used for lib.Assert, if nLog is installed and
|
|
-- enabled.
|
|
--
|
|
-- syntax:
|
|
-- assertion = lib.Assert(addon, test, ...)
|
|
--
|
|
-- parameters:
|
|
-- addon - (string) the name of the addon/file used to identify the
|
|
-- specific assertion
|
|
-- test - (any) false/nil, if the assertion failed
|
|
-- anything else, otherwise
|
|
-- ... - (any) data which will be appended to the nLog message
|
|
--
|
|
-- return:
|
|
-- assertion - (boolean) true, if the test passed
|
|
-- false, otherwise
|
|
--
|
|
-- remark:
|
|
-- >>>NLOG ENTRY<<<
|
|
-- If nLog is present, the message will not only be written to the user's
|
|
-- chat channel, but also to nLog with the priority set to N_CRITICAL, since
|
|
-- it is assumed that Assert() is only used in critical parts of functions
|
|
-- and that test is expected to never fail. This is especially useful to
|
|
-- track down bugs which might randomly occure. Therefore this log message is
|
|
-- given the highest priority.
|
|
--
|
|
-- >>>ERROR HANDLING<<<
|
|
-- If any error occurs, the error will be written to nLog, if nLog is
|
|
-- enabled. Whether or not nLog is installed, processing the debug
|
|
-- message will continue.
|
|
-- To continue processing, missing parameters will get default values. The
|
|
-- generated error message in nLog will explain, what was wrong.
|
|
-- For a list of possible errorcodes, refer to the Error Codes section.
|
|
--
|
|
-- >>>TEMPLATE FUNCTION<<<
|
|
-- This function is not designed to be called directly. Instead it is meant
|
|
-- to have a local counterpart in each file, which automatically specifies
|
|
-- the addon parameter and then calls this function.
|
|
-- Refer to the local assert() function for the reference implementation.
|
|
-------------------------------------------------------------------------------
|
|
function lib.Assert(addon, test, ...)
|
|
-- validate the parameters
|
|
if type(addon) ~= "string" then
|
|
debug("Invalid addon parameter. Addon must be a string.",
|
|
"assert",
|
|
1)
|
|
addon = "unspecified"
|
|
end
|
|
|
|
if test then
|
|
return true -- test passed
|
|
end
|
|
|
|
local message = private.format(...)
|
|
|
|
_G["ChatFrame1"]:AddMessage(message, 1.0, 0.3, 0.3)
|
|
|
|
if nLog then
|
|
nLog.AddMessage(addon, "Assertion", N_CRITICAL, "assertion failed", message)
|
|
end
|
|
|
|
return false -- test failed
|
|
end
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Used to make sure that conditions are met within functions.
|
|
-- If test is false, the error message will be written to the user's default
|
|
-- chat channel.
|
|
-- This is the version used for lib.Assert, if nLog is not installed or
|
|
-- disabled.
|
|
--
|
|
-- syntax:
|
|
-- assertion = lib.SimpleAssert(addon, test, ...)
|
|
--
|
|
-- parameters:
|
|
-- addon - (string) the name of the addon/file used to identify the
|
|
-- specific assertion
|
|
-- test - (any) false/nil, if the assertion failed
|
|
-- anything else, otherwise
|
|
-- ... - (any) data which will be appended to the nLog message
|
|
--
|
|
-- return:
|
|
-- assertion - (boolean) true, if the test passed
|
|
-- false, otherwise
|
|
--
|
|
-- remarks:
|
|
-- Refer to the description of lib.Assert() to see a more detailed explanation
|
|
-- about this function.
|
|
-------------------------------------------------------------------------------
|
|
function lib.SimpleAssert(addon, test, ...)
|
|
-- validate the parameters
|
|
if type(addon) ~= "string" then
|
|
debug("Invalid addon parameter. Addon must be a string.",
|
|
"assert",
|
|
1)
|
|
addon = "unspecified"
|
|
end
|
|
|
|
if test then
|
|
return true -- test passed
|
|
end
|
|
|
|
local message = private.format(...)
|
|
|
|
_G["ChatFrame1"]:AddMessage(message, 1.0, 0.3, 0.3)
|
|
|
|
return false -- test failed
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Creates a comma-separated string by transforming all parameters into string
|
|
-- representations and concatenating these. Recursion and length limits have
|
|
-- been added to prevent crashes and hour long waits for output.
|
|
--
|
|
-- syntax:
|
|
-- concatString = dump(...)
|
|
--
|
|
-- parameters:
|
|
-- ... - (any) parameters which should be added to the string
|
|
--
|
|
-- returns:
|
|
-- concatString - (string) The concatenated string.
|
|
--
|
|
-- Variables are concatenated the following way:
|
|
-- variable type => resulting string
|
|
-- [table] => {[key1] = [value1], [key2] = [value2], ...}
|
|
-- [nil] => NIL
|
|
-- [number] => [number]
|
|
-- [string] => "[string]"
|
|
-- [boolean] => true/false
|
|
-- [other] => TYPE_OF_OTHER??
|
|
-------------------------------------------------------------------------------
|
|
|
|
function private.dump(...)
|
|
if (private.dumpCurrentRecursionDepth >= private.dumpRecursionLimit) then
|
|
return "recursion limit reached"
|
|
end
|
|
private.dumpCurrentRecursionDepth = private.dumpCurrentRecursionDepth + 1
|
|
local out = ""
|
|
local numVarArgs = select("#", ...)
|
|
for i = 1, numVarArgs do
|
|
local d = select(i, ...)
|
|
local t = type(d)
|
|
if (t == "table") then
|
|
out = out .. "{"
|
|
local tableEntryCount = 0
|
|
if (d) then
|
|
for k, v in pairs(d) do
|
|
if (tableEntryCount <= private.dumpTableLengthLimit) then
|
|
if (tableEntryCount > 0) then out = out .. ", " end
|
|
out = out .. private.dump(k)
|
|
out = out .. " = "
|
|
out = out .. private.dump(v)
|
|
if (string.len(out) > private.dumpStringLimit) then
|
|
out = out .. "..."
|
|
break
|
|
end
|
|
end
|
|
tableEntryCount = tableEntryCount + 1
|
|
end
|
|
end
|
|
if (tableEntryCount > private.dumpTableLengthLimit) then
|
|
out = out .. " table was " .. tableEntryCount .. " entries long. "
|
|
end
|
|
out = out .. "}"
|
|
elseif (t == "nil") then
|
|
out = out .. "NIL"
|
|
elseif (t == "number") then
|
|
out = out .. d
|
|
elseif (t == "string") then
|
|
out = out .. "\"" .. d .. "\""
|
|
elseif (t == "boolean") then
|
|
if (d) then
|
|
out = out .. "true"
|
|
else
|
|
out = out .. "false"
|
|
end
|
|
else
|
|
out = out .. t:upper() .. "??"
|
|
end
|
|
if (string.len(out) > private.dumpStringLimit) then
|
|
out = out .. "..."
|
|
break
|
|
end
|
|
if (i < numVarArgs) then out = out .. ", " end
|
|
end
|
|
|
|
-- make sure returns come though here, or we won't balance the depth increase above
|
|
private.dumpCurrentRecursionDepth = private.dumpCurrentRecursionDepth - 1
|
|
return out
|
|
end
|
|
function lib.Dump(...)
|
|
return private.dump(...)
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Transforms all parameters into string representations and concatenates those
|
|
-- into a comma separated string (except strings, which are separated by a
|
|
-- single space).
|
|
--
|
|
-- syntax:
|
|
-- concatString = format(...)
|
|
--
|
|
-- parameters:
|
|
-- ... - (any) parameters which should be added to the string
|
|
--
|
|
-- returns:
|
|
-- concatString - (string) The concatenated string.
|
|
--
|
|
-- remark:
|
|
-- What makes this function different from the dump() function is how it
|
|
-- handles strings. For example:
|
|
-- format("str1", " str2") = str1 str2
|
|
-- dump("str1", " str2") = "str1", " str2"
|
|
--
|
|
-------------------------------------------------------------------------------
|
|
function private.format(...)
|
|
local n = select("#", ...)
|
|
local out = ""
|
|
for i = 1, n do
|
|
if i > 1 and out:sub(-1) ~= " " then out = out .. " "; end
|
|
local d = select(i, ...)
|
|
if (type(d) == "string") then
|
|
if (d:sub(1,1) == " ") then
|
|
out = out .. d:sub(2)
|
|
else
|
|
out = out..d;
|
|
end
|
|
else
|
|
out = out..private.dump(d);
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
|
|
--Kit functions
|
|
--These are the preferred ways to access the DebugLib functions now. The lib functions above are maintained for compatibility reasons only.
|
|
local kit = {}
|
|
if (nLog) then
|
|
function kit:Debug(...)
|
|
return lib.DebugPrint(self.name, ...)
|
|
end
|
|
function kit:Assert(...)
|
|
return lib.Assert(self.name, ...)
|
|
end
|
|
function kit:DebugQuick(...)
|
|
return lib.DebugPrintQuick(...)
|
|
end
|
|
else
|
|
function kit:Debug(...)
|
|
return lib.SimpleDebugPrint(self.name, ...)
|
|
end
|
|
function kit:Assert(...)
|
|
return lib.SimpleAssert(self.name, ...)
|
|
end
|
|
function kit:DebugQuick(...)
|
|
return lib.SimpleDebugPrintQuick(...)
|
|
end
|
|
end
|
|
|
|
function kit:Dump(...)
|
|
return private.dump(...)
|
|
end
|
|
|
|
function lib:New(addonName)
|
|
assert(addonName, "Usage: DebugLib(addonName)")
|
|
local debugObj, assertObj, debugQuickObj = {name=addonName}, {name=addonName}, {name=addonName}
|
|
for k,v in pairs(kit) do
|
|
debugObj[k] = v
|
|
assertObj[k] = v
|
|
debugQuickObj[k] = v
|
|
end
|
|
setmetatable(debugObj, { __call = kit.Debug })
|
|
setmetatable(assertObj, { __call = kit.Assert })
|
|
setmetatable(debugQuickObj, { __call = kit.DebugQuick })
|
|
return debugObj, assertObj, debugQuickObj
|
|
end
|
|
setmetatable(lib, { __call = lib.New })
|
|
|
|
-- Set our local debug function
|
|
debug = lib:New("DebugLib")
|