Comparison

core/loggingmanager.lua @ 1031:ec013f93de81

core.loggingmanager: Refactoring, converted to a module. Now possible to register additional sink types (think syslog) from other modules
author Matthew Wild <mwild1@gmail.com>
date Wed, 22 Apr 2009 19:49:58 +0100
parent 1024:1bcc8ca57a7c
child 1046:6fef969ff307
comparison
equal deleted inserted replaced
1030:a82268d507fc 1031:ec013f93de81
1 1
2 local format, rep = string.format, string.rep; 2 local format, rep = string.format, string.rep;
3 local io_write = io.write;
4 local pcall = pcall; 3 local pcall = pcall;
5 local debug = debug; 4 local debug = debug;
6 local tostring = tostring; 5 local tostring, setmetatable, rawset, pairs, ipairs, type =
7 local math_max = math.max; 6 tostring, setmetatable, rawset, pairs, ipairs, type;
7 local io_open, io_write = io.open, io.write;
8 local math_max, rep = math.max, string.rep;
9 local os_getenv = os.getenv;
10 local getstyle, getstring = require "util.termcolours".getstyle, require "util.termcolours".getstring;
11
12 local config = require "core.configmanager";
8 13
9 local logger = require "util.logger"; 14 local logger = require "util.logger";
10 15
16 module "loggingmanager"
17
18 -- The log config used if none specified in the config file
11 local default_logging = { { to = "console" } }; 19 local default_logging = { { to = "console" } };
12 20
13 -- Global log function, because some people are too 21 -- The actual config loggingmanager is using
14 -- lazy to get their own... 22 local logging_config = config.get("*", "core", "log") or default_logging;
15 _G.log = logger.init("general");
16 23
17 local log_sink_types = {}; 24 local apply_sink_rules;
25 local log_sink_types = setmetatable({}, { __newindex = function (t, k, v) rawset(t, k, v); apply_sink_rules(k); end; });
18 local get_levels; 26 local get_levels;
19 local logging_levels = { "debug", "info", "warn", "error", "critical" } 27 local logging_levels = { "debug", "info", "warn", "error", "critical" }
20 28
21 --- Main function to read config, create the appropriate sinks and tell logger module 29 local function add_rule(sink_config)
22 function setup_logging(log) 30 local sink_maker = log_sink_types[sink_config.to];
23 log = log or config.get("*", "core", "log") or default_logging; 31 if sink_maker then
24 -- Set default logger 32 if sink_config.levels and not sink_config.source then
25 if type(log) == "string" then 33 -- Create sink
26 if not log:match("^%*") then 34 local sink = sink_maker(sink_config);
27 end 35
28 elseif type(log) == "table" then 36 -- Set sink for all chosen levels
29 -- Advanced configuration chosen 37 for level in pairs(get_levels(sink_config.levels)) do
30 for i, sink_config in ipairs(log) do 38 logger.add_level_sink(level, sink);
31 local sink_maker = log_sink_types[sink_config.to]; 39 end
32 if sink_maker then 40 elseif sink_config.source and not sink_config.levels then
33 if sink_config.levels and not sink_config.source then 41 logger.add_name_sink(sink_config.source, sink_maker(sink_config));
34 -- Create sink 42 elseif sink_config.source and sink_config.levels then
35 local sink = sink_maker(sink_config); 43 local levels = get_levels(sink_config.levels);
36 44 local sink = sink_maker(sink_config);
37 -- Set sink for all chosen levels 45 logger.add_name_sink(sink_config.source,
38 for level in pairs(get_levels(sink_config.levels)) do 46 function (name, level, ...)
39 logger.add_level_sink(level, sink); 47 if levels[level] then
48 return sink(name, level, ...);
40 end 49 end
41 elseif sink_config.source and not sink_config.levels then 50 end);
42 logger.add_name_sink(sink_config.source, sink_maker(sink_config)); 51 else
43 elseif sink_config.source and sink_config.levels then 52 -- All sources
44 local levels = get_levels(sink_config.levels); 53 -- Create sink
45 local sink = sink_maker(sink_config); 54 local sink = sink_maker(sink_config);
46 logger.add_name_sink(sink_config.source, 55
47 function (name, level, ...) 56 -- Set sink for all levels
48 if levels[level] then 57 for _, level in pairs(logging_levels) do
49 return sink(name, level, ...); 58 logger.add_level_sink(level, sink);
50 end
51 end);
52 else
53 -- All sources
54 -- Create sink
55 local sink = sink_maker(sink_config);
56
57 -- Set sink for all levels
58 for _, level in pairs(logging_levels) do
59 logger.add_level_sink(level, sink);
60 end
61 end
62 else
63 -- No such sink type
64 end 59 end
65 end 60 end
61 else
62 -- No such sink type
66 end 63 end
67 end 64 end
68 65
69 --- Definition of built-in logging sinks --- 66 -- Search for all rules using a particular sink type,
70 local math_max, rep = math.max, string.rep; 67 -- and apply them
71 68 function apply_sink_rules(sink_type)
72 -- Column width for "source" (used by stdout and console) 69 if type(logging_config) == "table" then
73 70 for _, sink_config in pairs(logging_config) do
74 function log_sink_types.nowhere() 71 if sink_config.to == sink_type then
75 return function () return false; end; 72 add_rule(sink_config);
76 end
77
78 local sourcewidth = 20;
79
80 function log_sink_types.stdout()
81 return function (name, level, message, ...)
82 sourcewidth = math_max(#name+2, sourcewidth);
83 local namelen = #name;
84 if ... then
85 io_write(name, rep(" ", sourcewidth-namelen), level, "\t", format(message, ...), "\n");
86 else
87 io_write(name, rep(" ", sourcewidth-namelen), level, "\t", message, "\n");
88 end
89 end
90 end
91
92 do
93 local getstyle, getstring = require "util.termcolours".getstyle, require "util.termcolours".getstring;
94 local do_pretty_printing = not os.getenv("WINDIR");
95
96 local logstyles = {};
97 if do_pretty_printing then
98 logstyles["info"] = getstyle("bold");
99 logstyles["warn"] = getstyle("bold", "yellow");
100 logstyles["error"] = getstyle("bold", "red");
101 end
102 function log_sink_types.console(config)
103 -- Really if we don't want pretty colours then just use plain stdout
104 if not do_pretty_printing then
105 return log_sink_types.stdout(config);
106 end
107
108 return function (name, level, message, ...)
109 sourcewidth = math_max(#name+2, sourcewidth);
110 local namelen = #name;
111 if ... then
112 io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", format(message, ...), "\n");
113 else
114 io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", message, "\n");
115 end 73 end
116 end 74 end
75 elseif type(logging_config) == "string" and sink_type == "file" then
76 -- User specified simply a filename, and the "file" sink type
77 -- was just added
117 end 78 end
118 end 79 end
119 80
120 function log_sink_types.file(config)
121 local log = config.filename;
122 local logfile = io.open(log, "a+");
123 if not logfile then
124 return function () end
125 end
126 81
127 local write, format, flush = logfile.write, string.format, logfile.flush;
128 return function (name, level, message, ...)
129 if ... then
130 write(logfile, name, "\t", level, "\t", format(message, ...), "\n");
131 else
132 write(logfile, name, "\t" , level, "\t", message, "\n");
133 end
134 flush(logfile);
135 end;
136 end
137
138 function log_sink_types.syslog()
139 end
140 82
141 --- Helper function to get a set of levels given a "criteria" table 83 --- Helper function to get a set of levels given a "criteria" table
142 function get_levels(criteria, set) 84 function get_levels(criteria, set)
143 set = set or {}; 85 set = set or {};
144 if type(criteria) == "string" then 86 if type(criteria) == "string" then
165 set[level] = true; 107 set[level] = true;
166 end 108 end
167 return set; 109 return set;
168 end 110 end
169 111
170 --- Set up logging 112 --- Definition of built-in logging sinks ---
171 setup_logging();
172 113
114 function log_sink_types.nowhere()
115 return function () return false; end;
116 end
117
118 -- Column width for "source" (used by stdout and console)
119 local sourcewidth = 20;
120
121 function log_sink_types.stdout()
122 return function (name, level, message, ...)
123 sourcewidth = math_max(#name+2, sourcewidth);
124 local namelen = #name;
125 if ... then
126 io_write(name, rep(" ", sourcewidth-namelen), level, "\t", format(message, ...), "\n");
127 else
128 io_write(name, rep(" ", sourcewidth-namelen), level, "\t", message, "\n");
129 end
130 end
131 end
132
133 do
134 local do_pretty_printing = not os_getenv("WINDIR");
135
136 local logstyles = {};
137 if do_pretty_printing then
138 logstyles["info"] = getstyle("bold");
139 logstyles["warn"] = getstyle("bold", "yellow");
140 logstyles["error"] = getstyle("bold", "red");
141 end
142 function log_sink_types.console(config)
143 -- Really if we don't want pretty colours then just use plain stdout
144 if not do_pretty_printing then
145 return log_sink_types.stdout(config);
146 end
147
148 return function (name, level, message, ...)
149 sourcewidth = math_max(#name+2, sourcewidth);
150 local namelen = #name;
151 if ... then
152 io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", format(message, ...), "\n");
153 else
154 io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", message, "\n");
155 end
156 end
157 end
158 end
159
160 function log_sink_types.file(config)
161 local log = config.filename;
162 local logfile = io_open(log, "a+");
163 if not logfile then
164 return function () end
165 end
166
167 local write, format, flush = logfile.write, format, logfile.flush;
168 return function (name, level, message, ...)
169 if ... then
170 write(logfile, name, "\t", level, "\t", format(message, ...), "\n");
171 else
172 write(logfile, name, "\t" , level, "\t", message, "\n");
173 end
174 flush(logfile);
175 end;
176 end
177
178 function register_sink_type(name, sink_maker)
179 local old_sink_maker = log_sink_types[name];
180 log_sink_types[name] = sink_maker;
181 return old_sink_maker;
182 end
183
184 return _M;