-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4717 from pocoproject/feat/json-logging
Logging: JSONFormatter
- Loading branch information
Showing
16 changed files
with
462 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// | ||
// JSONFormatter.h | ||
// | ||
// Library: Foundation | ||
// Package: Logging | ||
// Module: JSONFormatter | ||
// | ||
// Definition of the JSONFormatter class. | ||
// | ||
// Copyright (c) 2024, Applied Informatics Software Engineering GmbH. | ||
// and Contributors. | ||
// | ||
// SPDX-License-Identifier: BSL-1.0 | ||
// | ||
|
||
|
||
#ifndef Foundation_JSONFormatter_INCLUDED | ||
#define Foundation_JSONFormatter_INCLUDED | ||
|
||
|
||
#include "Poco/Foundation.h" | ||
#include "Poco/Formatter.h" | ||
#include "Poco/Message.h" | ||
#include <vector> | ||
|
||
|
||
namespace Poco { | ||
|
||
|
||
class Foundation_API JSONFormatter: public Formatter | ||
/// This formatter formats log messages as compact | ||
/// (no unnecessary whitespace) single-line JSON strings. | ||
/// | ||
/// The following JSON schema is used: | ||
/// { | ||
/// "timestamp": "2024-09-26T13:41:23.324461Z", | ||
/// "source": "sample", | ||
/// "level": "information", | ||
/// "message": "This is a test message.", | ||
/// "thread": 12, | ||
/// "file": "source.cpp", | ||
/// "line": 456, | ||
/// "params": { | ||
/// "prop1": "value1" | ||
/// } | ||
/// } | ||
/// | ||
/// The "file" and "line" properties will only be included if the log | ||
/// message contains a file name and line number. | ||
/// | ||
/// The "params" object will only be included if custom parameters | ||
/// have been added to the Message. | ||
{ | ||
public: | ||
using Ptr = AutoPtr<JSONFormatter>; | ||
|
||
JSONFormatter() = default; | ||
/// Creates a JSONFormatter. | ||
|
||
~JSONFormatter() = default; | ||
/// Destroys the JSONFormatter. | ||
|
||
void format(const Message& msg, std::string& text); | ||
/// Formats the message as a JSON string. | ||
|
||
void setProperty(const std::string& name, const std::string& value); | ||
/// Sets the property with the given name to the given value. | ||
/// | ||
/// The following properties are supported: | ||
/// | ||
/// * times: Specifies whether times are adjusted for local time | ||
/// or taken as they are in UTC. Supported values are "local" and "UTC". | ||
/// * thread: Specifies the value given for the thread. Can be | ||
/// "none" (excluded), "name" (thread name), "id" (POCO thread ID) or "osid" | ||
/// (operating system thread ID). | ||
/// | ||
/// If any other property name is given, a PropertyNotSupported | ||
/// exception is thrown. | ||
|
||
std::string getProperty(const std::string& name) const; | ||
/// Returns the value of the property with the given name or | ||
/// throws a PropertyNotSupported exception if the given | ||
/// name is not recognized. | ||
|
||
static const std::string PROP_TIMES; | ||
static const std::string PROP_THREAD; | ||
|
||
protected: | ||
std::string getThread(const Message& message) const; | ||
static const std::string& getPriorityName(int prio); | ||
|
||
enum ThreadFormat | ||
{ | ||
JSONF_THREAD_NONE = 0, | ||
JSONF_THREAD_NAME = 1, | ||
JSONF_THREAD_ID = 2, | ||
JSONF_THREAD_OS_ID = 3 | ||
}; | ||
|
||
private: | ||
bool _localTime = false; | ||
ThreadFormat _threadFormat = JSONF_THREAD_ID; | ||
}; | ||
|
||
|
||
} // namespace Poco | ||
|
||
|
||
#endif // Foundation_JSONFormatter_INCLUDED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
// | ||
// JSONFormatter.cpp | ||
// | ||
// Library: Foundation | ||
// Package: Logging | ||
// Module: JSONFormatter | ||
// | ||
// Copyright (c) 2024, Applied Informatics Software Engineering GmbH. | ||
// and Contributors. | ||
// | ||
// SPDX-License-Identifier: BSL-1.0 | ||
// | ||
|
||
|
||
#include "Poco/JSONFormatter.h" | ||
#include "Poco/JSONString.h" | ||
#include "Poco/Message.h" | ||
#include "Poco/String.h" | ||
#include "Poco/JSONString.h" | ||
#include "Poco/NumberFormatter.h" | ||
#include "Poco/DateTimeFormatter.h" | ||
#include "Poco/DateTimeFormat.h" | ||
#include "Poco/Timezone.h" | ||
|
||
|
||
namespace Poco { | ||
|
||
|
||
const std::string JSONFormatter::PROP_TIMES("times"); | ||
const std::string JSONFormatter::PROP_THREAD("thread"); | ||
|
||
|
||
void JSONFormatter::format(const Message& msg, std::string& text) | ||
{ | ||
Timestamp timestamp = msg.getTime(); | ||
int tzd = DateTimeFormatter::UTC; | ||
if (_localTime) | ||
{ | ||
tzd = Timezone::utcOffset(); | ||
tzd += Timezone::dst(); | ||
timestamp += tzd*Timestamp::resolution(); | ||
} | ||
|
||
text += '{'; | ||
text += "\"timestamp\":\""; | ||
text += Poco::DateTimeFormatter::format(timestamp, Poco::DateTimeFormat::ISO8601_FRAC_FORMAT, tzd); | ||
text += "\",\"source\":"; | ||
text += toJSON(msg.getSource()); | ||
text += ",\"level\":\""; | ||
text += getPriorityName(msg.getPriority()); | ||
text += "\",\"message\":"; | ||
text += toJSON(msg.getText()); | ||
if (_threadFormat != JSONF_THREAD_NONE) | ||
{ | ||
text += ",\"thread\":"; | ||
text += getThread(msg); | ||
} | ||
if (msg.getSourceFile()) | ||
{ | ||
text += ",\"file\":"; | ||
text += toJSON(msg.getSourceFile()); | ||
} | ||
if (msg.getSourceLine()) | ||
{ | ||
text += ",\"line\":\""; | ||
text += Poco::NumberFormatter::format(msg.getSourceLine()); | ||
text += "\""; | ||
} | ||
if (!msg.getAll().empty()) | ||
{ | ||
text += ",\"params\":{"; | ||
const auto& props = msg.getAll(); | ||
bool first = true; | ||
for (const auto& p: props) | ||
{ | ||
if (!first) | ||
text += ','; | ||
else | ||
first = false; | ||
text += toJSON(p.first); | ||
text += ':'; | ||
text += toJSON(p.second); | ||
} | ||
text += '}'; | ||
} | ||
text += '}'; | ||
} | ||
|
||
|
||
void JSONFormatter::setProperty(const std::string& name, const std::string& value) | ||
{ | ||
if (name == PROP_TIMES) | ||
{ | ||
if (Poco::icompare(value, "local"s) == 0) | ||
_localTime = true; | ||
else if (Poco::icompare(value, "utc"s) == 0) | ||
_localTime = false; | ||
else | ||
throw Poco::InvalidArgumentException("Invalid times value (must be local or UTC)"s, value); | ||
} | ||
else if (name == PROP_THREAD) | ||
{ | ||
if (Poco::icompare(value, "none"s) == 0) | ||
_threadFormat = JSONF_THREAD_NONE; | ||
else if (Poco::icompare(value, "name"s) == 0) | ||
_threadFormat = JSONF_THREAD_NAME; | ||
else if (Poco::icompare(value, "id"s) == 0) | ||
_threadFormat = JSONF_THREAD_ID; | ||
else if (Poco::icompare(value, "osid"s) == 0) | ||
_threadFormat = JSONF_THREAD_OS_ID; | ||
else | ||
throw Poco::InvalidArgumentException("Invalid thread value (must be name, id or osID)"s, value); | ||
} | ||
else throw Poco::PropertyNotSupportedException(name); | ||
} | ||
|
||
|
||
std::string JSONFormatter::getProperty(const std::string& name) const | ||
{ | ||
if (name == PROP_TIMES) | ||
{ | ||
return _localTime ? "local"s : "UTC"s; | ||
} | ||
else if (name == PROP_THREAD) | ||
{ | ||
switch (_threadFormat) | ||
{ | ||
case JSONF_THREAD_NONE: | ||
return "none"s; | ||
case JSONF_THREAD_NAME: | ||
return "name"s; | ||
case JSONF_THREAD_ID: | ||
return "id"s; | ||
case JSONF_THREAD_OS_ID: | ||
return "osID"s; | ||
default: | ||
return "invalid"s; | ||
} | ||
} | ||
else throw Poco::PropertyNotSupportedException(name); | ||
} | ||
|
||
|
||
std::string JSONFormatter::getThread(const Message& message) const | ||
{ | ||
switch (_threadFormat) | ||
{ | ||
case JSONF_THREAD_NONE: | ||
return ""s; | ||
case JSONF_THREAD_NAME: | ||
return toJSON(message.getThread()); | ||
case JSONF_THREAD_ID: | ||
return Poco::NumberFormatter::format(message.getTid()); | ||
case JSONF_THREAD_OS_ID: | ||
return Poco::NumberFormatter::format(message.getOsTid()); | ||
default: | ||
return ""s; | ||
} | ||
} | ||
|
||
|
||
const std::string& JSONFormatter::getPriorityName(int prio) | ||
{ | ||
static const std::string PRIORITY_NAMES[] = { | ||
"none"s, | ||
"fatal"s, | ||
"critical"s, | ||
"error"s, | ||
"warning"s, | ||
"notice"s, | ||
"information"s, | ||
"debug"s, | ||
"trace" | ||
}; | ||
|
||
poco_assert (prio >= Message::PRIO_FATAL && prio <= Message::PRIO_TRACE); | ||
|
||
return PRIORITY_NAMES[prio]; | ||
} | ||
|
||
|
||
} // namespace Poco |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.