mod_log_ringbuffer/mod_log_ringbuffer.lua
changeset 4230 df2ccb42a241
parent 4229 d6fb9f7afaa5
child 5856 133b23758cf6
equal deleted inserted replaced
4229:d6fb9f7afaa5 4230:df2ccb42a241
     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);