GELF infrastructure
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
#pragma once
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace gelfcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct DocumentAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A single GELF message, with write-only access to its fields.
|
||||
*
|
||||
* \note This class only supports GELF version 1.1 and treats deprecated standard fields as normal additional fields.
|
||||
*
|
||||
* Currently the following standard fields are handled: <code>version, host, short_message, full_message, timestamp, level</code>.
|
||||
* Standard fields are included "as-is". All other fields are prefixed with "_".
|
||||
*/
|
||||
class GelfMessage
|
||||
{
|
||||
friend struct detail::DocumentAccessor;
|
||||
|
||||
struct FieldSetter
|
||||
{
|
||||
GelfMessage& owner;
|
||||
std::string field;
|
||||
|
||||
template<typename T>
|
||||
void operator=(T&& value)
|
||||
{
|
||||
owner.SetField(field, std::forward<T>(value));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
GelfMessage() :
|
||||
doc_(rapidjson::kObjectType)
|
||||
{
|
||||
SetField("version", "1.1");
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Quick accessor for <code>short_message</code> field.
|
||||
*
|
||||
* Behaves like \code SetField("short_message", message). \endcode
|
||||
*
|
||||
* \param message message
|
||||
*/
|
||||
void SetMessage(const std::string& message)
|
||||
{
|
||||
SetField("short_message", message);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Quick accessor for <code>full_message</code> field.
|
||||
*
|
||||
* Behaves like \code SetField("full_message", message). \endcode
|
||||
*
|
||||
* \param message message
|
||||
*/
|
||||
void SetFullMessage(const std::string& message)
|
||||
{
|
||||
SetField("full_message", message);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Quick accessor for <code>host</code> field.
|
||||
*
|
||||
* Behaves like \code SetField("host", message). \endcode
|
||||
*
|
||||
* \param host hostname
|
||||
*/
|
||||
void SetHost(const std::string& host)
|
||||
{
|
||||
SetField("host", host);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Quick accessor for <code>timestamp</code> field.
|
||||
*
|
||||
* Behaves like \code SetField("timestamp", message). \endcode
|
||||
*
|
||||
* \param timestamp timestamp
|
||||
*/
|
||||
void SetTimestamp(double timestamp)
|
||||
{
|
||||
SetField("timestamp", timestamp);
|
||||
}
|
||||
|
||||
#ifndef GELFCPP_DOXYGEN_RUNNING
|
||||
|
||||
template<typename T>
|
||||
auto SetField(const std::string& name, T value) -> std::enable_if_t<std::is_arithmetic<T>::value && !std::is_same<T, bool>::value>
|
||||
{
|
||||
rapidjson::Value json(std::forward<T>(value));
|
||||
SetField(name, std::move(json));
|
||||
}
|
||||
|
||||
void SetField(const std::string& name, bool value)
|
||||
{
|
||||
SetField(name, value ? 1 : 0);
|
||||
}
|
||||
|
||||
void SetField(const std::string& name, const char* value)
|
||||
{
|
||||
rapidjson::Value json(value, doc_.GetAllocator());
|
||||
SetField(name, std::move(json));
|
||||
}
|
||||
|
||||
void SetField(const std::string& name, const std::string& value)
|
||||
{
|
||||
rapidjson::Value json(value, doc_.GetAllocator());
|
||||
SetField(name, std::move(json));
|
||||
}
|
||||
|
||||
#else
|
||||
/**
|
||||
* \brief Adds a field to the message.
|
||||
*
|
||||
* \note If a field was already assigned the old value is overwritten.
|
||||
*
|
||||
* \tparam T value type, supported: <code>bool, integral, floating point, strings</code>
|
||||
* \param name field name, the "_" prefix is added automatically
|
||||
* \param value field value of any supported type
|
||||
*/
|
||||
template<typename T> void SetField(const std::string& name, T value) {}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Adds a field to the message.
|
||||
*
|
||||
* This behaves like SetField(const std::string&, T) but allows map/array like syntax.
|
||||
*
|
||||
* Example usage:
|
||||
* \code{.cpp}
|
||||
* GelfMessage message;
|
||||
* message["field_name"] = field_value;
|
||||
* \endcode
|
||||
*
|
||||
* \param field field name, the "_" prefix is added automatically
|
||||
* \return wrapper to allow assignment, see the usage example.
|
||||
*/
|
||||
FieldSetter operator[](const std::string& field)
|
||||
{
|
||||
return { *this, field };
|
||||
}
|
||||
|
||||
private:
|
||||
void SetField(const std::string& field, rapidjson::Value&& value)
|
||||
{
|
||||
static const std::unordered_set<std::string> BUILDIN_FIELDS{ "version", "host", "short_message", "full_message", "timestamp", "level" };
|
||||
|
||||
rapidjson::Value key;
|
||||
if (BUILDIN_FIELDS.find(field) != BUILDIN_FIELDS.end())
|
||||
key.SetString(field.c_str(), static_cast<rapidjson::SizeType>(field.size()), doc_.GetAllocator());
|
||||
else
|
||||
key.SetString(("_" + field).c_str(), static_cast<rapidjson::SizeType>(field.size() + 1), doc_.GetAllocator());
|
||||
|
||||
auto find = doc_.FindMember(key);
|
||||
if (find != doc_.MemberEnd())
|
||||
find->value = std::move(value);
|
||||
else
|
||||
doc_.AddMember(std::move(key), std::move(value), doc_.GetAllocator());
|
||||
}
|
||||
|
||||
private:
|
||||
rapidjson::Document doc_;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user