Skip to content
Snippets Groups Projects
Commit c1962037 authored by Joao Afonso's avatar Joao Afonso
Browse files

Fix special character encoding in json logging

parent e04a2a10
No related branches found
No related tags found
1 merge request!525Fix special character encoding in json logging
......@@ -20,6 +20,7 @@
- cta/CTA#683 - Fix problems with field consistency in json logging
- cta/CTA#688 - Fix tapeserver umask to allow directory creation in POSIX filesystems
- cta/CTA#693 - Fix tapeserver tool regressions
- cta/CTA#704 - Fix special character encoding in json logging
### Continuous Integration
- cta/CTA#615 - Going to xrdfs xattr API for EOS5 extended attribute tests (EOS >= 5.2.17)
......
......@@ -151,4 +151,49 @@ TEST_F(cta_log_JsonTest, testJsonPrinting) {
ASSERT_EQ(0, jObjB.jsonGetValueProbe<int64_t>("ParamsB_null"));
}
TEST_F(cta_log_JsonTest, testJsonStringEscape) {
using namespace cta::log;
// Prepare the logger for inspection
StringLogger logger("dummy", "cta_log_JsonTest", cta::log::DEBUG);
LogContext logContext(logger);
logger.setLogFormat("json");
{
cta::log::ScopedParamContainer paramsA(logContext);
paramsA.add("key_\"", "value_\"");
paramsA.add("key_\\", "value_\\");
paramsA.add("key_\b", "value_\b");
paramsA.add("key_\n", "value_\n");
paramsA.add("key_\f", "value_\f");
paramsA.add("key_\r", "value_\r");
paramsA.add("key_\t", "value_\t");
paramsA.add("key_\x00", "value_\x00");
paramsA.add("key_\x1f", "value_\x1f");
paramsA.add("key_\x20", "value_\x20"); //This is a whitespace character
logContext.log(INFO, "Testing escaped values");
}
std::string logLine = logger.getLog();
JSONCObjectProbe jObj;
jObj.buildFromJSON(logLine);
// Check that JSON is parsed correctly
ASSERT_NO_THROW(jObj.getJSON());
// Check expected keys and values
// Strings should be converted back to cpp with the escape characters correctly decoded
ASSERT_EQ("value_\"", jObj.jsonGetValueProbe<std::string>("key_\""));
ASSERT_EQ("value_\\", jObj.jsonGetValueProbe<std::string>("key_\\"));
ASSERT_EQ("value_\b", jObj.jsonGetValueProbe<std::string>("key_\b"));
ASSERT_EQ("value_\n", jObj.jsonGetValueProbe<std::string>("key_\n"));
ASSERT_EQ("value_\f", jObj.jsonGetValueProbe<std::string>("key_\f"));
ASSERT_EQ("value_\r", jObj.jsonGetValueProbe<std::string>("key_\r"));
ASSERT_EQ("value_\t", jObj.jsonGetValueProbe<std::string>("key_\t"));
ASSERT_EQ("value_\x00", jObj.jsonGetValueProbe<std::string>("key_\x00"));
ASSERT_EQ("value_\x1f", jObj.jsonGetValueProbe<std::string>("key_\x1f"));
ASSERT_EQ("value_ ", jObj.jsonGetValueProbe<std::string>("key_ "));
}
} // namespace unitTests
......@@ -19,6 +19,7 @@
#include <iostream>
#include <iomanip>
#include <algorithm>
namespace cta::log {
......@@ -61,14 +62,14 @@ std::string Param::getValueStr() const noexcept {
//------------------------------------------------------------------------------
std::string Param::getKeyValueJSON() const noexcept {
std::ostringstream oss;
oss << "\"" << m_name << "\":";
oss << "\"" << stringFormattingJSON(m_name) << "\":";
if (m_value.has_value()) {
std::visit([&oss](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, bool>) {
oss << (arg ? "true" : "false");
} else if constexpr (std::is_same_v<T, std::string>) {
oss << "\"" << arg << "\"";
oss << "\"" << stringFormattingJSON(arg) << "\"";
} else if constexpr (std::is_integral_v<T>) {
oss << arg;
} else if constexpr (std::is_floating_point_v<T>) {
......@@ -88,4 +89,35 @@ void Param::setValue<ParamValType>(const ParamValType& value) noexcept {
m_value = value;
}
//------------------------------------------------------------------------------
// stringFormattingJSON nested class
//------------------------------------------------------------------------------
Param::stringFormattingJSON::stringFormattingJSON(const std::string& str) : m_value(str) {}
//------------------------------------------------------------------------------
// stringFormattingJSON << operator overload
//------------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& oss, const Param::stringFormattingJSON& fp) {
std::ostringstream oss_tmp;
for (char c : fp.m_value) {
switch (c) {
case '\"': oss_tmp << R"(\")"; break;
case '\\': oss_tmp << R"(\\)"; break;
case '\b': oss_tmp << R"(\b)"; break;
case '\f': oss_tmp << R"(\f)"; break;
case '\n': oss_tmp << R"(\n)"; break;
case '\r': oss_tmp << R"(\r)"; break;
case '\t': oss_tmp << R"(\t)"; break;
default:
if ('\x00' <= c && c <= '\x1f') {
oss_tmp << R"(\u)" << std::hex << std::setw(4) << std::setfill('0') << static_cast<unsigned int>(c);
} else {
oss_tmp << c;
}
}
}
oss << oss_tmp.str();
return oss;
}
} // namespace cta::log
......@@ -149,6 +149,19 @@ protected:
*/
ParamValType m_value;
/**
* Helper class to format string values in JSON
*/
class stringFormattingJSON {
public:
explicit stringFormattingJSON(const std::string& str);
friend std::ostream& operator<<(std::ostream& oss, const stringFormattingJSON& fp);
private:
const std::string & m_value;
};
friend std::ostream& operator<<(std::ostream& oss, const Param::stringFormattingJSON& fp);
/**
* Helper class to format floating-point values
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment