Merge with 0.4.1
[prosody.git] / util / logger.lua
1 -- Prosody IM v0.4
2 -- Copyright (C) 2008-2009 Matthew Wild
3 -- Copyright (C) 2008-2009 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 config = require "core.configmanager";
12 local log_sources = config.get("*", "core", "log_sources");
13
14 local find = string.find;
15 local ipairs, pairs, setmetatable = ipairs, pairs, setmetatable;
16
17 module "logger"
18
19 local name_sinks, level_sinks = {}, {};
20 local name_patterns = {};
21
22 -- Weak-keyed so that loggers are collected
23 local modify_hooks = setmetatable({}, { __mode = "k" });
24
25 local make_logger;
26 local outfunction = nil;
27
28 function init(name)
29         if log_sources then
30                 local log_this = false;
31                 for _, source in ipairs(log_sources) do
32                         if find(name, source) then 
33                                 log_this = true;
34                                 break;
35                         end
36                 end
37                 
38                 if not log_this then return function () end end
39         end
40         
41         local log_debug = make_logger(name, "debug");
42         local log_info = make_logger(name, "info");
43         local log_warn = make_logger(name, "warn");
44         local log_error = make_logger(name, "error");
45
46         --name = nil; -- While this line is not commented, will automatically fill in file/line number info
47         local namelen = #name;
48         return function (level, message, ...)
49                         if outfunction then return outfunction(name, level, message, ...); end
50                         
51                         if level == "debug" then
52                                 return log_debug(message, ...);
53                         elseif level == "info" then
54                                 return log_info(message, ...);
55                         elseif level == "warn" then
56                                 return log_warn(message, ...);
57                         elseif level == "error" then
58                                 return log_error(message, ...);
59                         end
60                 end
61 end
62
63 function make_logger(source_name, level)
64         local level_handlers = level_sinks[level];
65         if not level_handlers then
66                 level_handlers = {};
67                 level_sinks[level] = level_handlers;
68         end
69
70         local source_handlers = name_sinks[source_name];
71         
72         -- All your premature optimisation is belong to me!
73         local num_level_handlers, num_source_handlers = #level_handlers, source_handlers and #source_handlers;
74         
75         local logger = function (message, ...)
76                 if source_handlers then
77                         for i = 1,num_source_handlers do
78                                 if source_handlers(source_name, level, message, ...) == false then
79                                         return;
80                                 end
81                         end
82                 end
83                 
84                 for i = 1,num_level_handlers do
85                         level_handlers[i](source_name, level, message, ...);
86                 end
87         end
88
89         -- To make sure our cached lengths stay in sync with reality
90         modify_hooks[logger] = function () num_level_handlers, num_source_handlers = #level_handlers, source_handlers and #source_handlers; end; 
91         
92         return logger;
93 end
94
95 function setwriter(f)
96         local old_func = outfunction;
97         if not f then outfunction = nil; return true, old_func; end
98         local ok, ret = pcall(f, "logger", "info", "Switched logging output successfully");
99         if ok then
100                 outfunction = f;
101                 ret = old_func;
102         end
103         return ok, ret;
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;