4 -- |
4 -- |
5 -- This project is MIT/X11 licensed. Please see the |
5 -- This project is MIT/X11 licensed. Please see the |
6 -- COPYING file in the source package for more information. |
6 -- COPYING file in the source package for more information. |
7 -- |
7 -- |
8 |
8 |
9 local format, rep = string.format, string.rep; |
|
10 local io_write = io.write; |
|
11 local pcall = pcall; |
9 local pcall = pcall; |
12 local debug = debug; |
|
13 local tostring = tostring; |
|
14 local math_max = math.max; |
|
15 |
10 |
16 local config = require "core.configmanager"; |
11 local config = require "core.configmanager"; |
17 local log_sources = config.get("*", "core", "log_sources"); |
12 local log_sources = config.get("*", "core", "log_sources"); |
18 |
13 |
19 local getstyle, getstring = require "util.termcolours".getstyle, require "util.termcolours".getstring; |
|
20 local do_pretty_printing = not os.getenv("WINDIR"); |
|
21 local find = string.find; |
14 local find = string.find; |
22 local ipairs = ipairs; |
15 local ipairs, pairs, setmetatable = ipairs, pairs, setmetatable; |
23 |
16 |
24 module "logger" |
17 module "logger" |
25 |
18 |
26 local logstyles = {}; |
19 local name_sinks, level_sinks = {}, {}; |
|
20 local name_patterns = {}; |
27 |
21 |
28 --TODO: This should be done in config, but we don't have proper config yet |
22 -- Weak-keyed so that loggers are collected |
29 if do_pretty_printing then |
23 local modify_hooks = setmetatable({}, { __mode = "k" }); |
30 logstyles["info"] = getstyle("bold"); |
|
31 logstyles["warn"] = getstyle("bold", "yellow"); |
|
32 logstyles["error"] = getstyle("bold", "red"); |
|
33 end |
|
34 |
24 |
35 local sourcewidth = 20; |
25 local make_logger; |
36 |
|
37 local outfunction = nil; |
26 local outfunction = nil; |
38 |
27 |
39 function init(name) |
28 function init(name) |
40 if log_sources then |
29 if log_sources then |
41 local log_this = false; |
30 local log_this = false; |
47 end |
36 end |
48 |
37 |
49 if not log_this then return function () end end |
38 if not log_this then return function () end end |
50 end |
39 end |
51 |
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 |
52 --name = nil; -- While this line is not commented, will automatically fill in file/line number info |
46 --name = nil; -- While this line is not commented, will automatically fill in file/line number info |
53 local namelen = #name; |
47 local namelen = #name; |
54 return function (level, message, ...) |
48 return function (level, message, ...) |
55 if outfunction then return outfunction(name, level, message, ...); end |
49 if outfunction then return outfunction(name, level, message, ...); end |
56 |
50 |
57 sourcewidth = math_max(#name+2, sourcewidth); |
51 if level == "debug" then |
58 if ... then |
52 return log_debug(message, ...); |
59 io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", format(message, ...), "\n"); |
53 elseif level == "info" then |
60 else |
54 return log_info(message, ...); |
61 io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", message, "\n"); |
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; |
62 end |
80 end |
63 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; |
64 end |
93 end |
65 |
94 |
66 function setwriter(f) |
95 function setwriter(f) |
67 local old_func = outfunction; |
96 local old_func = outfunction; |
68 if not f then outfunction = nil; return true, old_func; end |
97 if not f then outfunction = nil; return true, old_func; end |
72 ret = old_func; |
101 ret = old_func; |
73 end |
102 end |
74 return ok, ret; |
103 return ok, ret; |
75 end |
104 end |
76 |
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 |
77 return _M; |
140 return _M; |