2 |
2 |
3 local loggingmanager = require "core.loggingmanager"; |
3 local loggingmanager = require "core.loggingmanager"; |
4 local format = require "util.format".format; |
4 local format = require "util.format".format; |
5 local pposix = require "util.pposix"; |
5 local pposix = require "util.pposix"; |
6 local rb = require "util.ringbuffer"; |
6 local rb = require "util.ringbuffer"; |
|
7 local queue = require "util.queue"; |
7 |
8 |
8 local default_timestamp = "%b %d %H:%M:%S "; |
9 local default_timestamp = "%b %d %H:%M:%S "; |
9 local max_chunk_size = module:get_option_number("log_ringbuffer_chunk_size", 16384); |
10 local max_chunk_size = module:get_option_number("log_ringbuffer_chunk_size", 16384); |
10 |
11 |
11 local os_date = os.date; |
12 local os_date = os.date; |
20 end; |
21 end; |
21 }); |
22 }); |
22 |
23 |
23 local dump_count = 0; |
24 local dump_count = 0; |
24 |
25 |
25 local function dump_buffer(buffer, filename) |
26 local function dump_buffer(dump, filename) |
26 dump_count = dump_count + 1; |
27 dump_count = dump_count + 1; |
27 local f, err = io.open(filename, "a+"); |
28 local f, err = io.open(filename, "a+"); |
28 if not f then |
29 if not f then |
29 module:log("error", "Unable to open output file: %s", err); |
30 module:log("error", "Unable to open output file: %s", err); |
30 return; |
31 return; |
31 end |
32 end |
32 local bytes_remaining = buffer:length(); |
33 f:write(("-- Dumping log buffer at %s --\n"):format(os_date(default_timestamp))); |
33 f:write(("-- Dumping %d bytes at %s --\n"):format(bytes_remaining, os_date(default_timestamp))); |
34 dump(f); |
34 while bytes_remaining > 0 do |
|
35 local chunk_size = math.min(bytes_remaining, max_chunk_size); |
|
36 local chunk = buffer:read(chunk_size); |
|
37 if not chunk then |
|
38 f:write("-- Dump aborted due to error --\n\n"); |
|
39 f:close(); |
|
40 return; |
|
41 end |
|
42 f:write(chunk); |
|
43 bytes_remaining = bytes_remaining - chunk_size; |
|
44 end |
|
45 f:write("-- End of dump --\n\n"); |
35 f:write("-- End of dump --\n\n"); |
46 f:close(); |
36 f:close(); |
47 end |
37 end |
48 |
38 |
49 local function get_filename(filename_template) |
39 local function get_filename(filename_template) |
54 count = dump_count; |
44 count = dump_count; |
55 time = os.time(); |
45 time = os.time(); |
56 }); |
46 }); |
57 end |
47 end |
58 |
48 |
|
49 local function new_buffer(config) |
|
50 local write, dump; |
|
51 |
|
52 if config.lines then |
|
53 local buffer = queue.new(config.lines, true); |
|
54 function write(line) |
|
55 buffer:push(line); |
|
56 end |
|
57 function dump(f) |
|
58 -- COMPAT w/0.11 - update to use :consume() |
|
59 for line in buffer.pop, buffer do |
|
60 f:write(line); |
|
61 end |
|
62 end |
|
63 else |
|
64 local buffer_size = config.size or 100*1024; |
|
65 local buffer = rb.new(buffer_size); |
|
66 function write(line) |
|
67 if not buffer:write(line) then |
|
68 if #line > buffer_size then |
|
69 buffer:discard(buffer_size); |
|
70 buffer:write(line:sub(-buffer_size)); |
|
71 else |
|
72 buffer:discard(#line); |
|
73 buffer:write(line); |
|
74 end |
|
75 end |
|
76 end |
|
77 function dump(f) |
|
78 local bytes_remaining = buffer:length(); |
|
79 while bytes_remaining > 0 do |
|
80 local chunk_size = math.min(bytes_remaining, max_chunk_size); |
|
81 local chunk = buffer:read(chunk_size); |
|
82 if not chunk then |
|
83 return; |
|
84 end |
|
85 f:write(chunk); |
|
86 bytes_remaining = bytes_remaining - chunk_size; |
|
87 end |
|
88 end |
|
89 end |
|
90 return write, dump; |
|
91 end |
|
92 |
59 local function ringbuffer_log_sink_maker(sink_config) |
93 local function ringbuffer_log_sink_maker(sink_config) |
60 local buffer_size = sink_config.size or 100*1024; |
94 local write, dump = new_buffer(sink_config); |
61 local buffer = rb.new(buffer_size); |
|
62 |
95 |
63 local timestamps = sink_config.timestamps; |
96 local timestamps = sink_config.timestamps; |
64 |
97 |
65 if timestamps == true or timestamps == nil then |
98 if timestamps == true or timestamps == nil then |
66 timestamps = default_timestamp; -- Default format |
99 timestamps = default_timestamp; -- Default format |
67 elseif timestamps then |
100 elseif timestamps then |
68 timestamps = timestamps .. " "; |
101 timestamps = timestamps .. " "; |
69 end |
102 end |
70 |
103 |
71 local function dump() |
104 local function handler() |
72 dump_buffer(buffer, sink_config.filename or get_filename(sink_config.filename_template)); |
105 dump_buffer(dump, sink_config.filename or get_filename(sink_config.filename_template)); |
73 end |
106 end |
74 |
107 |
75 if sink_config.signal then |
108 if sink_config.signal then |
76 require "util.signal".signal(sink_config.signal, dump); |
109 require "util.signal".signal(sink_config.signal, handler); |
77 elseif sink_config.event then |
110 elseif sink_config.event then |
78 module:hook_global(sink_config.event, dump); |
111 module:hook_global(sink_config.event, handler); |
79 end |
112 end |
80 |
113 |
81 return function (name, level, message, ...) |
114 return function (name, level, message, ...) |
82 local line = format("%s%s\t%s\t%s\n", timestamps and os_date(timestamps) or "", name, level, format(message, ...)); |
115 local line = format("%s%s\t%s\t%s\n", timestamps and os_date(timestamps) or "", name, level, format(message, ...)); |
83 if not buffer:write(line) then |
116 write(line); |
84 if #line > buffer_size then |
|
85 buffer:discard(buffer_size); |
|
86 buffer:write(line:sub(-buffer_size)); |
|
87 else |
|
88 buffer:discard(#line); |
|
89 buffer:write(line); |
|
90 end |
|
91 end |
|
92 end; |
117 end; |
93 end |
118 end |
94 |
119 |
95 loggingmanager.register_sink_type("ringbuffer", ringbuffer_log_sink_maker); |
120 loggingmanager.register_sink_type("ringbuffer", ringbuffer_log_sink_maker); |