Модуль:DebugLog (Bk;rl,&DebugLog)

Перейти к навигации Перейти к поиску
Документация

Модуль предназначен для формирования отладочного лога других модулей. Может быть использован при разработке или на страницах с тестами других модулей.

Методы

[править код]
Основные:
  • DebugLog:new() — конструктор.
  • DebugLog:write(message, caller, state) — запись в лог сообщения message. caller — имя функции, state — статус (error, warning, info). Тип message может быть любым.
  • DebugLog:getAll() — вывод лога.
Прочие:
  • DebugLog:getIssues() — вывод только ошибок и предупреждений
  • DebugLog:tail(num) — вывод последних num записей
  • DebugLog:getFormatted(first, last, nowrap) — вывод записей с first по last
  • DebugLog:getSummary() — вывод строки с числом ошибок и предупреждений
  • DebugLog:getEntry(num) — получение записи num в табличном виде
  • DebugLog:formatEntry(num) — форматирование записи num в строку

Особенности

[править код]

Экземпляр таблицы с логом обычно должен быть продекларирован и инициализирован с помощью конструктора глобально, чтобы быть доступным в любом контексте. В любом случае он должен быть доступен в инициализированном виде в тех контекстах, где вызываются его методы. За включение режима логирования отвечает DebugLog.enabled, который по умолчанию false. Обычно имеет смысл установить его в true в отдельном методе, как в примере ниже. Этот отдельный метод вызывать из предпросмотра или на отдельной странице тестов.

При обычном вызове модуля DebugLog.enabled = false, это означает, что лог не пишется и не расходует ресурсы.

Использование

[править код]

В модуле:

-- подключение
local DebugLog = require( 'Module:DebugLog' )
local debLog = DebugLog:new() -- инициализация экземпляра debLog на основе прототипа DebugLog

local function foo()
	-- пример записи сообщения в лог в произвольном месте
	debLog:write('my message', 'foo', 'warning')
...

function p.loggedCall(frame)
	debLog.enabled = true -- включение для логируемого вызова
	local args = getArgs(frame) -- получение таблицы параметров фрейма, см. Module:Arguments
	local success, result = pcall(p[ args['function-name'] ], frame) -- вызов основной функции
	return debLog:getAll() .. mw.text.nowiki(tostring(result)) -- вывод лога и результата работы
end

На странице тестов или аналогичной:

{{#invoke:MyModule|loggedCall|function-name=MyFunc}}
local mwLang = mw.getContentLanguage()

local function pushRight( tab, val )
	local last = tab.last + 1
	tab.last = last
	tab[last] = val
end

local function wrapInPreTag( str, nowrap )
	if not nowrap then
		return table.concat{'<pre>', result, '</pre>'}
	else return str	end
end

local DebugLog = {}

DebugLog.stateStrings = {
	['ERROR'] = 'ERROR', ['ERR']='ERROR', ['E'] = 'ERROR',
	['WARNING'] = 'WARN', ['WARN']='WARN', ['W'] = 'WARN',
}

DebugLog.errWrapL = '<span style="color:red; font-weight:bold;">'
DebugLog.errWrapR = '</span>'
DebugLog.warnWrapL = '<span style="color:orange;">'
DebugLog.warnWrapR = '</span>'

-- Constructor
function DebugLog:new(o)
	o = o or {}
	setmetatable(o, self)
	self.__index = self
	o.first = 1 
	o.last  = 0
	o.warnings = 0
	o.errors = 0
	o.enabled = false
	return o
end

-- Appends an entry to log
-- message - string with a message
-- caller  - string with a name of a caller function
-- state   - status of an entry: info/warning/error
function DebugLog:write(message, caller, state)
	if not self.enabled then return end
	if type(message) == 'nil' then
		message = 'nil'
	elseif type(message) == 'string' then
		do end
	elseif type(message) ~= 'table' then
		message = tostring(message)
	else
		message = mw.dumpObject(message)
	end
	caller = tostring(caller or '')
	state = tostring(state or '')
	state = mwLang:uc(tostring(state or ''))
	state = self.stateStrings[state] or state
	if state  == 'ERROR' then
		self.errors = self.errors + 1
	elseif state  == 'WARN' then
		self.warnings = self.warnings + 1
	end
	local entry = {
		['message'] = message,
		['caller'] = caller,
		['state'] = state,
	}
	pushRight(self, entry)
end

-- returns table with an entry
function DebugLog:getEntry(num)
	num = num or self.last
	if type(num) ~= 'number' then num = self.last end
	if num > self.last  then num = self.last end
	if num < self.first then num = self.first end
	return self[num]
end

-- returns formatted string for entry 'num'
function DebugLog:formatEntry(num)
	local entry = self:getEntry(num)
	local message = self[num].message
	local state = self[num].state
	if state == 'ERROR' then
		state = table.concat{'[', self.errWrapL , state, self.errWrapR , ']'}
	elseif state == 'WARN' then
		state = table.concat{'[', self.warnWrapL , state, self.warnWrapR, ']'}
	else state = '[INFO]'
	end
	local caller = self[num].caller
	if caller ~= '' then
		caller = table.concat{' <i>', caller, '()</i>: '}
	end
	local message = self[num].message
	return table.concat{state, caller, message}
end

-- returns formatted string with number of warnings and errors
function DebugLog:getSummary()
	local errorsFmtd, warningsFmtd
	if self.errors > 0 then
		local errStr = string.format('%s errors', self.errors)
		errorsFmtd = table.concat{self.errWrapL , errStr, self.errWrapR}
	else
		errorsFmtd = 'no errors'
	end
	if self.warnings > 0 then
		local warnStr = string.format('%s warnings', self.warnings)
		warningsFmtd = table.concat{self.warnWrapL , warnStr, self.warnWrapR}
	else
		warningsFmtd = 'no warnings'
	end
	return table.concat{'Found ' , errorsFmtd, ' and ', warningsFmtd}
end

-- returns formatted string with entries from 'first' to 'last'
-- 'nowrap' prevents wrapping in <pre></pre>
function DebugLog:getFormatted(first, last, nowrap)
	first = first or self.first
	if type(first) ~= 'number' then first = self.first end
	if first < self.first then first = self.first end
	
	last = last or self.last
	if type(last) ~= 'number' then last = self.last end
	if last > self.last then last = self.last end
	
	local formattedEntries = {}
	for i = first, last do
		formattedEntries[i] = self:formatEntry(i)
	end
	local result = table.concat(formattedEntries, '<br>', first, last)
	
	return wrapInPreTag(result, nowrap)
end

-- returns formatted string with 'num' of last entries
function DebugLog:tail(num)
	num = num or 10
	if type(num) ~= 'number' then num = 10 end
	return wrapInPreTag(self:getFormatted(self.last - num, self.last))
end

-- returns formatted string, containing log entries with warnings and errors
function DebugLog:getIssues()
	local result = {first = 1, last = 0, }
	for i = self.first, self.last do
		if self[i]['state'] == 'ERROR' or self[i]['state'] == 'WARN' then
			pushRight(result, self:formatEntry(i))
		end
	end
	return wrapInPreTag(table.concat(result, '<br>'))
end

-- returns formatted string with all entries and summary
function DebugLog:getAll()
	local fullLog = self:getFormatted( nil, nil, true)
	local summary = self:getSummary()
	return table.concat{'<pre>', fullLog, '<br>', summary, '</pre>'}
end

local p = DebugLog
return p