diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp index 4cc8bae8..01b3019c 100644 --- a/userspace/falco/falco.cpp +++ b/userspace/falco/falco.cpp @@ -75,6 +75,7 @@ static void display_fatal_err(const string &msg, bool daemon) string lua_on_event = "on_event"; string lua_add_output = "add_output"; +string lua_print_stats = "print_stats"; // Splitting into key=value or key.subkey=value will be handled by configuration class. std::list cmdline_options; @@ -211,6 +212,26 @@ void add_output(lua_State *ls, output_config oc) } +// Print statistics on the the rules that triggered +void print_stats(lua_State *ls) +{ + lua_getglobal(ls, lua_print_stats.c_str()); + + if(lua_isfunction(ls, -1)) + { + if(lua_pcall(ls, 0, 0, 0) != 0) + { + const char* lerr = lua_tostring(ls, -1); + string err = "Error invoking function print_stats: " + string(lerr); + throw sinsp_exception(err); + } + } + else + { + throw sinsp_exception("No function " + lua_print_stats + " found in lua rule loader module"); + } + +} // // ARGUMENT PARSING AND PROGRAM SETUP @@ -504,6 +525,8 @@ int falco_init(int argc, char **argv) ls); inspector->close(); + + print_stats(ls); } catch(sinsp_exception& e) { diff --git a/userspace/falco/lua/output.lua b/userspace/falco/lua/output.lua index 78573b94..0bef1712 100644 --- a/userspace/falco/lua/output.lua +++ b/userspace/falco/lua/output.lua @@ -2,6 +2,8 @@ local mod = {} levels = {"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Informational", "Debug"} +mod.levels = levels + local outputs = {} function mod.stdout(evt, level, format) diff --git a/userspace/falco/lua/rule_loader.lua b/userspace/falco/lua/rule_loader.lua index f5cc8882..0e041f7e 100644 --- a/userspace/falco/lua/rule_loader.lua +++ b/userspace/falco/lua/rule_loader.lua @@ -230,12 +230,49 @@ function describe_rule(name) end end +local rule_output_counts = {by_level={}, by_name={}} + +for idx, level in ipairs(output.levels) do + rule_output_counts[level] = 0 +end + function on_event(evt_, rule_id) if state.rules_by_idx[rule_id] == nil then error ("rule_loader.on_event(): event with invalid rule_id: ", rule_id) end - output.event(evt_, state.rules_by_idx[rule_id].level, state.rules_by_idx[rule_id].output) + local rule = state.rules_by_idx[rule_id] + + if rule_output_counts.by_level[rule.level] == nil then + rule_output_counts.by_level[rule.level] = 1 + else + rule_output_counts.by_level[rule.level] = rule_output_counts.by_level[rule.level] + 1 + end + + if rule_output_counts.by_name[rule.rule] == nil then + rule_output_counts.by_name[rule.rule] = 1 + else + rule_output_counts.by_name[rule.rule] = rule_output_counts.by_name[rule.rule] + 1 + end + + output.event(evt_, rule.level, rule.output) end +function print_stats() + print("Rule counts by severity:") + for idx, level in ipairs(output.levels) do + -- To keep the output concise, we only print 0 counts for error, warning, and info levels + if rule_output_counts[level] > 0 or level == "Error" or level == "Warning" or level == "Informational" then + print (" "..level..": "..rule_output_counts[level]) + end + end + + print("Triggered rules by rule name:") + for name, count in pairs(rule_output_counts.by_name) do + print (" "..name..": "..count) + end +end + + +