Files
WrenchBoradWeb/wrenchboard/src/include/gelfcpp/GelfMessage.hpp
T
2022-11-13 00:16:43 -05:00

177 lines
4.7 KiB
C++

#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_;
};
}