local exports = {}
+local isatty = rspamd_util.isatty()
+
local decompressor = {
bz2 = 'bzip2 -cd',
gz = 'gzip -cd',
local spinner_update_time = 0
function exports.spinner()
+ if not isatty then
+ return
+ end
local now = os.time()
if (now - spinner_update_time) < 1 then
return
if not h then
io.stderr:write(string.format("Cannot open %s: %s\n", path, open_err or 'unknown error'))
else
- io.stderr:write(string.format("\027[J Parsing log files: [%d/%d] %s\027[G",
- idx, #logs, fname))
+ if isatty then
+ io.stderr:write(string.format("\027[J Parsing log files: [%d/%d] %s\027[G",
+ idx, #logs, fname))
+ else
+ io.stderr:write(string.format(" Parsing log files: [%d/%d] %s\n",
+ idx, #logs, fname))
+ end
exports.reset_spinner()
exports.spinner()
exports.iterate_log(h, start_time, end_time, callback, opts)
h:close()
end
end
- io.stderr:write("\027[J\027[G")
+ if isatty then
+ io.stderr:write("\027[J\027[G")
+ end
else
local h, open_err = exports.open_log_file(log_file)
if not h then
local rspamd_regexp = require "rspamd_regexp"
local ucl = require "ucl"
local log_utils = require "lua_log_utils"
+local ansicolors = require "ansicolors"
+
+local action_colors = {
+ reject = ansicolors.red,
+ ['add header'] = ansicolors.yellow,
+ ['rewrite subject'] = ansicolors.yellow,
+ ['soft reject'] = ansicolors.magenta,
+ greylist = ansicolors.cyan,
+ ['no action'] = ansicolors.green,
+}
local parser = argparse()
:name "rspamadm logstats"
local jtp = (total_junk ~= 0) and (jh * 100.0 / total_junk) or 0
io.write(string.format(
- "%s avg. weight %.3f, hits %d(%.3f%%):\n" ..
- " Ham %7.3f%%, %6d/%-6d (%7.3f%%)\n" ..
- " Spam %7.3f%%, %6d/%-6d (%7.3f%%)\n" ..
- " Junk %7.3f%%, %6d/%-6d (%7.3f%%)\n",
- s, r.weight / th, th, (th / total * 100),
+ "%s avg. weight %.3f, hits %d (%.3f%%):\n" ..
+ " %s %7.3f%%, %6d/%-6d (%7.3f%%)\n" ..
+ " %s %7.3f%%, %6d/%-6d (%7.3f%%)\n" ..
+ " %s %7.3f%%, %6d/%-6d (%7.3f%%)\n",
+ ansicolors.bright .. s .. ansicolors.reset,
+ r.weight / th, th, (th / total * 100),
+ ansicolors.green .. "Ham " .. ansicolors.reset,
(hh / th * 100), hh, total_ham, htp,
+ ansicolors.red .. "Spam" .. ansicolors.reset,
(sh / th * 100), sh, total_spam, stp,
+ ansicolors.yellow .. "Junk" .. ansicolors.reset,
(jh / th * 100), jh, total_junk, jtp))
local schp = (total_spam > 0) and (r.spam_change / total_spam * 100.0) or 0
if next(alpha_filtered) then
io.write(string.format(
- "\nWARNING: the following symbols were found but ignored" ..
- " due to score < alpha_score (%.2f):\n", diff_alpha))
+ "\n%s the following symbols were found but ignored" ..
+ " due to score < alpha_score (%.2f):\n",
+ ansicolors.yellow .. "WARNING:" .. ansicolors.reset, diff_alpha))
for sym, count in pairs(alpha_filtered) do
- io.write(string.format(" %s: %d hit(s)\n", sym, count))
+ io.write(string.format(" %s: %d hit(s)\n",
+ ansicolors.bright .. sym .. ansicolors.reset, count))
end
io.write("Use --alpha-score 0 to include them.\n")
end
- io.write(string.format("\n=== Summary %s\nMessages scanned: %d",
- string.rep('=', 68), total))
+ io.write(string.format("\n%s\nMessages scanned: %d",
+ ansicolors.bright .. "=== Summary " .. string.rep('=', 68) .. ansicolors.reset,
+ total))
if timeStamp['start'] then
io.write(string.format(" [ %s / %s ]\n", timeStamp['start'], timeStamp['end']))
else
end
table.sort(sorted_actions)
for _, a in ipairs(sorted_actions) do
- io.write(string.format("%11s: %6.2f%%, %d\n", a, 100 * actions[a] / total, actions[a]))
+ local color = action_colors[a] or ansicolors.white
+ io.write(string.format("%s: %6.2f%%, %d\n",
+ color .. string.format("%11s", a) .. ansicolors.reset,
+ 100 * actions[a] / total, actions[a]))
end
io.write('\n')
if scanTime['min'] then
local rspamd_regexp = require "rspamd_regexp"
local rspamd_ip = require "rspamd_ip"
local log_utils = require "lua_log_utils"
+local ansicolors = require "ansicolors"
local parser = argparse()
:name "rspamadm mapstats"
-- Skip non-file maps
if re_non_file_url:match(map_source) then
- io.write(string.format("%s: %s [SKIPPED]\n", symbol, map_source))
+ io.write(string.format("%s: %s %s\n",
+ ansicolors.bright .. symbol .. ansicolors.reset, map_source,
+ ansicolors.yellow .. "[SKIPPED]" .. ansicolors.reset))
goto continue_map
end
local entries = get_map(cfg, file_path)
if #entries == 0 then
- io.write(string.format("%s: %s [FAILED]\n", symbol, map_source))
+ io.write(string.format("%s: %s %s\n",
+ ansicolors.bright .. symbol .. ansicolors.reset, map_source,
+ ansicolors.red .. "[FAILED]" .. ansicolors.reset))
goto continue_map
end
entry_count = entry_count + 1
end
end
- io.write(string.format("%s: %s [OK] - %d entries\n", symbol, map_source, entry_count))
+ io.write(string.format("%s: %s %s - %d entries\n",
+ ansicolors.bright .. symbol .. ansicolors.reset, map_source,
+ ansicolors.green .. "[OK]" .. ansicolors.reset, entry_count))
table.insert(map[symbol].maps, {
source = map_source,
-- Output results
for _, symbol in ipairs(symbols_search) do
- io.write(string.format("%s:\n", symbol))
+ io.write(string.format("%s:\n", ansicolors.bright .. symbol .. ansicolors.reset))
io.write(string.format(" type=%s\n", map[symbol].type))
for _, map_entry in ipairs(map[symbol].maps) do
end
if entry.count and entry.count > 0 then
- io.write(string.format("\t%d", entry.count))
+ io.write(string.format("\t%s",
+ ansicolors.green .. tostring(entry.count) .. ansicolors.reset))
else
io.write("\t-")
end
-- Unmatched report
if next(unmatched) then
- io.write("\nSymbols with unmatched values:\n")
+ io.write(string.format("\n%s\n",
+ ansicolors.yellow .. "Symbols with unmatched values:" .. ansicolors.reset))
io.write(string.rep('-', 80) .. '\n')
local grouped = {}
local entries = grouped[symbol]
table.sort(entries, function(a, b) return a.count > b.count end)
- io.write(string.format("\n%s: %d unmatched value(s)\n", symbol, #entries))
+ io.write(string.format("\n%s: %s\n",
+ ansicolors.bright .. symbol .. ansicolors.reset,
+ ansicolors.yellow .. string.format("%d unmatched value(s)", #entries) .. ansicolors.reset))
local limit = math.min(#entries, 5)
for i = 1, limit do
io.write(string.format(" %dx: %s\n", entries[i].count, entries[i].full))