Skip to content

Commit

Permalink
Add notion of generic formatters/formatter factories
Browse files Browse the repository at this point in the history
Add the notion of "generic" event formatters and formatter factories
that can create a formatter for a given format string:

- gen_filter.h defines two new classes: gen_event_formatter provides
  the interface to format events:
    - set_format(): set the output format and format string
    - tostring(): resolve the format with info from a gen_event,
      populating a resolved string.
    - tostring_withformat(): like tostring() but with a one-off output
      format.
    - get_field_values(): return all templated field names and values
      from the configured format string.
    - get_output_format(): get the current output format.
- gen_event_formatter_factory performs a similar role as the existing
  sinsp_evt_formatter_cache, in that it maintains a cache of previously
  used format strings/formatters, to avoid the overhead of creating
  formatters. It simply returns a formatter, though, rather than
  duplicating the format methods like sinsp_evt_formatter_cache does.

This can be used in programs like falco to format general purpose
events without having a direct connection to an
inspector/filterchecks/etc.

- The eventformatter changes simply add gen_event_formatter as a
  parent class and implements the interfaces. To aid in backwards
  compatibility with other parts of libsinsp, this only adds new
  methods as needed to conform to the gen_event_formatter
  interface. In some cases, the new methods just call existing methods
  that did the same thing.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
  • Loading branch information
mstemm committed Sep 10, 2021
1 parent 92d3cd4 commit d7b7fb3
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 25 deletions.
118 changes: 96 additions & 22 deletions userspace/libsinsp/eventformatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,27 @@ limitations under the License.
#ifdef HAS_FILTERING
extern sinsp_filter_check_list g_filterlist;

sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector)
: m_inspector(inspector)
{
}

sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector, const string& fmt)
{
m_inspector = inspector;
set_format(fmt);

gen_event_formatter::output_format of = gen_event_formatter::OF_NORMAL;

if(m_inspector->get_buffer_format() == sinsp_evt::PF_JSON
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONEOLS
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEX
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEXASCII
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONBASE64)
{
of = gen_event_formatter::OF_JSON;
}

set_format(of, fmt);
}

sinsp_evt_formatter::~sinsp_evt_formatter()
Expand All @@ -43,12 +60,14 @@ sinsp_evt_formatter::~sinsp_evt_formatter()
}
}

void sinsp_evt_formatter::set_format(const string& fmt)
void sinsp_evt_formatter::set_format(gen_event_formatter::output_format of, const string& fmt)
{
uint32_t j;
uint32_t last_nontoken_str_start = 0;
string lfmt(fmt);

m_output_format = of;

if(lfmt == "")
{
throw sinsp_exception("empty formatting token");
Expand Down Expand Up @@ -200,25 +219,34 @@ bool sinsp_evt_formatter::resolve_tokens(sinsp_evt *evt, map<string,string>& val
return retval;
}

bool sinsp_evt_formatter::get_field_values(gen_event *gevt, std::map<std::string, std::string> &fields)
{
sinsp_evt *evt = static_cast<sinsp_evt *>(gevt);

bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res)
return resolve_tokens(evt, fields);
}

gen_event_formatter::output_format sinsp_evt_formatter::get_output_format()
{
return m_output_format;
}

bool sinsp_evt_formatter::tostring_withformat(gen_event* gevt, std::string &output, gen_event_formatter::output_format of)
{
bool retval = true;
const filtercheck_field_info* fi;

sinsp_evt *evt = static_cast<sinsp_evt *>(gevt);

uint32_t j = 0;
vector<sinsp_filter_check*>::iterator it;
res->clear();
output.clear();

ASSERT(m_tokenlens.size() == m_tokens.size());

for(j = 0; j < m_tokens.size(); j++)
{
if(m_inspector->get_buffer_format() == sinsp_evt::PF_JSON
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONEOLS
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEX
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEXASCII
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONBASE64)
if(of == OF_JSON)
{
Json::Value json_value = m_tokens[j].second->tojson(evt);

Expand Down Expand Up @@ -268,35 +296,41 @@ bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res)
{
string sstr(str);
sstr.resize(tks, ' ');
(*res) += sstr;
output += sstr;
}
else
{
(*res) += str;
output += str;
}
}
}

if(m_inspector->get_buffer_format() == sinsp_evt::PF_JSON
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONEOLS
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEX
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEXASCII
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONBASE64)
if(of == OF_JSON)
{
(*res) = m_writer.write(m_root);
(*res) = res->substr(0, res->size() - 1);
output = m_writer.write(m_root);
output = output.substr(0, output.size() - 1);
}

return retval;
}

