From bdccabe05b0ae3dacccfddcba1154395c411478c Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Wed, 22 Feb 2017 13:46:01 +0100 Subject: [PATCH 01/20] Initial implementation for storing extra Graylog fields. --- include/graylog_logger/LogUtil.hpp | 39 ++++++++++++++++++++++++++ include/graylog_logger/LoggingBase.hpp | 7 +++++ src/LoggingBase.cpp | 27 +++++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/include/graylog_logger/LogUtil.hpp b/include/graylog_logger/LogUtil.hpp index 1412885..6779ba5 100644 --- a/include/graylog_logger/LogUtil.hpp +++ b/include/graylog_logger/LogUtil.hpp @@ -26,6 +26,29 @@ enum class Severity : int { Debug = 7, }; +struct AdditionalField { + AdditionalField(double val) { + FieldType = Type::typeDbl; + dblVal = val; + }; + AdditionalField(std::string val) { + FieldType = Type::typeStr; + strVal = val; + }; + AdditionalField(std::int64_t val) { + FieldType = Type::typeInt; + intVal = val; + }; + enum class Type : char { + typeStr = 0, + typeDbl = 1, + typeInt = 2, + } FieldType; + std::string strVal; + std::int64_t intVal; + double dblVal; +}; + struct LogMessage { std::string message; system_time timestamp; @@ -34,6 +57,22 @@ struct LogMessage { std::string host; Severity severity; std::string threadId; + std::vector> additionalFields; + template + void AddField(std::string key, const valueType &val) { + int fieldLoc = -1; + for (int i = 0; i < additionalFields.size(); i++) { + if (additionalFields[i].first == key) { + fieldLoc = i; + break; + } + } + if (-1 == fieldLoc) { + additionalFields.push_back({key, val}); + } else { + additionalFields[fieldLoc] = {key, val}; + } + }; }; class BaseLogHandler { diff --git a/include/graylog_logger/LoggingBase.hpp b/include/graylog_logger/LoggingBase.hpp index 7d80de3..fc4b95e 100644 --- a/include/graylog_logger/LoggingBase.hpp +++ b/include/graylog_logger/LoggingBase.hpp @@ -18,13 +18,20 @@ class LoggingBase { LoggingBase(); virtual ~LoggingBase(); virtual void Log(Severity sev, std::string message); + virtual void Log(Severity sev, std::string message, std::vector> extraFields); virtual void AddLogHandler(const LogHandler_P handler); + template + void AddField(std::string key, const valueType &value) { + std::lock_guard guard(baseMsgMutex); + baseMsg.AddField(key, value); + }; virtual void RemoveAllHandlers(); virtual void SetMinSeverity(Severity sev); virtual std::vector GetHandlers(); protected: Severity minSeverity; std::mutex vectorMutex; + std::mutex baseMsgMutex; std::vector handlers; LogMessage baseMsg; }; diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index 6f74bf7..aa38649 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -23,12 +23,13 @@ #endif LoggingBase::LoggingBase() { - std::lock_guard guard(vectorMutex); + std::lock_guard guard1(vectorMutex); minSeverity = Severity::Notice; const int stringBufferSize = 100; char stringBuffer[stringBufferSize]; int res; res = gethostname(stringBuffer, stringBufferSize); + std::lock_guard guard2(baseMsgMutex); if (0 == res) { baseMsg.host = std::string(stringBuffer); } @@ -44,7 +45,31 @@ void LoggingBase::Log(Severity sev, std::string message) { if (int(sev) > int(minSeverity)) { return; } + baseMsgMutex.lock(); + LogMessage cMsg(baseMsg); + baseMsgMutex.unlock(); + cMsg.timestamp = std::chrono::system_clock::now(); + cMsg.message = message; + cMsg.severity = sev; + std::ostringstream ss; + ss << std::this_thread::get_id(); + cMsg.threadId = ss.str(); + std::lock_guard guard(vectorMutex); + for(auto ptr : handlers) { + ptr.get()->AddMessage(cMsg); + } +} + +void LoggingBase::Log(Severity sev, std::string message, std::vector> extraFields) { + if (int(sev) > int(minSeverity)) { + return; + } + baseMsgMutex.lock(); LogMessage cMsg(baseMsg); + baseMsgMutex.unlock(); + for (auto &fld : extraFields) { + cMsg.AddField(fld.first, fld.second); + } cMsg.timestamp = std::chrono::system_clock::now(); cMsg.message = message; cMsg.severity = sev; From 45bc9906c1a4e8a5db22ebb63d07faa7a0da25d8 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Wed, 22 Feb 2017 13:46:19 +0100 Subject: [PATCH 02/20] Added unit tests for extra fields. --- unit_tests/LogMessageTest.cpp | 83 ++++++++++++++++++++++++++++++++++ unit_tests/LoggingBaseTest.cpp | 16 +++++++ 2 files changed, 99 insertions(+) create mode 100644 unit_tests/LogMessageTest.cpp diff --git a/unit_tests/LogMessageTest.cpp b/unit_tests/LogMessageTest.cpp new file mode 100644 index 0000000..ea429b1 --- /dev/null +++ b/unit_tests/LogMessageTest.cpp @@ -0,0 +1,83 @@ +// +// LogMessageTest.cpp +// dm-graylog-logger +// +// Created by Jonas Nilsson on 2017-01-20. +// Copyright © 2017 European Spallation Source. All rights reserved. +// + +#include +#include +#include +#include "graylog_logger/LogUtil.hpp" + +class LogMessageTesting : public ::testing::Test{ +public: + static void SetUpTestCase() { + }; + + static void TearDownTestCase() { + }; + + virtual void SetUp() { + }; + + virtual void TearDown() { + }; +}; + +TEST_F(LogMessageTesting, AddDoubleExtraField) { + LogMessage testMsg; + std::string someKey = "my_key"; + double someValue = 3.43234; + testMsg.AddField(someKey, someValue); + ASSERT_EQ(testMsg.additionalFields.at(0).first, someKey); + ASSERT_EQ(testMsg.additionalFields.at(0).second.dblVal, someValue); + ASSERT_EQ(testMsg.additionalFields.at(0).second.FieldType, AdditionalField::Type::typeDbl); + ASSERT_EQ(testMsg.additionalFields.size(), 1); +} + +TEST_F(LogMessageTesting, AddStringExtraField) { + LogMessage testMsg; + std::string someKey = "my_key"; + std::string someValue = "some_random_value_string"; + testMsg.AddField(someKey, someValue); + ASSERT_EQ(testMsg.additionalFields.at(0).first, someKey); + ASSERT_EQ(testMsg.additionalFields.at(0).second.strVal, someValue); + ASSERT_EQ(testMsg.additionalFields.at(0).second.FieldType, AdditionalField::Type::typeStr); + ASSERT_EQ(testMsg.additionalFields.size(), 1); +} + +TEST_F(LogMessageTesting, AddIntExtraField) { + LogMessage testMsg; + std::string someKey = "my_key"; + std::int64_t someValue = 9124432895; + testMsg.AddField(someKey, someValue); + ASSERT_EQ(testMsg.additionalFields.at(0).first, someKey); + ASSERT_EQ(testMsg.additionalFields.at(0).second.intVal, someValue); + ASSERT_EQ(testMsg.additionalFields.at(0).second.FieldType, AdditionalField::Type::typeInt); + ASSERT_EQ(testMsg.additionalFields.size(), 1); +} + +TEST_F(LogMessageTesting, AddTwoKeys1) { + LogMessage testMsg; + std::string someKey = "my_key"; + std::int64_t someValue = 9124432895; + testMsg.AddField(someKey, someValue); + testMsg.AddField(someKey, someValue); + ASSERT_EQ(testMsg.additionalFields.size(), 1); +} + +TEST_F(LogMessageTesting, AddTwoKeys2) { + LogMessage testMsg; + std::string someKey = "my_key"; + std::int64_t someValue1 = 9124432895; + std::string someValue2 = "912"; + testMsg.AddField(someKey, someValue1); + ASSERT_EQ(testMsg.additionalFields.at(0).second.FieldType, AdditionalField::Type::typeInt); + ASSERT_EQ(testMsg.additionalFields.at(0).second.intVal, someValue1); + testMsg.AddField(someKey, someValue2); + ASSERT_EQ(testMsg.additionalFields.size(), 1); + ASSERT_EQ(testMsg.additionalFields.at(0).second.FieldType, AdditionalField::Type::typeStr); + ASSERT_EQ(testMsg.additionalFields.at(0).second.strVal, someValue2); +} diff --git a/unit_tests/LoggingBaseTest.cpp b/unit_tests/LoggingBaseTest.cpp index a4ad546..84db671 100644 --- a/unit_tests/LoggingBaseTest.cpp +++ b/unit_tests/LoggingBaseTest.cpp @@ -17,6 +17,11 @@ #include "graylog_logger/LoggingBase.hpp" #include "graylog_logger/LogUtil.hpp" +class LoggingBaseStandIn : public LoggingBase { +public: + using LoggingBase::baseMsg; +}; + TEST(LoggingBase, InitTest) { LoggingBase log; ASSERT_EQ(log.GetHandlers().size(), 0); @@ -81,6 +86,17 @@ TEST(LoggingBase, LogMessageTest) { } } +TEST(LoggingBase, SetExtraField) { + LoggingBaseStandIn log; + std::string someKey = "yet_another_key"; + double someValue = -13.543462; + log.AddField(someKey, someValue); + ASSERT_EQ(log.baseMsg.additionalFields.size(), 1); + ASSERT_EQ(log.baseMsg.additionalFields[0].first, someKey); + ASSERT_EQ(log.baseMsg.additionalFields[0].second.FieldType, AdditionalField::Type::typeDbl); + ASSERT_EQ(log.baseMsg.additionalFields[0].second.dblVal, someValue); +} + TEST(LoggingBase, MachineInfoTest) { LoggingBase log; auto standIn = std::make_shared(); From 97a58d1a4628e3117027d5f2ea87064e97d84245 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Wed, 22 Feb 2017 15:38:51 +0100 Subject: [PATCH 03/20] Added const and ref. --- include/graylog_logger/LoggingBase.hpp | 4 ++-- src/LoggingBase.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/graylog_logger/LoggingBase.hpp b/include/graylog_logger/LoggingBase.hpp index fc4b95e..4db6bc6 100644 --- a/include/graylog_logger/LoggingBase.hpp +++ b/include/graylog_logger/LoggingBase.hpp @@ -17,8 +17,8 @@ class LoggingBase { public: LoggingBase(); virtual ~LoggingBase(); - virtual void Log(Severity sev, std::string message); - virtual void Log(Severity sev, std::string message, std::vector> extraFields); + virtual void Log(const Severity sev, const std::string &message); + virtual void Log(const Severity sev, const std::string &message, const std::vector> &extraFields); virtual void AddLogHandler(const LogHandler_P handler); template void AddField(std::string key, const valueType &value) { diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index aa38649..629f0d9 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -41,7 +41,7 @@ LoggingBase::~LoggingBase() { handlers.clear(); } -void LoggingBase::Log(Severity sev, std::string message) { +void LoggingBase::Log(const Severity sev, const std::string &message) { if (int(sev) > int(minSeverity)) { return; } @@ -60,7 +60,7 @@ void LoggingBase::Log(Severity sev, std::string message) { } } -void LoggingBase::Log(Severity sev, std::string message, std::vector> extraFields) { +void LoggingBase::Log(const Severity sev, const std::string &message, const std::vector> &extraFields) { if (int(sev) > int(minSeverity)) { return; } From 94056704da3fe7e2266e65879975aad786b48025 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 07:39:50 +0100 Subject: [PATCH 04/20] Completed implementation for extra fields (hopefully). --- include/graylog_logger/Log.hpp | 5 +++++ include/graylog_logger/Logger.hpp | 1 + include/graylog_logger/LoggingBase.hpp | 1 + src/GraylogInterface.cpp | 9 +++++++++ src/Log.cpp | 21 +++++++++++++++++++++ src/LoggingBase.cpp | 21 +++++---------------- 6 files changed, 42 insertions(+), 16 deletions(-) diff --git a/include/graylog_logger/Log.hpp b/include/graylog_logger/Log.hpp index 5852cd2..31e636d 100644 --- a/include/graylog_logger/Log.hpp +++ b/include/graylog_logger/Log.hpp @@ -14,6 +14,11 @@ namespace Log { void Msg(const Severity sev, const std::string &message); void Msg(const int sev, const std::string &message); + void Msg(const Severity sev, const std::string &message, std::pair extraField); + void Msg(const int sev, const std::string &message, std::pair extraField); + void Msg(const Severity sev, const std::string &message, std::vector> extraFields); + void Msg(const int sev, const std::string &message, std::vector> extraFields); + void AddField(const std::string &key, const AdditionalField &value); void SetMinimumSeverity(const Severity sev); void AddLogHandler(const LogHandler_P handler); template diff --git a/include/graylog_logger/Logger.hpp b/include/graylog_logger/Logger.hpp index 87589a8..1b66682 100644 --- a/include/graylog_logger/Logger.hpp +++ b/include/graylog_logger/Logger.hpp @@ -20,6 +20,7 @@ class Logger : private LoggingBase { using LoggingBase::RemoveAllHandlers; using LoggingBase::GetHandlers; using LoggingBase::SetMinSeverity; + using LoggingBase::AddField; private: Logger(); ~Logger(); diff --git a/include/graylog_logger/LoggingBase.hpp b/include/graylog_logger/LoggingBase.hpp index 4db6bc6..63976ea 100644 --- a/include/graylog_logger/LoggingBase.hpp +++ b/include/graylog_logger/LoggingBase.hpp @@ -19,6 +19,7 @@ class LoggingBase { virtual ~LoggingBase(); virtual void Log(const Severity sev, const std::string &message); virtual void Log(const Severity sev, const std::string &message, const std::vector> &extraFields); + virtual void Log(const Severity sev, const std::string &message, const std::pair &extraField); virtual void AddLogHandler(const LogHandler_P handler); template void AddField(std::string key, const valueType &value) { diff --git a/src/GraylogInterface.cpp b/src/GraylogInterface.cpp index eb666e8..5112cc9 100644 --- a/src/GraylogInterface.cpp +++ b/src/GraylogInterface.cpp @@ -43,6 +43,15 @@ std::string GraylogInterface::LogMsgToJSON(const LogMessage &msg) { root["_process_id"] = msg.processId; root["_process"] = msg.processName; root["_thread_id"] = msg.threadId; + for (auto &field : msg.additionalFields) { + if (AdditionalField::Type::typeStr == field.second.FieldType) { + root["_" + field.first] = field.second.strVal; + } else if (AdditionalField::Type::typeDbl == field.second.FieldType) { + root["_" + field.first] = field.second.dblVal; + } else if (AdditionalField::Type::typeInt == field.second.FieldType) { + root["_" + field.first] = field.second.intVal; + } + } Json::FastWriter writer; return writer.write(root); } diff --git a/src/Log.cpp b/src/Log.cpp index beeae6d..4d7876d 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -19,6 +19,27 @@ namespace Log { Logger::Inst().Log(Severity(sev), message); } + void Msg(const Severity sev, const std::string &message, std::pair extraField) { + Logger::Inst().Log(sev, message, extraField); + } + + void Msg(const int sev, const std::string &message, std::pair extraField) { + Logger::Inst().Log(Severity(sev), message, extraField); + } + + void Msg(const Severity sev, const std::string &message, std::vector> extraFields) { + Logger::Inst().Log(sev, message, extraFields); + } + + void Msg(const int sev, const std::string &message, std::vector> extraFields) { + Logger::Inst().Log(Severity(sev), message, extraFields); + } + + + void AddField(const std::string &key, const AdditionalField &value) { + Logger::Inst().AddField(key, value); + } + void SetMinimumSeverity(const Severity sev) { Logger::Inst().SetMinSeverity(sev); } diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index 629f0d9..1975e4c 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -42,22 +42,11 @@ LoggingBase::~LoggingBase() { } void LoggingBase::Log(const Severity sev, const std::string &message) { - if (int(sev) > int(minSeverity)) { - return; - } - baseMsgMutex.lock(); - LogMessage cMsg(baseMsg); - baseMsgMutex.unlock(); - cMsg.timestamp = std::chrono::system_clock::now(); - cMsg.message = message; - cMsg.severity = sev; - std::ostringstream ss; - ss << std::this_thread::get_id(); - cMsg.threadId = ss.str(); - std::lock_guard guard(vectorMutex); - for(auto ptr : handlers) { - ptr.get()->AddMessage(cMsg); - } + Log(sev, message, {}); +} + +void LoggingBase::Log(const Severity sev, const std::string &message, const std::pair &extraField) { + Log(sev, message, std::vector>{extraField, }); } void LoggingBase::Log(const Severity sev, const std::string &message, const std::vector> &extraFields) { From a48c6394ab39618abc3bbad13a72700f9da65706 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 07:40:10 +0100 Subject: [PATCH 05/20] Added some extra fields unit tests. --- unit_tests/GraylogInterfaceTest.cpp | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/unit_tests/GraylogInterfaceTest.cpp b/unit_tests/GraylogInterfaceTest.cpp index 68e6a2a..f7cadca 100644 --- a/unit_tests/GraylogInterfaceTest.cpp +++ b/unit_tests/GraylogInterfaceTest.cpp @@ -15,6 +15,7 @@ #include "LogTestServer.hpp" #include #include +#include using namespace boost::property_tree; @@ -35,6 +36,7 @@ class GraylogInterfaceStandIn : public GraylogInterface { GraylogInterfaceStandIn(std::string host, int port, int queueLength) : GraylogInterface(host, port, queueLength) { }; MOCK_METHOD1(SendMessage, void(std::string)); + using GraylogInterface::LogMsgToJSON; }; class GraylogConnectionStandIn : public GraylogConnection { @@ -200,3 +202,50 @@ TEST(GraylogInterfaceCom, MessageJSONContentTest) { EXPECT_CALL(con, SendMessage(::testing::_)).WillOnce(testing::Invoke(&TestJsonString)); con.AddMessage(msg); } + +ptree ParseJSON(std::string str) { + std::stringstream ss; + ss << str; + ptree pt; + EXPECT_NO_THROW(read_json(ss, pt)); + return pt; +} + +TEST(GraylogInterfaceCom, TestAdditionalFieldString) { + GraylogInterfaceStandIn con("localhost", testPort, 100); + LogMessage testMsg = GetPopulatedLogMsg(); + std::string key = "yet_another_key"; + std::string value = "yet another value"; + testMsg.AddField(key, value); + std::string jsonStr = con.LogMsgToJSON(testMsg); + ptree pt = ParseJSON(jsonStr); + std::string tempStr; + EXPECT_NO_THROW(tempStr = pt.get("_" + key)); + EXPECT_EQ(tempStr, value); +} + +TEST(GraylogInterfaceCom, TestAdditionalFieldInt) { + GraylogInterfaceStandIn con("localhost", testPort, 100); + LogMessage testMsg = GetPopulatedLogMsg(); + std::string key = "yet_another_key"; + std::int64_t value = -12431454; + testMsg.AddField(key, value); + std::string jsonStr = con.LogMsgToJSON(testMsg); + ptree pt = ParseJSON(jsonStr); + std::int64_t tempVal; + EXPECT_NO_THROW(tempVal = pt.get("_" + key)); + EXPECT_EQ(tempVal, value); +} + +TEST(GraylogInterfaceCom, TestAdditionalFieldDouble) { + GraylogInterfaceStandIn con("localhost", testPort, 100); + LogMessage testMsg = GetPopulatedLogMsg(); + std::string key = "yet_another_key"; + double value = M_PI; + testMsg.AddField(key, value); + std::string jsonStr = con.LogMsgToJSON(testMsg); + ptree pt = ParseJSON(jsonStr); + double tempVal; + EXPECT_NO_THROW(tempVal = pt.get("_" + key)); + EXPECT_EQ(tempVal, value); +} From 1be5dca6d9a59cf2602d4eae38946d9a4214632d Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 07:40:21 +0100 Subject: [PATCH 06/20] More extra field unit tests. --- unit_tests/LoggingBaseTest.cpp | 83 ++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/unit_tests/LoggingBaseTest.cpp b/unit_tests/LoggingBaseTest.cpp index 84db671..a4b8f3f 100644 --- a/unit_tests/LoggingBaseTest.cpp +++ b/unit_tests/LoggingBaseTest.cpp @@ -97,6 +97,89 @@ TEST(LoggingBase, SetExtraField) { ASSERT_EQ(log.baseMsg.additionalFields[0].second.dblVal, someValue); } +TEST(LoggingBase, LogMsgWithoutStaticExtraField) { + LoggingBase log; + auto standIn = std::make_shared(); + log.AddLogHandler(standIn); + log.Log(Severity::Alert, "Some message"); + ASSERT_EQ(standIn->cMsg.additionalFields.size(), 0); +} + +TEST(LoggingBase, LogMsgWithStaticExtraField) { + LoggingBase log; + auto standIn = std::make_shared(); + log.AddLogHandler(standIn); + std::string someStaticExtraField = "some_key"; + std::int64_t someStaticExtraValue = -42344093; + log.AddField(someStaticExtraField, someStaticExtraValue); + log.Log(Severity::Alert, "Some message"); + ASSERT_EQ(standIn->cMsg.additionalFields.size(), 1); + ASSERT_EQ(standIn->cMsg.additionalFields[0].first, someStaticExtraField); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.FieldType, AdditionalField::Type::typeInt); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.intVal, someStaticExtraValue); +} + +TEST(LoggingBase, LogMsgWithDynamicExtraField) { + LoggingBase log; + auto standIn = std::make_shared(); + log.AddLogHandler(standIn); + std::string someStaticExtraField = "some_key"; + std::int64_t someStaticExtraValue = -42344093; + log.Log(Severity::Alert, "Some message", {someStaticExtraField, someStaticExtraValue}); + ASSERT_EQ(standIn->cMsg.additionalFields.size(), 1); + ASSERT_EQ(standIn->cMsg.additionalFields[0].first, someStaticExtraField); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.FieldType, AdditionalField::Type::typeInt); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.intVal, someStaticExtraValue); +} + +TEST(LoggingBase, LogMsgWithTwoDynamicExtraFields) { + LoggingBase log; + auto standIn = std::make_shared(); + log.AddLogHandler(standIn); + std::string f1 = "key1"; + std::string f2 = "key2"; + std::int64_t v1 = -4234324123; + std::string v2 = "value2"; + log.Log(Severity::Alert, "Some message", {{f1, v1}, {f2, v2}} ); + ASSERT_EQ(standIn->cMsg.additionalFields.size(), 2); + ASSERT_EQ(standIn->cMsg.additionalFields[0].first, f1); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.FieldType, AdditionalField::Type::typeInt); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.intVal, v1); + + ASSERT_EQ(standIn->cMsg.additionalFields[1].first, f2); + ASSERT_EQ(standIn->cMsg.additionalFields[1].second.FieldType, AdditionalField::Type::typeStr); + ASSERT_EQ(standIn->cMsg.additionalFields[1].second.strVal, v2); +} + +TEST(LoggingBase, LogMsgWithTwoDynamicOverlappingExtraFields) { + LoggingBase log; + auto standIn = std::make_shared(); + log.AddLogHandler(standIn); + std::string f1 = "key1"; + std::int64_t v1 = -4234324123; + std::string v2 = "value2"; + log.Log(Severity::Alert, "Some message", {{f1, v1}, {f1, v2}} ); + ASSERT_EQ(standIn->cMsg.additionalFields.size(), 1); + ASSERT_EQ(standIn->cMsg.additionalFields[0].first, f1); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.FieldType, AdditionalField::Type::typeStr); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.strVal, v2); +} + +TEST(LoggingBase, LogMsgWithOverlappingStatDynExtraFields) { + LoggingBase log; + auto standIn = std::make_shared(); + log.AddLogHandler(standIn); + std::string f1 = "key1"; + std::int64_t v1 = -4234324123; + std::string v2 = "value2"; + log.AddField(f1, v2); + log.Log(Severity::Alert, "Some message", {f1, v1}); + ASSERT_EQ(standIn->cMsg.additionalFields.size(), 1); + ASSERT_EQ(standIn->cMsg.additionalFields[0].first, f1); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.FieldType, AdditionalField::Type::typeInt); + ASSERT_EQ(standIn->cMsg.additionalFields[0].second.intVal, v1); +} + TEST(LoggingBase, MachineInfoTest) { LoggingBase log; auto standIn = std::make_shared(); From cf924d5a210be344d91771e6b0258cf37a87e71a Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 10:10:34 +0100 Subject: [PATCH 07/20] Added extra field feature to the console logger application. --- console_logger/ConsoleLogger.cpp | 44 +++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/console_logger/ConsoleLogger.cpp b/console_logger/ConsoleLogger.cpp index 77599aa..929a17b 100644 --- a/console_logger/ConsoleLogger.cpp +++ b/console_logger/ConsoleLogger.cpp @@ -26,12 +26,13 @@ void PrintAlternatives(); int main(int argc, char **argv) { std::string fileName("messages.log"); std::string address1("localhost"); - std::string address2("192.168.12.11"); std::string msg; int sevLevel = 7; unsigned short port = 12201; float timeout = 1.0; int c; + std::string extraKey; + AdditionalField extraField; static struct option long_options[] { {"help", no_argument, 0, 'h'}, {"file", optional_argument, 0, 'f'}, @@ -40,10 +41,11 @@ int main(int argc, char **argv) { {"time", required_argument, 0, 't'}, {"level", required_argument, 0, 'l'}, {"message", required_argument, 0, 'm'}, + {"extra", optional_argument, 0, 'e'}, }; int option_index = 0; while (true) { - c = getopt_long(argc, argv, "hf::p:t:l:m:a::", long_options, &option_index); + c = getopt_long(argc, argv, "hf::p:t:l:m:a::e:", long_options, &option_index); if (c == -1) { break; } @@ -111,12 +113,33 @@ int main(int argc, char **argv) { return 0; } } + break; + case 'e': + if (optarg) { + std::string value(optarg); + size_t splitLoc = value.find(":"); + if (std::string::npos == splitLoc) { + break; + } + extraKey = value.substr(0, splitLoc); + extraField = value.substr(splitLoc + 1, value.size() - 1); + if (extraKey.size() == 0 or extraField.strVal.size() == 0) { + extraKey = ""; + std::cout << "Unable to parse extra field: \"" << value << "\"" << std::endl; + } + } + break; } } if (0 == msg.size()) { PrintAlternatives(); return 0; } + std::string commandLineArguments; + for (int i = 1; i < argc; i++) { + commandLineArguments += " " + std::string(argv[i]); + } + Log::AddField("arguments", commandLineArguments); Log::SetMinimumSeverity(Severity::Debug); Log::RemoveAllHandlers(); Log::AddLogHandler(LogHandler_P(new ConsoleInterface())); @@ -128,13 +151,15 @@ int main(int argc, char **argv) { if (address1.size() > 0) { if ("localhost" == address1) { Log::AddLogHandler(LogHandler_P(new GraylogInterface(address1, port))); - Log::AddLogHandler(LogHandler_P(new GraylogInterface(address2, port))); } else { Log::AddLogHandler(LogHandler_P(new GraylogInterface(address1, port))); } } - - Log::Msg(Severity(sevLevel), msg); + if (extraKey.size() > 0) { + Log::Msg(Severity(sevLevel), msg, {extraKey, extraField}); + } else { + Log::Msg(Severity(sevLevel), msg); + } std::vector allInterfaces = Log::GetHandlers(); std::vector graylogInt; @@ -158,6 +183,9 @@ int main(int argc, char **argv) { } } } + if (continueLoop) { + std::cout << "Reached timeout when sending message to Graylog-server." << std::endl; + } } return 0; @@ -166,12 +194,14 @@ int main(int argc, char **argv) { void PrintAlternatives() { std::cout << "\nusage: console_logger [-h] [-f] [-a
] [-p]\n"; - std::cout << " [-t] [-l ] [-m]\n\n"; + std::cout << " [-t] [-l ] [-m]\n"; + std::cout << " [-e:]\n\n"; std::cout << "This application will write the log message to file and socket by default.\n"; std::cout << "To prevent the application from doing this, use the -f and -a flags but do not\n"; std::cout << "provide a file name or address. The level paramater is a value between 0 and 7\n"; std::cout << "with 0 being \"Emergency\" and 7 indicating a debug message. The default level\n"; std::cout << "is 7 (debug). The default file name is \"messages.log\". The default address\n"; - std::cout << "is \"localhost\" and the default port is 12201.\n\n"; + std::cout << "is \"localhost\" and the default port is 12201. The extra field parameter requires\n"; + std::cout << "that the key and value of the field is separated using the colon character.\n\n"; std::cout << "Example: ./console_logger -t2.0 -m\"This is a log message.\"" << std::endl; } From b1fd7bf41ab85108504b7cf675f81b3406bc5fa9 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 10:11:35 +0100 Subject: [PATCH 08/20] Some minor bug fixes. --- include/graylog_logger/LogUtil.hpp | 3 +++ src/LoggingBase.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/graylog_logger/LogUtil.hpp b/include/graylog_logger/LogUtil.hpp index 6779ba5..a6102f7 100644 --- a/include/graylog_logger/LogUtil.hpp +++ b/include/graylog_logger/LogUtil.hpp @@ -27,6 +27,9 @@ enum class Severity : int { }; struct AdditionalField { + AdditionalField() { + FieldType = Type::typeStr; + }; AdditionalField(double val) { FieldType = Type::typeDbl; dblVal = val; diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index 1975e4c..ea9a22c 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -42,7 +42,7 @@ LoggingBase::~LoggingBase() { } void LoggingBase::Log(const Severity sev, const std::string &message) { - Log(sev, message, {}); + Log(sev, message, std::pair()); } void LoggingBase::Log(const Severity sev, const std::string &message, const std::pair &extraField) { From 9cb4660c3580cfe4cd97842dcf4ad1ea1bba9f23 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 10:13:05 +0100 Subject: [PATCH 09/20] Removed default Graylog handler. --- src/Logger.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Logger.cpp b/src/Logger.cpp index fe4e10d..eed4501 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -21,9 +21,6 @@ Logger& Logger::Inst() { Logger::Logger() : LoggingBase() { LogHandler_P ptr1(new ConsoleInterface()); AddLogHandler(ptr1); - - LogHandler_P ptr3(new GraylogInterface("localhost", 12201)); - AddLogHandler(ptr3); } Logger::~Logger() { From 7a26ea6b875a08d8c124d9cbb228681609f44f37 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 17:16:52 +0100 Subject: [PATCH 10/20] Added a way to get process name from Windows, Linux and MacOSX (hopefully). Needs a unit test that works. --- src/LoggingBase.cpp | 63 +++++++++++++++++++++++++++++++++- unit_tests/CMakeLists.txt | 2 +- unit_tests/LoggingBaseTest.cpp | 2 ++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index ea9a22c..c763c3a 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -13,15 +13,75 @@ #include #include #include +#include #ifdef _WIN32 #include #include +#include #define getpid _getpid #else #include #endif +#ifdef _WIN32 +std::string get_process_name() { + std::wstring buf; + buf.resize(PATH_MAX); + do { + size_t len = GetModuleFileNameW(NULL, &buf[0], static_cast< size_t >(buf.size())); + if (len < buf.size()) { + buf.resize(len); + break; + } + buf.resize(buf.size() * 2); + } while (buf.size() < 65536); + + auto lastSlash = buf.rfind("\""); + if (std::string::npos == lastSlash) { + return buf; + } + return buf.substr(lastSlash + 1, buf.size() - 1); +} +#elif defined(__APPLE__) || defined(__APPLE_CC__) +#include +std::string get_process_name() { + std::string buf; + buf.resize(PATH_MAX); + while (true) { + uint32_t size = static_cast< uint32_t >(buf.size()); + if (_NSGetExecutablePath(&buf[0], &size) == 0) { + buf.resize(std::strlen(&buf[0])); + break; + } + buf.resize(size); + } + auto lastSlash = buf.rfind("/"); + if (std::string::npos == lastSlash) { + return buf; + } + return buf.substr(lastSlash + 1, buf.size() - 1); +} +#else +#include +std::string get_process_name() { + std::vector filePaths = {"/proc/self/exe", "/proc/curproc/file", "/proc/curproc/exe"}; + char pathBuffer[1024]; + for (auto &path : filePaths) { + int nameLen = readlink(path.c_str(), pathBuffer, sizeof(buf) - 1); + if (-1 != nameLen) { + std::string tempPath(pathBuffer); + auto lastSlash = tempPath.rfind("/"); + if (std::string::npos == lastSlash) { + return tempPath; + } + return tempPath.substr(lastSlash + 1, tempPath.size() - 1); + } + } + return std::to_string(getpid()); +} +#endif + LoggingBase::LoggingBase() { std::lock_guard guard1(vectorMutex); minSeverity = Severity::Notice; @@ -34,6 +94,7 @@ LoggingBase::LoggingBase() { baseMsg.host = std::string(stringBuffer); } baseMsg.processId = getpid(); + baseMsg.processName = get_process_name(); } LoggingBase::~LoggingBase() { @@ -42,7 +103,7 @@ LoggingBase::~LoggingBase() { } void LoggingBase::Log(const Severity sev, const std::string &message) { - Log(sev, message, std::pair()); + Log(sev, message, std::vector>()); } void LoggingBase::Log(const Severity sev, const std::string &message, const std::pair &extraField) { diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index ea0b1ff..67f359d 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -18,7 +18,7 @@ if(MSVC) set(Boost_USE_STATIC_LIBS ON) endif() -find_package(Boost COMPONENTS system thread regex filesystem REQUIRED) +find_package(Boost COMPONENTS system thread regex filesystem log REQUIRED) include_directories(${Boost_INCLUDE_DIR}) link_directories(${Boost_LIBRARY_DIRS}) diff --git a/unit_tests/LoggingBaseTest.cpp b/unit_tests/LoggingBaseTest.cpp index a4b8f3f..74fdb7e 100644 --- a/unit_tests/LoggingBaseTest.cpp +++ b/unit_tests/LoggingBaseTest.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "BaseLogHandlerStandIn.hpp" #include "graylog_logger/LoggingBase.hpp" #include "graylog_logger/LogUtil.hpp" @@ -191,6 +192,7 @@ TEST(LoggingBase, MachineInfoTest) { ss << std::this_thread::get_id(); ASSERT_EQ(msg.threadId, ss.str()) << "Incorrect thread id."; ASSERT_EQ(msg.processId, getpid()) << "Incorrect process id."; + //ASSERT_EQ(msg.processName, boost::log::aux::get_process_name()) << "Incorrect process name."; } TEST(LoggingBase, TimestampTest) { From 0fd7bbff70d9b7572797daabb0b4ad5aa7c3f322 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 22:50:28 +0100 Subject: [PATCH 11/20] Improved documentation and examples. --- EXAMPLES.md | 156 +++++++++++++++++++++++++++++++++++----------------- README.md | 22 ++++++-- 2 files changed, 121 insertions(+), 57 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index f4669ed..9df816b 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,128 +1,113 @@ -# *dm-graylog-logger* examples -Instructions on how to install the C++ library dm-graylog-logger can be found on the [repository page](https://bitbucket.org/europeanspallationsource/dm-graylog-logger). The intent of this page is to give you examples on how the library can be used and extended. Other than the examples shown here, the code should be used as a reference. +# *graylog-logger* examples +Instructions on how to install the C++ library *graylog-logger* can be found on the [repository page](https://github.com/ess-dmsc/graylog-logger). The intent of this document is to give you examples on how the library can be used and extended. There is currently no code documentation except for the examples given here. For more information on implementation and interfaces see the header and implementation files. Most of the relevant interface information can be found in the header files `graylog_logger/Log.hpp` and `graylog_logger/LogUtil.hpp`. ## Basic example -By default, the library will log messages to console and Graylog server on localhost port 12201 (if available). +By default, the library will log messages to console. ```c++ -#include -#include #include int main() { Log::Msg(Severity::Warning, "Some message."); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); return 0; } ``` -The 100 *ms* delay has been added in order for the logging system to have time to actually write something before the application exists. The compiled application will print the following to console: +The compiled application will (should) print the following to console: ``` WARNING: Some message. ``` -The following line will be written to the file `messages.log`: - -``` -2017-01-02 18:12:04 (CI0011840) WARNING: Some message. -``` - -Finally, a message will also be sent to the Graylog server listening on localhost port 12201. - -## Printing only to console -In order to set-up the library to only print to console, all other interfaces have to be removed and a new console interface has to be added: +## Write log messages to file +In order to set-up the library to write messages to file, a file writer has to be added. ```c++ #include #include #include -#include +#include int main() { - Log::RemoveAllHandlers(); - Log::AddLogHandler(new ConsoleInterface()); + Log::AddLogHandler(new FileInterface("new_log_file.log")); Log::Msg(Severity::Error, "This is an error."); std::this_thread::sleep_for(std::chrono::milliseconds(100)); return 0; } ``` -This, of course, prints the following to console: +The `sleep_for()` function is added in order to give the file writing thread time to actually write the message before the application exits. The resulting file output should be similar to the following: ``` -ERROR: This is an error. +2017-01-02 18:29:20 (CI0011840) ERROR: This is an error. ``` -## Use another file name -As the code is currently set-up to immediatly create a file logging interface on run, the file `messages.log` will be created regardless. However in order to use another file name when writing the log messages, use the following code: +## Send messages to a Graylog server +To use the library for its original purpose, a Graylog server interface has to be added. This can be done as follows: ```c++ #include #include #include -#include +#include int main() { - Log::RemoveAllHandlers(); - Log::AddLogHandler(new FileInterface("new_log_file.log")); - Log::Msg(Severity::Warning, "New file warning."); + Log::AddLogHandler(new GraylogInterface("somehost.com", 12201)); + Log::Msg(Severity::Error, "This message will be sent to a Graylog server."); std::this_thread::sleep_for(std::chrono::milliseconds(100)); return 0; } ``` -This will output the following line to the file `new_log_file.log`. - -``` -2017-01-02 18:29:20 (CI0011840) WARNING: New file warning. -``` +As the default file handler has not been removed this will send a message to console as well as to the Graylog server on "somehost.com". -## Send messages to another Graylog server -It is possible to send log messages to multiple Graylog servers (or files for that matter) at the same time: +## Stop writing to console +In order to prevent the logger from writing messages to (e.g.) console but still write to file (or Graylog server), existing log handlers must be removed using the `Log::RemoveAllHandlers()` function before adding the log handlers you do want to use. ```c++ +#include +#include +#include +#include +#include + int main() { + Log::RemoveAllHandlers(); + Log::AddLogHandler(new FileInterface("new_log_file.log")); Log::AddLogHandler(new GraylogInterface("somehost.com", 12201)); - Log::AddLogHandler(new GraylogInterface("anotherhost.com", 12202)); - Log::Msg(Severity::Error, "This message will be sent to multiple Graylog servers."); + Log::Msg(Severity::Error, "This is an error."); std::this_thread::sleep_for(std::chrono::milliseconds(100)); return 0; } ``` ## Set global severity limit -This library currently uses a single severity limit. The following code snippet illustrates how it can be used. +This library currently uses a global severity limit setting. The following code snippet illustrates how it can be used. ```c++ -#include -#include #include int main() { Log::Msg(Severity::Debug, "This debug message will not be shown."); Log::SetMinimumSeverity(Severity::Debug); Log::Msg(Severity::Debug, "This debug message will be shown."); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); return 0; } ``` -The resulting output to console is a single line: +The resulting output to console is the line: ``` Debug: This debug message will be shown. ``` -## Message string formating -It is possible to supply your own string formating function as shown below. +## Message string formatting +It is possible to supply your own string formatting function as shown below. ```c++ -#include -#include #include #include -std::string MyFormater(LogMessage &msg) { +std::string MyFormatter(LogMessage &msg) { std::time_t cTime = std::chrono::system_clock::to_time_t(msg.timestamp); char timeBuffer[50]; size_t bytes = std::strftime(timeBuffer, 50, "%T", std::localtime(&cTime)); @@ -135,18 +120,17 @@ int main() { ci->SetMessageStringCreatorFunction(MyFormater); Log::AddLogHandler(ci); Log::Msg(Severity::Warning, "A warning with a custom format."); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); return 0; } ``` -The output to console produced by this code is: +The definition of `struct LogMessage` can be found in the file `graylog_logger/LogUtil.hpp`. The output to console produced by this code is: ``` 20:10:07 (host:Hecate, sev:4): A warning with a custom format. ``` -## Message string formating +## Retrieve message handler status Pointers to used logging interfaces can be retrieved from the logging system. This is illustrated here where the Graylog interface is singled out and its connection status is examined. ```c++ @@ -156,6 +140,7 @@ Pointers to used logging interfaces can be retrieved from the logging system. Th #include int main() { + Log::AddLogHandler(new GraylogInterface("somehost.com", 12201)); std::vector interfaces = Log::GetHandlers(); auto checkFunc = [&](){for (auto h : interfaces) { auto casted = dynamic_cast(h.get()); @@ -178,4 +163,73 @@ ERROR: An error message Queued messages (true/false): 1 ``` -Although the library can print log messages to console very quickly, there is a slight delay when sending messages over the network. Thus in the second call to the GraylogInterface instance, the message is still queued up. \ No newline at end of file +Although the library can print log messages to console very quickly, there is a slight delay when sending messages over the network. Thus in the second call to the GraylogInterface instance, the message is still queued up. + +## Additional fields +The standard fields provided with every log message sent to the Graylog server are the following: + +* Timestamp +* Host name +* Process id +* Process name +* Thread id +* Log message +* Severity level + +It is possible to add more fields if so required and this can be done globally or on a message by message basis. Only three types of fields are currently supported: + +* std::int64_t +* double +* std::string + +### Additional global fields +Additional global fields are added using the `Log::AddField()` function. It can be used as follows: + +```c++ +int main() { + Log::AddField("some_key", "some_value"); + Log::AddLogHandler(new GraylogInterface("somehost.com", 12201)); + Log::Msg(Severity::Error, "This message will be sent to a Graylog servers."); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return 0; +} +``` + +The output to console will not change (unless you write a replacement string formatter) but every log message sent to the Graylog server will contain an extra field with the name `some_key` containing the value `some_value`. It is possible to change this value with consecutive calls to `Log::AddField()` using the same key but different value. + +### Additional fields on a per message basis +Adding fields on a per message basis is done when calling the `Log::Msg()`-function. An example follows. + +```c++ +int main() { + Log::AddLogHandler(new GraylogInterface("somehost.com", 12201)); + Log::Msg(Severity::Error, "A message.", {"another_key", 3.14}); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return 0; +} +``` + +It is possible to add several new fields as well. + +```c++ +int main() { + Log::AddLogHandler(new GraylogInterface("somehost.com", 12201)); + Log::Msg(Severity::Error, "A message.", {{"another_key", 3.14}, {"a_third_key", "some_other_value"}}); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return 0; +} +``` + +Finally, an extra field added to a message will change the value (including the type) of a field set using `Log::AddField()`. + +```c++ +int main() { + Log::AddField("some_key", "some_value"); + Log::AddLogHandler(new GraylogInterface("somehost.com", 12201)); + Log::Msg(Severity::Error, "Yet another message", {"some_key", 42}); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return 0; +} +``` + +This last piece of code will send a log message to the Graylog server containing only a single extra field with the key `some_key` and the integer value `42`. \ No newline at end of file diff --git a/README.md b/README.md index 690f955..ac71fbf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # graylog-logger -This C++ message logging library has been developed for use at the ESS. By default, the library will write log messages to console and to TCP port 12201 on ``localhost`` in [GELF](http://docs.graylog.org/en/2.1/pages/gelf.html) format. This port will accept log messages from the library if the [graylog-machine](https://github.com/ess-dmsc/graylog-machine) [Vagrant](https://www.vagrantup.com/) machine is up and running. +This is a simple logging library which can be used to send log messages to a Graylog server. This is done by creating messages in the [GELF](http://docs.graylog.org/en/2.1/pages/gelf.html) format and sending them to a Graylog server via TCP. For testing purposes a [Vagrant](https://www.vagrantup.com/) machine running Graylog can be used. A simple Vagrantfile for creating this set-up can be [found here](https://github.com/ess-dmsc/graylog-machine). The argument for creating yet another logging library instead of writing a plugin/sink/handler for an already existing one is that a relatively light weight solution was desired. The library has functionality for writing log messages to console and file as well and by default the library will only write log messages to console. The repository is split into three parts: @@ -11,7 +11,7 @@ A sister project to this library is the Python logging handler [GraylogHandler]( ## Requirements The logging library uses only standard libraries with one exception: [Jsoncpp](https://github.com/open-source-parsers/jsoncpp). This library is included in the project in the form of a header file and an implementation file and will thus not require any packages to be installed. -C++11 features are used extensively and ignoring possible bugs, thread safety is only guaranteed if the compiler used has a correct C++11 (or above) implementation. Although the library should compile on most *nix systems, small differences in how sockets are handled could introduce bugs. For the same reason, the library will not compile under windows. +C++11 features are used extensively and ignoring possible bugs, thread safety is only guaranteed if the compiler used has a correct C++11 (or above) implementation. Although the library should compile on most *nix systems and Windows, small differences in how sockets are handled could introduce bugs. In order to build the unit tests, the following libraries are also required: @@ -35,7 +35,7 @@ make install In order to only build the library, change the first lin to ```cd graylog-logger/graylog_logger``` and then follow the rest of the instructions. ### Building on Windows -The library has been tested on Windows using the Microsoft Visual C++ compiler. Assuming that CMake as well as Boost are correctly installed and that the appropriate environment variables are configured, the instructions for compiling everything are as follows: +The library has been tested on Windows 10 using the Microsoft Visual C++ compiler (version 14.0). Assuming that CMake as well as Boost are correctly installed and that the appropriate environment variables are configured, the instructions for compiling everything are as follows: ``` cd graylog-logger @@ -56,10 +56,10 @@ As the library only depends on standard libraries, the easiest solution is likel ### 2. Use the compiled library If the instructions under **Build and install** are followed, a compiled binary of the library and the relevant header files will be installed into some system location. Probably ```/usr/local/```. Simply link to the library in the same way as other libraries (e.g. ```-lgraylog_logger```). -### Using the command line application +### Optional: Using the command line application If the command line application is compiled, instructions on how to use it are shown if run without any arguments (e.g. ```./console_logger/console_logger```). -### Running the unit tests +### Optional: Running the unit tests If compiled, the unit tests are run by running the ```unit_tests``` application in the ```unit_tests``` directory of the ```build```directory. ### Code examples @@ -77,4 +77,14 @@ The items in the following list is in no particular order. Suggestions and/or pa * Fix IPv4/IPv6 support * Add UDP support * Determine process name -* ... \ No newline at end of file +* ... + +##Changes + +###Version 1.0 + +* Added support for additional (user defined) fields. +* The library will no longer attempt to connect to a Graylog server on localhost. +* Using code adopted from boost, the library should now be able to determine the current process name. +* Updated the documentation. +* Minor bug fixes. From d0875f336d051b680bf994afc3f5df275bcab0df Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 22:52:18 +0100 Subject: [PATCH 12/20] Bug fix. --- src/LoggingBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index c763c3a..bc4ac82 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -68,7 +68,7 @@ std::string get_process_name() { std::vector filePaths = {"/proc/self/exe", "/proc/curproc/file", "/proc/curproc/exe"}; char pathBuffer[1024]; for (auto &path : filePaths) { - int nameLen = readlink(path.c_str(), pathBuffer, sizeof(buf) - 1); + int nameLen = readlink(path.c_str(), pathBuffer, sizeof(pathBuffer) - 1); if (-1 != nameLen) { std::string tempPath(pathBuffer); auto lastSlash = tempPath.rfind("/"); From 010bf947e8a63ccc472ee0efd87773c279e1e258 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 23:15:39 +0100 Subject: [PATCH 13/20] Removed unused boost library. --- unit_tests/CMakeLists.txt | 2 +- unit_tests/LoggingBaseTest.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index 67f359d..8b35805 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -18,7 +18,7 @@ if(MSVC) set(Boost_USE_STATIC_LIBS ON) endif() -find_package(Boost COMPONENTS system thread regex filesystem log REQUIRED) +find_package(Boost COMPONENTS system thread regex filesystem REQUIRED) #log include_directories(${Boost_INCLUDE_DIR}) link_directories(${Boost_LIBRARY_DIRS}) diff --git a/unit_tests/LoggingBaseTest.cpp b/unit_tests/LoggingBaseTest.cpp index 74fdb7e..c0ba430 100644 --- a/unit_tests/LoggingBaseTest.cpp +++ b/unit_tests/LoggingBaseTest.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +//#include #include "BaseLogHandlerStandIn.hpp" #include "graylog_logger/LoggingBase.hpp" #include "graylog_logger/LogUtil.hpp" From e4fc4f00e4908028b7d9fa03dfa53c7f24850597 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 23:20:06 +0100 Subject: [PATCH 14/20] Minor windows fix. --- src/LoggingBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index bc4ac82..fc4b3d9 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -27,7 +27,7 @@ #ifdef _WIN32 std::string get_process_name() { std::wstring buf; - buf.resize(PATH_MAX); + buf.resize(260); do { size_t len = GetModuleFileNameW(NULL, &buf[0], static_cast< size_t >(buf.size())); if (len < buf.size()) { From 02f4422437302c473e0fa785031278ddd4e112b7 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 23:31:41 +0100 Subject: [PATCH 15/20] More windows fixes. --- src/LoggingBase.cpp | 11 +++++++---- unit_tests/GraylogInterfaceTest.cpp | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index fc4b3d9..999d287 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -37,11 +37,14 @@ std::string get_process_name() { buf.resize(buf.size() * 2); } while (buf.size() < 65536); - auto lastSlash = buf.rfind("\""); - if (std::string::npos == lastSlash) { - return buf; + int lastSlash = buf.rfind("\""); + if (std::string::npos != lastSlash) { + buf = buf.substr(lastSlash + 1, buf.size() - 1); } - return buf.substr(lastSlash + 1, buf.size() - 1); + using convert_typeX = std::codecvt_utf8; + std::wstring_convert converterX; + + return converterX.to_bytes(wstr); } #elif defined(__APPLE__) || defined(__APPLE_CC__) #include diff --git a/unit_tests/GraylogInterfaceTest.cpp b/unit_tests/GraylogInterfaceTest.cpp index f7cadca..bd50ae1 100644 --- a/unit_tests/GraylogInterfaceTest.cpp +++ b/unit_tests/GraylogInterfaceTest.cpp @@ -241,7 +241,7 @@ TEST(GraylogInterfaceCom, TestAdditionalFieldDouble) { GraylogInterfaceStandIn con("localhost", testPort, 100); LogMessage testMsg = GetPopulatedLogMsg(); std::string key = "yet_another_key"; - double value = M_PI; + double value = 3.1415926535897932384626433832795028841; testMsg.AddField(key, value); std::string jsonStr = con.LogMsgToJSON(testMsg); ptree pt = ParseJSON(jsonStr); From 920945ee72b8d85acfa22fed12731ff12d2dc4d3 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 23:35:16 +0100 Subject: [PATCH 16/20] Even more windows fixes. --- src/LoggingBase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index 999d287..c12e01b 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #define getpid _getpid #else #include From de2a2a8dab72ea0b7ee6df5bcd8e97f131c610da Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 23:41:16 +0100 Subject: [PATCH 17/20] Moar windows fixes. --- src/LoggingBase.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index c12e01b..c6cc9c5 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #define getpid _getpid #else #include @@ -38,7 +39,7 @@ std::string get_process_name() { buf.resize(buf.size() * 2); } while (buf.size() < 65536); - int lastSlash = buf.rfind("\""); + int lastSlash = buf.rfind(L"\""); if (std::string::npos != lastSlash) { buf = buf.substr(lastSlash + 1, buf.size() - 1); } From d2df3805c4df1c3b20aaa7d01728e433e7786b39 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Thu, 23 Feb 2017 23:42:58 +0100 Subject: [PATCH 18/20] Last windows fix? --- src/LoggingBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LoggingBase.cpp b/src/LoggingBase.cpp index c6cc9c5..f5e0a75 100644 --- a/src/LoggingBase.cpp +++ b/src/LoggingBase.cpp @@ -46,7 +46,7 @@ std::string get_process_name() { using convert_typeX = std::codecvt_utf8; std::wstring_convert converterX; - return converterX.to_bytes(wstr); + return converterX.to_bytes(buf); } #elif defined(__APPLE__) || defined(__APPLE_CC__) #include From 8c014c45d78ad094b5f41723bf6aa2e9351864f1 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Fri, 24 Feb 2017 00:33:09 +0100 Subject: [PATCH 19/20] Attempt at windows socket fix. --- include/graylog_logger/GraylogConnection.hpp | 3 +++ src/GraylogConnection.cpp | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/include/graylog_logger/GraylogConnection.hpp b/include/graylog_logger/GraylogConnection.hpp index 3e2f401..28822c2 100644 --- a/include/graylog_logger/GraylogConnection.hpp +++ b/include/graylog_logger/GraylogConnection.hpp @@ -74,4 +74,7 @@ class GraylogConnection { mutable std::mutex stateMutex; ConStatus retConState; ConcurrentQueue stateQueue; +#ifdef _WIN32 + WSADATA wsaData; +#endif }; diff --git a/src/GraylogConnection.cpp b/src/GraylogConnection.cpp index 021788a..59eb7b6 100644 --- a/src/GraylogConnection.cpp +++ b/src/GraylogConnection.cpp @@ -34,10 +34,16 @@ GraylogConnection::GraylogConnection(std::string host, int port) : closeThread(false), host(host), port(std::to_string(port)), socketFd(-1), conAddresses(NULL), connectionTries(0), firstMessage(true) { retConState = GraylogConnection::ConStatus::NONE; +#ifdef _WIN32 + WSAStartup(MAKEWORD(2, 2), &wsaData); +#endif connectionThread = std::thread(&GraylogConnection::ThreadFunction, this); } GraylogConnection::~GraylogConnection() { +#ifdef _WIN32 + WSACleanup(); +#endif EndThread(); } @@ -67,6 +73,7 @@ void GraylogConnection::MakeConnectionHints() { //hints.ai_family = AF_UNSPEC; //Accept both IPv4 and IPv6 hints.ai_family = AF_INET; //Accept only IPv4 hints.ai_socktype = SOCK_STREAM; //Use TCP + hints.ai_protocol = IPPROTO_TCP; } void GraylogConnection::GetServerAddr() { From d315a076eb562159ba895183b9d75299b496d339 Mon Sep 17 00:00:00 2001 From: Jonas Nilsson Date: Fri, 24 Feb 2017 01:12:18 +0100 Subject: [PATCH 20/20] Corrections and spelling fixes to the documentation. --- EXAMPLES.md | 2 +- README.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 9df816b..2d2daf2 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -117,7 +117,7 @@ std::string MyFormatter(LogMessage &msg) { int main() { Log::RemoveAllHandlers(); auto ci = new ConsoleInterface(); - ci->SetMessageStringCreatorFunction(MyFormater); + ci->SetMessageStringCreatorFunction(MyFormatter); Log::AddLogHandler(ci); Log::Msg(Severity::Warning, "A warning with a custom format."); return 0; diff --git a/README.md b/README.md index ac71fbf..00473a9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A sister project to this library is the Python logging handler [GraylogHandler]( ## Requirements The logging library uses only standard libraries with one exception: [Jsoncpp](https://github.com/open-source-parsers/jsoncpp). This library is included in the project in the form of a header file and an implementation file and will thus not require any packages to be installed. -C++11 features are used extensively and ignoring possible bugs, thread safety is only guaranteed if the compiler used has a correct C++11 (or above) implementation. Although the library should compile on most *nix systems and Windows, small differences in how sockets are handled could introduce bugs. +C++11 features are used extensively and ignoring possible bugs, thread safety is only guaranteed if the compiler used has a correct C++11 (or above) implementation. Although the library should compile on most *nix systems and, small differences in how sockets are handled could introduce bugs. The library also compiles on Windows though it can not yet transmit messages to a Graylog server on this platform. In order to build the unit tests, the following libraries are also required: @@ -35,7 +35,7 @@ make install In order to only build the library, change the first lin to ```cd graylog-logger/graylog_logger``` and then follow the rest of the instructions. ### Building on Windows -The library has been tested on Windows 10 using the Microsoft Visual C++ compiler (version 14.0). Assuming that CMake as well as Boost are correctly installed and that the appropriate environment variables are configured, the instructions for compiling everything are as follows: +The compilation of the library has been tested on Windows 10 using the Microsoft Visual C++ compiler (version 14.0). Assuming that CMake as well as Boost are correctly installed and that the appropriate environment variables are configured, the instructions for compiling everything are as follows: ``` cd graylog-logger @@ -86,5 +86,6 @@ The items in the following list is in no particular order. Suggestions and/or pa * Added support for additional (user defined) fields. * The library will no longer attempt to connect to a Graylog server on localhost. * Using code adopted from boost, the library should now be able to determine the current process name. +* The library now builds on Windows and passes all the unit tests. However, during integration testing it failed to correctly send log messages to a Graylog server on this platform. * Updated the documentation. * Minor bug fixes.