| 1 | -- Prosody IM |
| 2 | -- Copyright (C) 2008-2010 Matthew Wild |
| 3 | -- Copyright (C) 2008-2010 Waqas Hussain |
| 4 | -- |
| 5 | -- This project is MIT/X11 licensed. Please see the |
| 6 | -- COPYING file in the source package for more information. |
| 7 | -- |
| 8 | |
| 9 | local pcall = pcall; |
| 10 | |
| 11 | --local log_sources = config.get("*", "core", "log_sources"); |
| 12 | |
| 13 | local find = string.find; |
| 14 | local ipairs, pairs, setmetatable = ipairs, pairs, setmetatable; |
| 15 | |
| 16 | module "logger" |
| 17 | |
| 18 | local name_sinks, level_sinks = {}, {}; |
| 19 | local name_patterns = {}; |
| 20 | |
| 21 | -- Weak-keyed so that loggers are collected |
| 22 | local modify_hooks = setmetatable({}, { __mode = "k" }); |
| 23 | |
| 24 | local make_logger; |
| 25 | local outfunction = nil; |
| 26 | |
| 27 | function init(name) |
| 28 | if log_sources then |
| 29 | local log_this = false; |
| 30 | for _, source in ipairs(log_sources) do |
| 31 | if find(name, source) then |
| 32 | log_this = true; |
| 33 | break; |
| 34 | end |
| 35 | end |
| 36 | |
| 37 | if not log_this then return function () end end |
| 38 | end |
| 39 | |
| 40 | local log_debug = make_logger(name, "debug"); |
| 41 | local log_info = make_logger(name, "info"); |
| 42 | local log_warn = make_logger(name, "warn"); |
| 43 | local log_error = make_logger(name, "error"); |
| 44 | |
| 45 | --name = nil; -- While this line is not commented, will automatically fill in file/line number info |
| 46 | local namelen = #name; |
| 47 | return function (level, message, ...) |
| 48 | if outfunction then return outfunction(name, level, message, ...); end |
| 49 | |
| 50 | if level == "debug" then |
| 51 | return log_debug(message, ...); |
| 52 | elseif level == "info" then |
| 53 | return log_info(message, ...); |
| 54 | elseif level == "warn" then |
| 55 | return log_warn(message, ...); |
| 56 | elseif level == "error" then |
| 57 | return log_error(message, ...); |
| 58 | end |
| 59 | end |
| 60 | end |
| 61 | |
| 62 | function make_logger(source_name, level) |
| 63 | local level_handlers = level_sinks[level]; |
| 64 | if not level_handlers then |
| 65 | level_handlers = {}; |
| 66 | level_sinks[level] = level_handlers; |
| 67 | end |
| 68 | |
| 69 | local source_handlers = name_sinks[source_name]; |
| 70 | |
| 71 | -- All your premature optimisation is belong to me! |
| 72 | local num_level_handlers, num_source_handlers = #level_handlers, source_handlers and #source_handlers; |
| 73 | |
| 74 | local logger = function (message, ...) |
| 75 | if source_handlers then |
| 76 | for i = 1,num_source_handlers do |
| 77 | if source_handlers[i](source_name, level, message, ...) == false then |
| 78 | return; |
| 79 | end |
| 80 | end |
| 81 | end |
| 82 | |
| 83 | for i = 1,num_level_handlers do |
| 84 | level_handlers[i](source_name, level, message, ...); |
| 85 | end |
| 86 | end |
| 87 | |
| 88 | -- To make sure our cached lengths stay in sync with reality |
| 89 | modify_hooks[logger] = function () num_level_handlers, num_source_handlers = #level_handlers, source_handlers and #source_handlers; end; |
| 90 | |
| 91 | return logger; |
| 92 | end |
| 93 | |
| 94 | function setwriter(f) |
| 95 | local old_func = outfunction; |
| 96 | if not f then outfunction = nil; return true, old_func; end |
| 97 | local ok, ret = pcall(f, "logger", "info", "Switched logging output successfully"); |
| 98 | if ok then |
| 99 | outfunction = f; |
| 100 | ret = old_func; |
| 101 | end |
| 102 | return ok, ret; |
| 103 | end |
| 104 | |
| 105 | function reset() |
| 106 | for k in pairs(name_sinks) do name_sinks[k] = nil; end |
| 107 | for level, handler_list in pairs(level_sinks) do |
| 108 | -- Clear all handlers for this level |
| 109 | for i = 1, #handler_list do |
| 110 | handler_list[i] = nil; |
| 111 | end |
| 112 | end |
| 113 | for k in pairs(name_patterns) do name_patterns[k] = nil; end |
| 114 | |
| 115 | for _, modify_hook in pairs(modify_hooks) do |
| 116 | modify_hook(); |
| 117 | end |
| 118 | end |
| 119 | |
| 120 | function add_level_sink(level, sink_function) |
| 121 | if not level_sinks[level] then |
| 122 | level_sinks[level] = { sink_function }; |
| 123 | else |
| 124 | level_sinks[level][#level_sinks[level] + 1 ] = sink_function; |
| 125 | end |
| 126 | |
| 127 | for _, modify_hook in pairs(modify_hooks) do |
| 128 | modify_hook(); |
| 129 | end |
| 130 | end |
| 131 | |
| 132 | function add_name_sink(name, sink_function, exclusive) |
| 133 | if not name_sinks[name] then |
| 134 | name_sinks[name] = { sink_function }; |
| 135 | else |
| 136 | name_sinks[name][#name_sinks[name] + 1] = sink_function; |
| 137 | end |
| 138 | |
| 139 | for _, modify_hook in pairs(modify_hooks) do |
| 140 | modify_hook(); |
| 141 | end |
| 142 | end |
| 143 | |
| 144 | function add_name_pattern_sink(name_pattern, sink_function, exclusive) |
| 145 | if not name_patterns[name_pattern] then |
| 146 | name_patterns[name_pattern] = { sink_function }; |
| 147 | else |
| 148 | name_patterns[name_pattern][#name_patterns[name_pattern] + 1] = sink_function; |
| 149 | end |
| 150 | end |
| 151 | |
| 152 | _M.new = make_logger; |
| 153 | |
| 154 | return _M; |