2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
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";
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
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; });
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
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
);
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
70 levels
= { min = level
};
72 elseif value
== "*"..sink_type
then
75 levels
= { min = level
};
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
});
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
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;
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
});
104 --- Helper function to get a set of levels given a "criteria" table
105 function get_levels(criteria
, set
)
107 if type(criteria
) == "string" then
108 set
[criteria
] = true;
111 local min, max = criteria
.min, criteria
.max;
114 for _
, level
in ipairs(logging_levels
) do
118 elseif max == level
then
127 for _
, level
in ipairs(criteria
) do
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;
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+");
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
;
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");
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;
215 if do_pretty_printing
then
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
228 return function (name
, level
, message
, ...)
229 local logstyle
= logstyles
[level
];
231 level
= getstring(logstyle
, level
);
233 return logstdout(name
, level
, message
, ...);
236 log_sink_types
.console
= log_to_console
;
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
, ...));
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
;
261 reload_logging
= reload_logging
;
262 register_sink_type
= register_sink_type
;