changeset 4226:df2ccb42a241

mod_log_ringbuffer: Add 'lines' option (actually an alternative ringbuffer implementation)
author Matthew Wild <mwild1@gmail.com>
date Tue, 20 Oct 2020 15:54:07 +0100
parents d6fb9f7afaa5
children 13b065432bf2
files mod_log_ringbuffer/README.markdown mod_log_ringbuffer/mod_log_ringbuffer.lua
diffstat 2 files changed, 58 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/mod_log_ringbuffer/README.markdown	Tue Oct 20 15:34:29 2020 +0100
+++ b/mod_log_ringbuffer/README.markdown	Tue Oct 20 15:54:07 2020 +0100
@@ -50,6 +50,10 @@
 :   The size, in bytes, of the buffer. When the buffer fills,
     old data will be overwritten by new data.
 
+`lines`
+:   If specified, preserves the latest N complete lines in the
+    buffer. The `size` option is ignored when this option is set.
+
 `filename`
 :   The name of the file to dump logs to when triggered.
 
--- a/mod_log_ringbuffer/mod_log_ringbuffer.lua	Tue Oct 20 15:34:29 2020 +0100
+++ b/mod_log_ringbuffer/mod_log_ringbuffer.lua	Tue Oct 20 15:54:07 2020 +0100
@@ -4,6 +4,7 @@
 local format = require "util.format".format;
 local pposix = require "util.pposix";
 local rb = require "util.ringbuffer";
+local queue = require "util.queue";
 
 local default_timestamp = "%b %d %H:%M:%S ";
 local max_chunk_size = module:get_option_number("log_ringbuffer_chunk_size", 16384);
@@ -22,26 +23,15 @@
 
 local dump_count = 0;
 
-local function dump_buffer(buffer, filename)
+local function dump_buffer(dump, filename)
 	dump_count = dump_count + 1;
 	local f, err = io.open(filename, "a+");
 	if not f then
 		module:log("error", "Unable to open output file: %s", err);
 		return;
 	end
-	local bytes_remaining = buffer:length();
-	f:write(("-- Dumping %d bytes at %s --\n"):format(bytes_remaining, os_date(default_timestamp)));
-	while bytes_remaining > 0 do
-		local chunk_size = math.min(bytes_remaining, max_chunk_size);
-		local chunk = buffer:read(chunk_size);
-		if not chunk then
-			f:write("-- Dump aborted due to error --\n\n");
-			f:close();
-			return;
-		end
-		f:write(chunk);
-		bytes_remaining = bytes_remaining - chunk_size;
-	end
+	f:write(("-- Dumping log buffer at %s --\n"):format(os_date(default_timestamp)));
+	dump(f);
 	f:write("-- End of dump --\n\n");
 	f:close();
 end
@@ -56,9 +46,52 @@
 	});
 end
 
+local function new_buffer(config)
+	local write, dump;
+
+	if config.lines then
+		local buffer = queue.new(config.lines, true);
+		function write(line)
+			buffer:push(line);
+		end
+		function dump(f)
+			-- COMPAT w/0.11 - update to use :consume()
+			for line in buffer.pop, buffer do
+				f:write(line);
+			end
+		end
+	else
+		local buffer_size = config.size or 100*1024;
+		local buffer = rb.new(buffer_size);
+		function write(line)
+			if not buffer:write(line) then
+				if #line > buffer_size then
+					buffer:discard(buffer_size);
+					buffer:write(line:sub(-buffer_size));
+				else
+					buffer:discard(#line);
+					buffer:write(line);
+				end
+			end
+		end
+		function dump(f)
+			local bytes_remaining = buffer:length();
+			while bytes_remaining > 0 do
+				local chunk_size = math.min(bytes_remaining, max_chunk_size);
+				local chunk = buffer:read(chunk_size);
+				if not chunk then
+					return;
+				end
+				f:write(chunk);
+				bytes_remaining = bytes_remaining - chunk_size;
+			end
+		end
+	end
+	return write, dump;
+end
+
 local function ringbuffer_log_sink_maker(sink_config)
-	local buffer_size = sink_config.size or 100*1024;
-	local buffer = rb.new(buffer_size);
+	local write, dump = new_buffer(sink_config);
 
 	local timestamps = sink_config.timestamps;
 
@@ -68,27 +101,19 @@
 		timestamps = timestamps .. " ";
 	end
 
-	local function dump()
-		dump_buffer(buffer, sink_config.filename or get_filename(sink_config.filename_template));
+	local function handler()
+		dump_buffer(dump, sink_config.filename or get_filename(sink_config.filename_template));
 	end
 
 	if sink_config.signal then
-		require "util.signal".signal(sink_config.signal, dump);
+		require "util.signal".signal(sink_config.signal, handler);
 	elseif sink_config.event then
-		module:hook_global(sink_config.event, dump);
+		module:hook_global(sink_config.event, handler);
 	end
 
 	return function (name, level, message, ...)
 		local line = format("%s%s\t%s\t%s\n", timestamps and os_date(timestamps) or "", name, level, format(message, ...));
-		if not buffer:write(line) then
-			if #line > buffer_size then
-				buffer:discard(buffer_size);
-				buffer:write(line:sub(-buffer_size));
-			else
-				buffer:discard(#line);
-				buffer:write(line);
-			end
-		end
+		write(line);
 	end;
 end