Monster whitespace commit (beware the whitespace monster).
[prosody.git] / util / logger.lua
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 find = string.find;
12 local ipairs, pairs, setmetatable = ipairs, pairs, setmetatable;
13
14 module "logger"
15
16 local name_sinks, level_sinks = {}, {};
17 local name_patterns = {};
18
19 -- Weak-keyed so that loggers are collected
20 local modify_hooks = setmetatable({}, { __mode = "k" });
21
22 local make_logger;
23 local outfunction = nil;
24
25 function init(name)
26         local log_debug = make_logger(name, "debug");
27         local log_info = make_logger(name, "info");
28         local log_warn = make_logger(name, "warn");
29         local log_error = make_logger(name, "error");
30
31         --name = nil; -- While this line is not commented, will automatically fill in file/line number info
32         local namelen = #name;
33         return function (level, message, ...)
34                         if outfunction then return outfunction(name, level, message, ...); end
35                         
36                         if level == "debug" then
37                                 return log_debug(message, ...);
38                         elseif level == "info" then
39                                 return log_info(message, ...);
40                         elseif level == "warn" then
41                                 return log_warn(message, ...);
42                         elseif level == "error" then
43                                 return log_error(message, ...);
44                         end
45                 end
46 end
47
48 function make_logger(source_name, level)
49         local level_handlers = level_sinks[level];
50         if not level_handlers then
51                 level_handlers = {};
52                 level_sinks[level] = level_handlers;
53         end
54
55         local source_handlers = name_sinks[source_name];
56         
57         -- All your premature optimisation is belong to me!
58         local num_level_handlers, num_source_handlers = #level_handlers, source_handlers and #source_handlers;
59         
60         local logger = function (message, ...)
61                 if source_handlers then
62                         for i = 1,num_source_handlers do
63                                 if source_handlers[i](source_name, level, message, ...) == false then
64                                         return;
65                                 end
66                         end
67                 end
68                 
69                 for i = 1,num_level_handlers do
70                         level_handlers[i](source_name, level, message, ...);
71                 end
72         end
73
74         -- To make sure our cached lengths stay in sync with reality
75         modify_hooks[logger] = function () num_level_handlers, num_source_handlers = #level_handlers, source_handlers and #source_handlers; end;
76         
77         return logger;
78 end
79
80 function setwriter(f)
81         local old_func = outfunction;
82         if not f then outfunction = nil; return true, old_func; end
83         local ok, ret = pcall(f, "logger", "info", "Switched logging output successfully");
84         if ok then
85                 outfunction = f;
86                 ret = old_func;
87         end
88         return ok, ret;
89 end
90
91 function reset()
92         for k in pairs(name_sinks) do name_sinks[k] = nil; end
93         for level, handler_list in pairs(level_sinks) do
94                 -- Clear all handlers for this level
95                 for i = 1, #handler_list do
96                         handler_list[i] = nil;
97                 end
98         end
99         for k in pairs(name_patterns) do name_patterns[k] = nil; end
100
101         for _, modify_hook in pairs(modify_hooks) do
102                 modify_hook();
103         end
104 end
105
106 function add_level_sink(level, sink_function)
107         if not level_sinks[level] then
108                 level_sinks[level] = { sink_function };
109         else
110                 level_sinks[level][#level_sinks[level] + 1 ] = sink_function;
111         end
112         
113         for _, modify_hook in pairs(modify_hooks) do
114                 modify_hook();
115         end
116 end
117
118 function add_name_sink(name, sink_function, exclusive)
119         if not name_sinks[name] then
120                 name_sinks[name] = { sink_function };
121         else
122                 name_sinks[name][#name_sinks[name] + 1] = sink_function;
123         end
124         
125         for _, modify_hook in pairs(modify_hooks) do
126                 modify_hook();
127         end
128 end
129
130 function add_name_pattern_sink(name_pattern, sink_function, exclusive)
131         if not name_patterns[name_pattern] then
132                 name_patterns[name_pattern] = { sink_function };
133         else
134                 name_patterns[name_pattern][#name_patterns[name_pattern] + 1] = sink_function;
135         end
136 end
137
138 _M.new = make_logger;
139
140 return _M;