comparison mod_log_ringbuffer/mod_log_ringbuffer.lua @ 4205:481c4d75e77d

mod_log_ringbuffer: New module to send logs to an in-memory ringbuffer
author Matthew Wild <mwild1@gmail.com>
date Thu, 15 Oct 2020 16:47:21 +0100
parents
children 86f8ece24029
comparison
equal deleted inserted replaced
4204:a5930a185806 4205:481c4d75e77d
1 module:set_global();
2
3 local loggingmanager = require "core.loggingmanager";
4 local format = require "util.format".format;
5 local pposix = require "util.pposix";
6 local rb = require "util.ringbuffer";
7
8 local default_timestamp = "%b %d %H:%M:%S ";
9 local max_chunk_size = module:get_option_number("log_ringbuffer_chunk_size", 16384);
10
11 local os_date = os.date;
12
13 local default_filename_template = "ringbuffer-logs-{pid}-{count}.log";
14 local render_filename = require "util.interpolation".new("%b{}", function (s) return s; end, {
15 yyyymmdd = function (t)
16 return os_date("%Y%m%d", t);
17 end;
18 hhmmss = function (t)
19 return os_date("%H%M%S", t);
20 end;
21 });
22
23 local dump_count = 0;
24
25 local function dump_buffer(buffer, filename)
26 dump_count = dump_count + 1;
27 local f, err = io.open(filename, "a+");
28 if not f then
29 module:log("error", "Unable to open output file: %s", err);
30 return;
31 end
32 local bytes_remaining = buffer:length();
33 f:write(("-- Dumping %d bytes at %s --\n"):format(bytes_remaining, os_date(default_timestamp)));
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");
46 f:close();
47 end
48
49 local function get_filename(filename_template)
50 filename_template = filename_template or default_filename_template;
51 return render_filename(filename_template, {
52 paths = prosody.paths;
53 pid = pposix.getpid();
54 count = dump_count;
55 time = os.time();
56 });
57 end
58
59 local function ringbuffer_log_sink_maker(sink_config)
60 local buffer = rb.new(sink_config.size or 100*1024);
61
62 local timestamps = sink_config.timestamps;
63
64 if timestamps == true or timestamps == nil then
65 timestamps = default_timestamp; -- Default format
66 elseif timestamps then
67 timestamps = timestamps .. " ";
68 end
69
70 local function dump()
71 dump_buffer(buffer, get_filename(sink_config.filename));
72 end
73
74 if sink_config.signal then
75 require "util.signal".signal(sink_config.signal, dump);
76 elseif sink_config.event then
77 module:hook_global(sink_config.global_event, dump);
78 end
79
80 return function (name, level, message, ...)
81 buffer:write(format("%s%s\t%s\t%s\n", timestamps and os_date(timestamps) or "", name, level, format(message, ...)));
82 end;
83 end
84
85 loggingmanager.register_sink_type("ringbuffer", ringbuffer_log_sink_maker);