Skip to content

Commit

Permalink
Merge pull request ecmwf#122 from sametd/feature/syslog-structured-data
Browse files Browse the repository at this point in the history
Feature/syslog structured data
  • Loading branch information
jameshawkes authored Jun 3, 2024
2 parents 3a95f91 + a726bd8 commit 5f08ff0
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 5 deletions.
24 changes: 20 additions & 4 deletions src/eckit/log/SysLog.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "eckit/runtime/Main.h"

#include "eckit/log/TimeStamp.h"
#include "eckit/net/IPAddress.h"

namespace eckit {

Expand All @@ -43,11 +44,26 @@ int SysLog::procid() const {
return ::getpid();
}


std::string SysLog::structuredData() const {
if (software_.empty() && swVersion_.empty() && enterpriseId_.empty()) {
return std::string(1, nilvalue());
}
// RFC 5424 section 6.3 (only origin)
std::ostringstream s;

/// @todo Implement the structured message meta-description as described in RFC5424 secion 6.3
s << nilvalue();
std::string ip = net::IPAddress::myIPAddress().asString();

s << "[origin ip=\"" << ip << "\"";
if (!enterpriseId_.empty()) {
s << " enterpriseId=\"" << enterpriseId_ << "\"";
}
if (!software_.empty()) {
s << " software=\"" << software_ << "\"";
if (!swVersion_.empty()) {
s << " swVersion=\"" << swVersion_ << "\"";
}
}
s << "]";

return s.str();
}
Expand All @@ -59,7 +75,7 @@ SysLog::operator std::string() const {
static char sep = ' ';

os // RFC 5424 section 6.2 (Header)
<< "<" << priotity() << ">" << version() << sep << timestamp() << sep << fqdn() << sep << appName() << sep
<< "<" << priority() << ">" << version() << sep << timestamp() << sep << fqdn() << sep << appName() << sep
<< procid() << sep << msgid()
<< sep
// RFC 5424 section 6.3
Expand Down
12 changes: 11 additions & 1 deletion src/eckit/log/SysLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class SysLog {

SysLog(const std::string& msg, int msgid = 0, Facility f = SysLog::User, Severity s = SysLog::Info);

unsigned priotity() const { return facility_ * 8 + severity_; }
unsigned priority() const { return facility_ * 8 + severity_; }

unsigned version() const { return 1; }

Expand All @@ -99,6 +99,11 @@ class SysLog {
return s;
}

/// Optional fields for structured data (RFC 5424 section 6.3)
void software(const std::string& software) { software_ = software; }
void swVersion(const std::string& version) { swVersion_ = version; }
void enterpriseId(const std::string& id) { enterpriseId_ = id; }

private: // methods
void print(std::ostream& out) const;

Expand All @@ -112,6 +117,11 @@ class SysLog {
int msgid_;

std::string msg_;

// optional fields for structured data
std::string software_;
std::string swVersion_;
std::string enterpriseId_;
};

} // namespace eckit
Expand Down
4 changes: 4 additions & 0 deletions tests/log/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ ecbuild_add_test( TARGET eckit_test_log_user_channels
ENABLED OFF
SOURCES test_log_user_channels.cc
LIBS eckit )

ecbuild_add_test( TARGET eckit_test_syslog
SOURCES test_syslog.cc
LIBS eckit )
84 changes: 84 additions & 0 deletions tests/log/test_syslog.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* (C) Copyright 1996- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/

#include <iostream>

#include "eckit/log/SysLog.h"
#include "eckit/testing/Test.h"

using namespace std;
using namespace eckit;
using namespace eckit::testing;

namespace eckit::test {

//----------------------------------------------------------------------------------------------------------------------

CASE("test_priority") {
SysLog log("Test message", 0, SysLog::Local7, SysLog::Info);
std::string logString = static_cast<std::string>(log);
std::string expectedPriority = "<" + std::to_string(log.priority()) + ">";
EXPECT(logString.find(expectedPriority) != std::string::npos);
}

//----------------------------------------------------------------------------------------------------------------------

CASE("test_timezone") {
SysLog log("Test message", 0, SysLog::Local7, SysLog::Info);
std::string logString = static_cast<std::string>(log);
// Check if 'Z' UTC indicator is present
EXPECT(logString.find("Z") != std::string::npos);
// Check if 'T' separator is present
EXPECT(logString.find("T") != std::string::npos);
}

//----------------------------------------------------------------------------------------------------------------------

CASE("test_appname") {
SysLog log("Test message", 0, SysLog::Local7, SysLog::Info);
EXPECT(log.appName() == Main::instance().name());

// Change the appName and check if it persists
log.appName("test_app");
std::string logString = static_cast<std::string>(log);
EXPECT(logString.find("test_app") != std::string::npos);

// Create a new SysLog instance and check if it retains the original appName
SysLog newLog("New message", 2, SysLog::Local7, SysLog::Info);
EXPECT(newLog.appName() == Main::instance().name());
}

//----------------------------------------------------------------------------------------------------------------------

CASE("test_without_structured_data") {
SysLog log("Test message", 0, SysLog::Local7, SysLog::Info);
std::string logString = static_cast<std::string>(log);
// Check if structured data is not present
EXPECT(logString.find("[origin") == std::string::npos);
}

//----------------------------------------------------------------------------------------------------------------------
CASE("test_with_structured_data") {
SysLog log("Test message", 0, SysLog::Local7, SysLog::Info);
log.software("log_test");
log.swVersion("1.0.0");
log.enterpriseId("7464");
std::string logString = static_cast<std::string>(log);
EXPECT(logString.find("enterpriseId=\"7464\"") != std::string::npos);
EXPECT(logString.find("software=\"log_test\"") != std::string::npos);
EXPECT(logString.find("swVersion=\"1.0.0\"") != std::string::npos);
}
//----------------------------------------------------------------------------------------------------------------------

} // namespace eckit::test

int main(int argc, char** argv) {
return run_tests(argc, argv);
}

0 comments on commit 5f08ff0

Please sign in to comment.