bool sinsp_evt_formatter::tostring(gen_event* gevt, std::string &output)
{
return tostring_withformat(gevt, output, m_output_format);
}

bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res)
{
return tostring_withformat(evt, *res, m_output_format);
}

#else // HAS_FILTERING

sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector, const string& fmt)
sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector)
{
}

void sinsp_evt_formatter::set_format(const string& fmt)
void sinsp_evt_formatter::set_format(gen_event_formatter::output_format of, const std::string &format) = 0;
{
throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library");
}
Expand All @@ -306,7 +340,12 @@ bool sinsp_evt_formatter::resolve_tokens(sinsp_evt *evt, map<string,string>& val
throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library");
}

bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res)
bool sinsp_evt_formatter::tostring(gen_event* gevt, std::string &output)
{
throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library");
}

bool sinsp_evt_formatter::tostring_withformat(gen_event* gevt, std::string &output, gen_event_formatter::output_format of)
{
throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library");
}
Expand Down Expand Up @@ -342,5 +381,40 @@ bool sinsp_evt_formatter_cache::resolve_tokens(sinsp_evt *evt, string &format, m

bool sinsp_evt_formatter_cache::tostring(sinsp_evt *evt, string &format, OUT string *res)
{
return get_cached_formatter(format)->tostring(evt, res);
return get_cached_formatter(format)->tostring(evt, *res);
}

sinsp_evt_formatter_factory::sinsp_evt_formatter_factory(sinsp *inspector)
: m_inspector(inspector), m_output_format(gen_event_formatter::OF_NORMAL)
{
}

sinsp_evt_formatter_factory::~sinsp_evt_formatter_factory()
{
}

void sinsp_evt_formatter_factory::set_output_format(gen_event_formatter::output_format of)
{
m_formatters.clear();

m_output_format = of;
}

std::shared_ptr<gen_event_formatter> sinsp_evt_formatter_factory::create_formatter(const std::string &format)
{
auto it = m_formatters.find(format);

if (it != m_formatters.end())
{
return it->second;
}

std::shared_ptr<gen_event_formatter> ret;

ret.reset(new sinsp_evt_formatter(m_inspector));

ret->set_format(m_output_format, format);
m_formatters[format] = ret;

return ret;
}
41 changes: 39 additions & 2 deletions userspace/libsinsp/eventformatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ limitations under the License.
*/

#pragma once
#include <map>
#include <utility>
#include <string>
#include <json/json.h>

class sinsp_filter_check;
Expand All @@ -29,7 +32,7 @@ class sinsp_filter_check;
This class can be used to format an event into a string, based on an arbitrary
format.
*/
class SINSP_PUBLIC sinsp_evt_formatter
class SINSP_PUBLIC sinsp_evt_formatter : public gen_event_formatter
{
public:
/*!
Expand All @@ -41,8 +44,12 @@ class SINSP_PUBLIC sinsp_evt_formatter
as the one of the sysdig '-p' command line flag, so refer to the sysdig
manual for details.
*/
sinsp_evt_formatter(sinsp* inspector);

sinsp_evt_formatter(sinsp* inspector, const string& fmt);

void set_format(gen_event_formatter::output_format of, const string& fmt) override;

~sinsp_evt_formatter();

/*!
Expand All @@ -57,6 +64,12 @@ class SINSP_PUBLIC sinsp_evt_formatter
*/
bool resolve_tokens(sinsp_evt *evt, map<string,string>& values);

// For compatibility with gen_event_filter_factory
// interface. It just calls resolve_tokens().
bool get_field_values(gen_event *evt, std::map<std::string, std::string> &fields) override;

gen_event_formatter::output_format get_output_format() override;

/*!
\brief Fills res with the string rendering of the event.
Expand All @@ -68,6 +81,11 @@ class SINSP_PUBLIC sinsp_evt_formatter
*/
bool tostring(sinsp_evt* evt, OUT string* res);

// For compatibility with gen_event_formatter
bool tostring(gen_event* evt, std::string &output) override;

bool tostring_withformat(gen_event* evt, std::string &output, gen_event_formatter::output_format of) override;

/*!
\brief Fills res with end of capture string rendering of the event.
\param res Pointer to the string that will be filled with the result.
Expand All @@ -78,7 +96,7 @@ class SINSP_PUBLIC sinsp_evt_formatter
bool on_capture_end(OUT string* res);

private:
void set_format(const string& fmt);
gen_event_formatter::output_format m_output_format;

// vector of (full string of the token, filtercheck) pairs
// e.g. ("proc.aname[2], ptr to sinsp_filter_check_thread)
Expand Down Expand Up @@ -123,3 +141,22 @@ class SINSP_PUBLIC sinsp_evt_formatter_cache
sinsp *m_inspector;
};
/*@}*/

class sinsp_evt_formatter_factory : public gen_event_formatter_factory
{
public:
sinsp_evt_formatter_factory(sinsp *inspector);
virtual ~sinsp_evt_formatter_factory();

void set_output_format(gen_event_formatter::output_format of) override;

std::shared_ptr<gen_event_formatter> create_formatter(const std::string &format) override;

protected:

// Maps from output string to formatter
std::map<std::string, std::shared_ptr<gen_event_formatter>> m_formatters;

sinsp *m_inspector;
gen_event_formatter::output_format m_output_format;
};
15 changes: 15 additions & 0 deletions userspace/libsinsp/gen_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,18 @@ void gen_event_filter::add_check(gen_event_filter_check* chk)
{
m_curexpr->add_check((gen_event_filter_check *) chk);
}
gen_event_formatter::gen_event_formatter()
{
}

gen_event_formatter::~gen_event_formatter()
{
}

gen_event_formatter_factory::gen_event_formatter_factory()
{
}

gen_event_formatter_factory::~gen_event_formatter_factory()
{
}
47 changes: 46 additions & 1 deletion userspace/libsinsp/gen_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ along with Falco. If not, see <http://www.gnu.org/licenses/>.

#pragma once

#include <map>
#include <memory>
#include <string>
#include <vector>

/*
Expand Down Expand Up @@ -155,7 +158,7 @@ class gen_event_filter_expression : public gen_event_filter_check
//
// An expression is consistent if all its checks are of the same type (or/and).
//
// This method returns the expression operator (BO_AND/BO_OR/BO_NONE) if the
// This method returns the expression operator (BO_AND/BO_OR/BO_NONE) if the
// expression is consistent. It returns -1 if the expression is not consistent.
//
int32_t get_expr_boolop();
Expand Down Expand Up @@ -206,3 +209,45 @@ class gen_event_filter_factory
// Create a new filtercheck
virtual gen_event_filter_check *new_filtercheck(const char *fldname) = 0;
};

class gen_event_formatter
{
public:
enum output_format {
OF_NORMAL = 0,
OF_JSON = 1
};

gen_event_formatter();
virtual ~gen_event_formatter();

virtual void set_format(output_format of, const std::string &format) = 0;

// Format the output string with the configured format
virtual bool tostring(gen_event *evt, std::string &output) = 0;

// In some cases, it may be useful to format an output string
// with a custom format.
virtual bool tostring_withformat(gen_event *evt, std::string &output, output_format of) = 0;

// The map should map from field name, without the '%'
// (e.g. "proc.name"), to field value (e.g. "nginx")
virtual bool get_field_values(gen_event *evt, std::map<std::string, std::string> &fields) = 0;

virtual output_format get_output_format() = 0;
};


class gen_event_formatter_factory
{
public:
gen_event_formatter_factory();
virtual ~gen_event_formatter_factory();

// This should be called before any calls to
// create_formatter(), and changes the output format of new
// formatters.
virtual void set_output_format(gen_event_formatter::output_format of) = 0;

virtual std::shared_ptr<gen_event_formatter> create_formatter(const std::string &format) = 0;
};

0 comments on commit d7b7fb3

Please sign in to comment.