util.x509: Only collect commonNames that pass idna
[prosody.git] / core / loggingmanager.lua
blob85a6380beaf972303449c2945973fa178aa42595
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 --
9 local format = require "util.format".format;
10 local setmetatable, rawset, pairs, ipairs, type =
11 setmetatable, rawset, pairs, ipairs, type;
12 local stdout = io.stdout;
13 local io_open = io.open;
14 local math_max, rep = math.max, string.rep;
15 local os_date = os.date;
16 local getstyle, getstring = require "util.termcolours".getstyle, require "util.termcolours".getstring;
18 local config = require "core.configmanager";
19 local logger = require "util.logger";
21 local have_pposix, pposix = pcall(require, "util.pposix");
22 have_pposix = have_pposix and pposix._VERSION == "0.4.0";
24 local _ENV = nil;
25 -- luacheck: std none
27 -- The log config used if none specified in the config file (see reload_logging for initialization)
28 local default_logging;
29 local default_file_logging;
30 local default_timestamp = "%b %d %H:%M:%S ";
31 -- The actual config loggingmanager is using
32 local logging_config;
34 local apply_sink_rules;
35 local log_sink_types = setmetatable({}, { __newindex = function (t, k, v) rawset(t, k, v); apply_sink_rules(k); end; });
36 local get_levels;
37 local logging_levels = { "debug", "info", "warn", "error" }
39 -- Put a rule into action. Requires that the sink type has already been registered.
40 -- This function is called automatically when a new sink type is added [see apply_sink_rules()]
41 local function add_rule(sink_config)
42 local sink_maker = log_sink_types[sink_config.to];
43 if not sink_maker then
44 return; -- No such sink type
45 end
47 -- Create sink
48 local sink = sink_maker(sink_config);
50 -- Set sink for all chosen levels
51 for level in pairs(get_levels(sink_config.levels or logging_levels)) do
52 logger.add_level_sink(level, sink);
53 end
54 end
56 -- Search for all rules using a particular sink type, and apply
57 -- them. Called automatically when a new sink type is added to
58 -- the log_sink_types table.
59 function apply_sink_rules(sink_type)
60 if type(logging_config) == "table" then
62 for _, level in ipairs(logging_levels) do
63 if type(logging_config[level]) == "string" then
64 local value = logging_config[level];
65 if sink_type == "file" and not value:match("^%*") then
66 add_rule({
67 to = sink_type;
68 filename = value;
69 timestamps = true;
70 levels = { min = level };
71 });
72 elseif value == "*"..sink_type then
73 add_rule({
74 to = sink_type;
75 levels = { min = level };
76 });
77 end
78 end
79 end
81 for _, sink_config in ipairs(logging_config) do
82 if (type(sink_config) == "table" and sink_config.to == sink_type) then
83 add_rule(sink_config);
84 elseif (type(sink_config) == "string" and sink_config:match("^%*(.+)") == sink_type) then
85 add_rule({ levels = { min = "debug" }, to = sink_type });
86 end
87 end
88 elseif type(logging_config) == "string" and (not logging_config:match("^%*")) and sink_type == "file" then
89 -- User specified simply a filename, and the "file" sink type
90 -- was just added
91 for _, sink_config in pairs(default_file_logging) do
92 sink_config.filename = logging_config;
93 add_rule(sink_config);
94 sink_config.filename = nil;
95 end
96 elseif type(logging_config) == "string" and logging_config:match("^%*(.+)") == sink_type then
97 -- Log all levels (debug+) to this sink
98 add_rule({ levels = { min = "debug" }, to = sink_type });
99 end
104 --- Helper function to get a set of levels given a "criteria" table
105 function get_levels(criteria, set)
106 set = set or {};
107 if type(criteria) == "string" then
108 set[criteria] = true;
109 return set;
111 local min, max = criteria.min, criteria.max;
112 if min or max then
113 local in_range;
114 for _, level in ipairs(logging_levels) do
115 if min == level then
116 set[level] = true;
117 in_range = true;
118 elseif max == level then
119 set[level] = true;
120 return set;
121 elseif in_range then
122 set[level] = true;
127 for _, level in ipairs(criteria) do
128 set[level] = true;
130 return set;
133 -- Initialize config, etc. --
134 local function reload_logging()
135 local old_sink_types = {};
137 for name, sink_maker in pairs(log_sink_types) do
138 old_sink_types[name] = sink_maker;
139 log_sink_types[name] = nil;
142 logger.reset();
144 local debug_mode = config.get("*", "debug");
146 default_logging = { { to = "console" , levels = { min = (debug_mode and "debug") or "info" } } };
147 default_file_logging = {
148 { to = "file", levels = { min = (debug_mode and "debug") or "info" }, timestamps = true }
151 logging_config = config.get("*", "log") or default_logging;
153 for name, sink_maker in pairs(old_sink_types) do
154 log_sink_types[name] = sink_maker;
158 --- Definition of built-in logging sinks ---
160 -- Null sink, must enter log_sink_types *first*
161 local function log_to_nowhere()
162 return function () return false; end;
164 log_sink_types.nowhere = log_to_nowhere;
166 local function log_to_file(sink_config, logfile)
167 logfile = logfile or io_open(sink_config.filename, "a+");
168 if not logfile then
169 return log_to_nowhere(sink_config);
171 local write = logfile.write;
173 local timestamps = sink_config.timestamps;
175 if timestamps == true or timestamps == nil then
176 timestamps = default_timestamp; -- Default format
177 elseif timestamps then
178 timestamps = timestamps .. " ";
181 if sink_config.buffer_mode ~= false then
182 logfile:setvbuf(sink_config.buffer_mode or "line");
185 -- Column width for "source" (used by stdout and console)
186 local sourcewidth = sink_config.source_width;
188 if sourcewidth then
189 return function (name, level, message, ...)
190 sourcewidth = math_max(#name+2, sourcewidth);
191 write(logfile, timestamps and os_date(timestamps) or "", name, rep(" ", sourcewidth-#name), level, "\t", format(message, ...), "\n");
193 else
194 return function (name, level, message, ...)
195 write(logfile, timestamps and os_date(timestamps) or "", name, "\t", level, "\t", format(message, ...), "\n");
199 log_sink_types.file = log_to_file;
201 local function log_to_stdout(sink_config)
202 if not sink_config.timestamps then
203 sink_config.timestamps = false;
205 if sink_config.source_width == nil then
206 sink_config.source_width = 20;
208 return log_to_file(sink_config, stdout);
210 log_sink_types.stdout = log_to_stdout;
212 local do_pretty_printing = true;
214 local logstyles;
215 if do_pretty_printing then
216 logstyles = {};
217 logstyles["info"] = getstyle("bold");
218 logstyles["warn"] = getstyle("bold", "yellow");
219 logstyles["error"] = getstyle("bold", "red");
222 local function log_to_console(sink_config)
223 -- Really if we don't want pretty colours then just use plain stdout
224 local logstdout = log_to_stdout(sink_config);
225 if not do_pretty_printing then
226 return logstdout;
228 return function (name, level, message, ...)
229 local logstyle = logstyles[level];
230 if logstyle then
231 level = getstring(logstyle, level);
233 return logstdout(name, level, message, ...);
236 log_sink_types.console = log_to_console;
238 if have_pposix then
239 local syslog_opened;
240 local function log_to_syslog(sink_config) -- luacheck: ignore 212/sink_config
241 if not syslog_opened then
242 local facility = sink_config.syslog_facility or config.get("*", "syslog_facility");
243 pposix.syslog_open(sink_config.syslog_name or "prosody", facility);
244 syslog_opened = true;
246 local syslog = pposix.syslog_log;
247 return function (name, level, message, ...)
248 syslog(level, name, format(message, ...));
249 end;
251 log_sink_types.syslog = log_to_syslog;
254 local function register_sink_type(name, sink_maker)
255 local old_sink_maker = log_sink_types[name];
256 log_sink_types[name] = sink_maker;
257 return old_sink_maker;
260 return {
261 reload_logging = reload_logging;
262 register_sink_type = register_sink_type;