Merge branch 'master' of https://gitbox.apache.org/repos/asf/logging-log4cxx
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 92d8b47..2113a81 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -106,6 +106,7 @@
<action issue="LOGCXX-262" type="fix">socketappendertestcase and xmlsocketappendertestcase not run</action>
<action issue="LOGCXX-249" type="fix">Console appender crashes if layout is not set</action>
+ <action type="add">JSONLayout (https://github.com/apache/logging-log4cxx/pull/13)</action>
<action type="update">Behavior of StringHelper::startsWith and endsWith synced</action>
<action type="update">Documented C (class) and M (method) log format keywords.</action>
<action type="add">Locationinfo for Borland C++ Builder and successors improved</action>
diff --git a/src/main/cpp/Makefile.am b/src/main/cpp/Makefile.am
index 6da89cb..236a9a6 100644
--- a/src/main/cpp/Makefile.am
+++ b/src/main/cpp/Makefile.am
@@ -72,6 +72,7 @@
inputstreamreader.cpp \
integer.cpp \
integerpatternconverter.cpp \
+ jsonlayout.cpp \
layout.cpp\
level.cpp \
levelmatchfilter.cpp \
diff --git a/src/main/cpp/jsonlayout.cpp b/src/main/cpp/jsonlayout.cpp
new file mode 100644
index 0000000..a2e72c3
--- /dev/null
+++ b/src/main/cpp/jsonlayout.cpp
@@ -0,0 +1,389 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log4cxx/logstring.h>
+#include <log4cxx/jsonlayout.h>
+#include <log4cxx/spi/loggingevent.h>
+#include <log4cxx/level.h>
+#include <log4cxx/helpers/optionconverter.h>
+#include <log4cxx/helpers/iso8601dateformat.h>
+#include <log4cxx/helpers/stringhelper.h>
+#include <log4cxx/helpers/transcoder.h>
+
+#include <apr_time.h>
+#include <apr_strings.h>
+#include <string.h>
+
+using namespace log4cxx;
+using namespace log4cxx::helpers;
+using namespace log4cxx::spi;
+
+IMPLEMENT_LOG4CXX_OBJECT(JSONLayout)
+
+
+JSONLayout::JSONLayout() :
+ locationInfo(false),
+ prettyPrint(false),
+ dateFormat(),
+ ppIndentL1(LOG4CXX_STR(" ")),
+ ppIndentL2(LOG4CXX_STR(" "))
+{
+}
+
+
+void JSONLayout::setOption(const LogString& option, const LogString& value)
+{
+ if (StringHelper::equalsIgnoreCase(option,
+ LOG4CXX_STR("LOCATIONINFO"), LOG4CXX_STR("locationinfo")))
+ {
+ setLocationInfo(OptionConverter::toBoolean(value, false));
+ }
+
+ if (StringHelper::equalsIgnoreCase(option,
+ LOG4CXX_STR("PRETTYPRINT"), LOG4CXX_STR("prettyprint")))
+ {
+ setPrettyPrint(OptionConverter::toBoolean(value, false));
+ }
+}
+void JSONLayout::format(LogString& output,
+ const spi::LoggingEventPtr& event,
+ Pool& p) const
+{
+ output.append(LOG4CXX_STR("{"));
+ output.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ output.append(ppIndentL1);
+ }
+
+ appendQuotedEscapedString(output, LOG4CXX_STR("timestamp"));
+ output.append(LOG4CXX_STR(": "));
+ LogString timestamp;
+ dateFormat.format(timestamp, event->getTimeStamp(), p);
+ appendQuotedEscapedString(output, timestamp);
+ output.append(LOG4CXX_STR(","));
+ output.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ output.append(ppIndentL1);
+ }
+
+ appendQuotedEscapedString(output, LOG4CXX_STR("level"));
+ output.append(LOG4CXX_STR(": "));
+ LogString level;
+ event->getLevel()->toString(level);
+ appendQuotedEscapedString(output, level);
+ output.append(LOG4CXX_STR(","));
+ output.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ output.append(ppIndentL1);
+ }
+
+ appendQuotedEscapedString(output, LOG4CXX_STR("logger"));
+ output.append(LOG4CXX_STR(": "));
+ appendQuotedEscapedString(output, event->getLoggerName());
+ output.append(LOG4CXX_STR(","));
+ output.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ output.append(ppIndentL1);
+ }
+
+ appendQuotedEscapedString(output, LOG4CXX_STR("message"));
+ output.append(LOG4CXX_STR(": "));
+ appendQuotedEscapedString(output, event->getMessage());
+
+ appendSerializedMDC(output, event);
+ appendSerializedNDC(output, event);
+
+ if (locationInfo)
+ {
+ output.append(LOG4CXX_STR(","));
+ output.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+ appendSerializedLocationInfo(output, event, p);
+ }
+
+ output.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+ output.append(LOG4CXX_STR("}"));
+ output.append(LOG4CXX_EOL);
+}
+
+void JSONLayout::appendQuotedEscapedString(LogString& buf,
+ const LogString& input) const
+{
+ /* add leading quote */
+ buf.push_back(0x22);
+
+ logchar specialChars[] =
+ {
+ 0x08, /* \b backspace */
+ 0x09, /* \t tab */
+ 0x0a, /* \n newline */
+ 0x0c, /* \f form feed */
+ 0x0d, /* \r carriage return */
+ 0x22, /* \" double quote */
+ 0x5c /* \\ backslash */
+ };
+
+ size_t start = 0;
+ size_t found = input.find_first_of(specialChars, start);
+
+ while (found != LogString::npos)
+ {
+ if (found > start)
+ {
+ buf.append(input, start, found - start);
+ }
+
+ switch (input[found])
+ {
+ case 0x08:
+ /* \b backspace */
+ buf.push_back(0x5c);
+ buf.push_back('b');
+ break;
+
+ case 0x09:
+ /* \t tab */
+ buf.push_back(0x5c);
+ buf.push_back('t');
+ break;
+
+ case 0x0a:
+ /* \n newline */
+ buf.push_back(0x5c);
+ buf.push_back('n');
+ break;
+
+ case 0x0c:
+ /* \f form feed */
+ buf.push_back(0x5c);
+ buf.push_back('f');
+ break;
+
+ case 0x0d:
+ /* \r carriage return */
+ buf.push_back(0x5c);
+ buf.push_back('r');
+ break;
+
+ case 0x22:
+ /* \" double quote */
+ buf.push_back(0x5c);
+ buf.push_back(0x22);
+ break;
+
+ case 0x5c:
+ /* \\ backslash */
+ buf.push_back(0x5c);
+ buf.push_back(0x5c);
+ break;
+
+ default:
+ buf.push_back(input[found]);
+ break;
+ }
+
+ start = found + 1;
+
+ if (found < input.size())
+ {
+ found = input.find_first_of(specialChars, start);
+ }
+ else
+ {
+ found = LogString::npos;
+ }
+ }
+
+ if (start < input.size())
+ {
+ buf.append(input, start, input.size() - start);
+ }
+
+ /* add trailing quote */
+ buf.push_back(0x22);
+}
+
+void JSONLayout::appendSerializedMDC(LogString& buf,
+ const LoggingEventPtr& event) const
+{
+ LoggingEvent::KeySet keys = event->getMDCKeySet();
+
+ if (keys.empty())
+ {
+ return;
+ }
+
+ buf.append(LOG4CXX_STR(","));
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL1);
+ }
+
+ appendQuotedEscapedString(buf, LOG4CXX_STR("context_map"));
+ buf.append(LOG4CXX_STR(": {"));
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ for (LoggingEvent::KeySet::iterator it = keys.begin();
+ it != keys.end(); ++it)
+ {
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL2);
+ }
+
+ appendQuotedEscapedString(buf, *it);
+ buf.append(LOG4CXX_STR(": "));
+ LogString value;
+ event->getMDC(*it, value);
+ appendQuotedEscapedString(buf, value);
+
+ /* if this isn't the last k:v pair, we need a comma */
+ if (it + 1 != keys.end())
+ {
+ buf.append(LOG4CXX_STR(","));
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+ }
+ else
+ {
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+ }
+ }
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL1);
+ }
+
+ buf.append(LOG4CXX_STR("}"));
+}
+
+void JSONLayout::appendSerializedNDC(LogString& buf,
+ const LoggingEventPtr& event) const
+{
+ LogString ndcVal;
+
+ if (!event->getNDC(ndcVal))
+ {
+ return;
+ }
+
+ buf.append(LOG4CXX_STR(","));
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL1);
+ }
+
+ appendQuotedEscapedString(buf, LOG4CXX_STR("context_stack"));
+ buf.append(LOG4CXX_STR(": ["));
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL2);
+ }
+
+ appendQuotedEscapedString(buf, ndcVal);
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL1);
+ }
+
+ buf.append(LOG4CXX_STR("]"));
+}
+
+void JSONLayout::appendSerializedLocationInfo(LogString& buf,
+ const LoggingEventPtr& event, Pool& p) const
+{
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL1);
+ }
+
+ appendQuotedEscapedString(buf, LOG4CXX_STR("location_info"));
+ buf.append(LOG4CXX_STR(": {"));
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+ const LocationInfo& locInfo = event->getLocationInformation();
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL2);
+ }
+
+ appendQuotedEscapedString(buf, LOG4CXX_STR("file"));
+ buf.append(LOG4CXX_STR(": "));
+ LOG4CXX_DECODE_CHAR(fileName, locInfo.getFileName());
+ appendQuotedEscapedString(buf, fileName);
+ buf.append(LOG4CXX_STR(","));
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL2);
+ }
+
+ appendQuotedEscapedString(buf, LOG4CXX_STR("line"));
+ buf.append(LOG4CXX_STR(": "));
+ LogString lineNumber;
+ StringHelper::toString(locInfo.getLineNumber(), p, lineNumber);
+ appendQuotedEscapedString(buf, lineNumber);
+ buf.append(LOG4CXX_STR(","));
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL2);
+ }
+
+ appendQuotedEscapedString(buf, LOG4CXX_STR("class"));
+ buf.append(LOG4CXX_STR(": "));
+ LOG4CXX_DECODE_CHAR(className, locInfo.getClassName());
+ appendQuotedEscapedString(buf, className);
+ buf.append(LOG4CXX_STR(","));
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL2);
+ }
+
+ appendQuotedEscapedString(buf, LOG4CXX_STR("method"));
+ buf.append(LOG4CXX_STR(": "));
+ LOG4CXX_DECODE_CHAR(methodName, locInfo.getMethodName());
+ appendQuotedEscapedString(buf, methodName);
+ buf.append(prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
+
+ if (prettyPrint)
+ {
+ buf.append(ppIndentL1);
+ }
+
+ buf.append(LOG4CXX_STR("}"));
+}
+
diff --git a/src/main/include/log4cxx/Makefile.am b/src/main/include/log4cxx/Makefile.am
index 75ef01c..f629b45 100644
--- a/src/main/include/log4cxx/Makefile.am
+++ b/src/main/include/log4cxx/Makefile.am
@@ -35,6 +35,7 @@
$(top_srcdir)/src/main/include/log4cxx/file.h \
$(top_srcdir)/src/main/include/log4cxx/hierarchy.h \
$(top_srcdir)/src/main/include/log4cxx/htmllayout.h \
+ $(top_srcdir)/src/main/include/log4cxx/jsonlayout.h \
$(top_srcdir)/src/main/include/log4cxx/layout.h \
$(top_srcdir)/src/main/include/log4cxx/level.h \
$(top_srcdir)/src/main/include/log4cxx/logger.h \
diff --git a/src/main/include/log4cxx/jsonlayout.h b/src/main/include/log4cxx/jsonlayout.h
new file mode 100644
index 0000000..dd997d1
--- /dev/null
+++ b/src/main/include/log4cxx/jsonlayout.h
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOG4CXX_JSON_LAYOUT_H
+#define _LOG4CXX_JSON_LAYOUT_H
+
+#include <log4cxx/layout.h>
+#include <log4cxx/helpers/iso8601dateformat.h>
+#include <log4cxx/spi/loggingevent.h>
+
+
+
+namespace log4cxx
+{
+/**
+This layout outputs events in a JSON dictionary.
+*/
+class LOG4CXX_EXPORT JSONLayout : public Layout
+{
+ private:
+ // Print no location info by default
+ bool locationInfo; //= false
+ bool prettyPrint; //= false
+
+ helpers::ISO8601DateFormat dateFormat;
+
+ protected:
+
+ LogString ppIndentL1;
+ LogString ppIndentL2;
+
+ void appendQuotedEscapedString(LogString& buf, const LogString& input) const;
+ void appendSerializedMDC(LogString& buf,
+ const spi::LoggingEventPtr& event) const;
+ void appendSerializedNDC(LogString& buf,
+ const spi::LoggingEventPtr& event) const;
+ void appendSerializedLocationInfo(LogString& buf,
+ const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& p) const;
+
+ public:
+ DECLARE_LOG4CXX_OBJECT(JSONLayout)
+ BEGIN_LOG4CXX_CAST_MAP()
+ LOG4CXX_CAST_ENTRY(JSONLayout)
+ LOG4CXX_CAST_ENTRY_CHAIN(Layout)
+ END_LOG4CXX_CAST_MAP()
+
+ JSONLayout();
+
+ /**
+ The <b>LocationInfo</b> option takes a boolean value. By
+ default, it is set to false which means there will be no location
+ information output by this layout. If the the option is set to
+ true, then the file name and line number of the statement
+ at the origin of the log statement will be output.
+ */
+ inline void setLocationInfo(bool locationInfoFlag)
+ {
+ this->locationInfo = locationInfoFlag;
+ }
+
+
+ /**
+ Returns the current value of the <b>LocationInfo</b> option.
+ */
+ inline bool getLocationInfo() const
+ {
+ return locationInfo;
+ }
+
+ /**
+ The <b>PrettyPrint</b> option takes a boolean value. By
+ default, it is set to false which means output by this layout will
+ be one line per log event. If the option is set to true, then
+ then each log event will produce multiple lines, each indented
+ for readability.
+ */
+ inline void setPrettyPrint(bool prettyPrintFlag)
+ {
+ this->prettyPrint = prettyPrintFlag;
+ }
+
+ /**
+ Returns the current value of the <b>PrettyPrint</b> option.
+ */
+ inline bool getPrettyPrint() const
+ {
+ return prettyPrint;
+ }
+
+
+ /**
+ Returns the content type output by this layout, i.e "application/json".
+ */
+ virtual LogString getContentType() const
+ {
+ return LOG4CXX_STR("application/json");
+ }
+
+ /**
+ No options to activate.
+ */
+ virtual void activateOptions(log4cxx::helpers::Pool& /* p */) {}
+
+ /**
+ Set options
+ */
+ virtual void setOption(const LogString& option, const LogString& value);
+
+ virtual void format(LogString& output,
+ const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& pool) const;
+
+ /**
+ The JSON layout handles the throwable contained in logging
+ events. Hence, this method return <code>false</code>. */
+ virtual bool ignoresThrowable() const
+ {
+ return false;
+ }
+
+}; // class JSONLayout
+LOG4CXX_PTR_DEF(JSONLayout);
+} // namespace log4cxx
+
+
+#endif // _LOG4CXX_JSON_LAYOUT_H
diff --git a/src/test/cpp/Makefile.am b/src/test/cpp/Makefile.am
index 13b82ac..8b6c95d 100644
--- a/src/test/cpp/Makefile.am
+++ b/src/test/cpp/Makefile.am
@@ -168,6 +168,7 @@
filetestcase.cpp \
hierarchytest.cpp \
hierarchythresholdtestcase.cpp \
+ jsonlayouttest.cpp \
l7dtestcase.cpp \
leveltestcase.cpp \
logunit.cpp \
diff --git a/src/test/cpp/jsonlayouttest.cpp b/src/test/cpp/jsonlayouttest.cpp
new file mode 100644
index 0000000..de1d074
--- /dev/null
+++ b/src/test/cpp/jsonlayouttest.cpp
@@ -0,0 +1,481 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logunit.h"
+#include <log4cxx/logger.h>
+#include <log4cxx/jsonlayout.h>
+#include <log4cxx/ndc.h>
+#include <log4cxx/mdc.h>
+
+#include <iostream>
+#include <log4cxx/helpers/stringhelper.h>
+
+
+using namespace log4cxx;
+using namespace log4cxx::helpers;
+using namespace log4cxx::spi;
+
+#if defined(__LOG4CXX_FUNC__)
+ #undef __LOG4CXX_FUNC__
+ #define __LOG4CXX_FUNC__ "X::X()"
+#else
+ #error __LOG4CXX_FUNC__ expected to be defined
+#endif
+/**
+ * Test for JSONLayout.
+ *
+ */
+LOGUNIT_CLASS(JSONLayoutTest), public JSONLayout
+{
+ LOGUNIT_TEST_SUITE(JSONLayoutTest);
+ LOGUNIT_TEST(testGetContentType);
+ LOGUNIT_TEST(testIgnoresThrowable);
+ LOGUNIT_TEST(testAppendQuotedEscapedStringWithPrintableChars);
+ LOGUNIT_TEST(testAppendQuotedEscapedStringWithControlChars);
+ LOGUNIT_TEST(testAppendSerializedMDC);
+ LOGUNIT_TEST(testAppendSerializedMDCWithPrettyPrint);
+ LOGUNIT_TEST(testAppendSerializedNDC);
+ LOGUNIT_TEST(testAppendSerializedNDCWithPrettyPrint);
+ LOGUNIT_TEST(testAppendSerializedLocationInfo);
+ LOGUNIT_TEST(testAppendSerializedLocationInfoWithPrettyPrint);
+ LOGUNIT_TEST(testFormat);
+ LOGUNIT_TEST(testFormatWithPrettyPrint);
+ LOGUNIT_TEST(testGetSetLocationInfo);
+ LOGUNIT_TEST_SUITE_END();
+
+
+public:
+ /**
+ * Clear MDC and NDC before test.
+ */
+ void setUp()
+ {
+ NDC::clear();
+ MDC::clear();
+ }
+
+ /**
+ * Clear MDC and NDC after test.
+ */
+ void tearDown()
+ {
+ setUp();
+ }
+
+
+public:
+ /**
+ * Tests getContentType.
+ */
+ void testGetContentType()
+ {
+ LogString expected(LOG4CXX_STR("application/json"));
+ LogString actual(JSONLayout().getContentType());
+ LOGUNIT_ASSERT(expected == actual);
+ }
+
+ /**
+ * Tests ignoresThrowable.
+ */
+ void testIgnoresThrowable()
+ {
+ LOGUNIT_ASSERT_EQUAL(false, JSONLayout().ignoresThrowable());
+ }
+
+ /**
+ * Tests appendQuotedEscapedString with printable characters.
+ */
+ void testAppendQuotedEscapedStringWithPrintableChars()
+ {
+ LogString s1(LOG4CXX_STR("foo")); /* foo */
+ LogString s2;
+ appendQuotedEscapedString(s2, s1); /* "foo" */
+ LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("\"foo\""), s2);
+
+ LogString s3;
+ appendQuotedEscapedString(s3, s2); /* "\"foo\"" */
+ LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("\"\\\"foo\\\"\""), s3);
+
+ LogString t1(LOG4CXX_STR("bar\"baz")); /* bar"baz */
+ LogString t2;
+ appendQuotedEscapedString(t2, t1); /* "bar\"baz" */
+ LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("\"bar\\\"baz\""), t2);
+
+ LogString t3;
+ appendQuotedEscapedString(t3, t2); /* "\"bar\\\"baz\"" */
+ LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("\"\\\"bar\\\\\\\"baz\\\"\""), t3);
+ }
+
+ /**
+ * Tests appendQuotedEscapedString with control characters.
+ */
+ void testAppendQuotedEscapedStringWithControlChars()
+ {
+ logchar bs[] = {0x08, 0x00};
+ logchar bs_expected[] = {0x22, 0x5c, 'b', 0x22, 0x00}; /* "\b" */
+ LogString bs_escaped;
+
+ appendQuotedEscapedString(bs_escaped, bs);
+ LOGUNIT_ASSERT_EQUAL(bs_expected, bs_escaped);
+
+ logchar tab[] = {0x09, 0x00};
+ logchar tab_expected[] = {0x22, 0x5c, 't', 0x22, 0x00}; /* "\t" */
+ LogString tab_escaped;
+
+ appendQuotedEscapedString(tab_escaped, tab);
+ LOGUNIT_ASSERT_EQUAL(tab_expected, tab_escaped);
+
+ logchar newline[] = {0x0a, 0x00};
+ logchar newline_expected[] = {0x22, 0x5c, 'n', 0x22, 0x00}; /* "\n" */
+ LogString newline_escaped;
+
+ appendQuotedEscapedString(newline_escaped, newline);
+ LOGUNIT_ASSERT_EQUAL(newline_expected, newline_escaped);
+
+ logchar ff[] = {0x0c, 0x00};
+ logchar ff_expected[] = {0x22, 0x5c, 'f', 0x22, 0x00}; /* "\f" */
+ LogString ff_escaped;
+
+ appendQuotedEscapedString(ff_escaped, ff);
+ LOGUNIT_ASSERT_EQUAL(ff_expected, ff_escaped);
+
+ logchar cr[] = {0x0d, 0x00};
+ logchar cr_expected[] = {0x22, 0x5c, 'r', 0x22, 0x00}; /* "\r" */
+ LogString cr_escaped;
+
+ appendQuotedEscapedString(cr_escaped, cr);
+ LOGUNIT_ASSERT_EQUAL(cr_expected, cr_escaped);
+ }
+
+ /**
+ * Tests appendSerializedMDC.
+ */
+ void testAppendSerializedMDC()
+ {
+ LoggingEventPtr event1 = new LoggingEvent(LOG4CXX_STR("Logger"),
+ Level::getInfo(),
+ LOG4CXX_STR("A message goes here."),
+ LOG4CXX_LOCATION);
+
+ MDC::put("key1", "value1");
+ MDC::put("key2", "value2");
+
+ LogString output1;
+ LogString expected1 = LOG4CXX_STR(", \"context_map\": { "
+ "\"key1\": \"value1\", \"key2\": \"value2\" }");
+
+ appendSerializedMDC(output1, event1);
+ LOGUNIT_ASSERT_EQUAL(expected1, output1);
+ }
+
+ /**
+ * Tests appendSerializedMDC with prettyPrint set to true.
+ */
+ void testAppendSerializedMDCWithPrettyPrint()
+ {
+ LoggingEventPtr event1 = new LoggingEvent(LOG4CXX_STR("Logger"),
+ Level::getInfo(),
+ LOG4CXX_STR("A message goes here."),
+ LOG4CXX_LOCATION);
+
+ MDC::put("key1", "value1");
+ MDC::put("key2", "value2");
+
+ LogString output1;
+ LogString expected1;
+
+ expected1
+ .append(LOG4CXX_STR(","))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("\"context_map\": {"))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL2)
+ .append(LOG4CXX_STR("\"key1\": \"value1\","))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL2)
+ .append(LOG4CXX_STR("\"key2\": \"value2\""))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("}"));
+
+ setPrettyPrint(true);
+ appendSerializedMDC(output1, event1);
+
+ LOGUNIT_ASSERT_EQUAL(expected1, output1);
+ }
+
+ /**
+ * Tests appendSerializedNDC.
+ */
+ void testAppendSerializedNDC()
+ {
+ LoggingEventPtr event1 = new LoggingEvent(LOG4CXX_STR("Logger"),
+ Level::getInfo(),
+ LOG4CXX_STR("A message goes here."),
+ LOG4CXX_LOCATION);
+
+ NDC::push("one");
+ NDC::push("two");
+ NDC::push("three");
+
+ LogString output1;
+ LogString expected1 = LOG4CXX_STR(", \"context_stack\": [ \"one two three\" ]");
+
+ appendSerializedNDC(output1, event1);
+ LOGUNIT_ASSERT_EQUAL(expected1, output1);
+ }
+
+ /**
+ * Tests appendSerializedNDC with prettyPrint set to true.
+ */
+ void testAppendSerializedNDCWithPrettyPrint()
+ {
+ LoggingEventPtr event1 = new LoggingEvent(LOG4CXX_STR("Logger"),
+ Level::getInfo(),
+ LOG4CXX_STR("A message goes here."),
+ LOG4CXX_LOCATION);
+
+ NDC::push("one");
+ NDC::push("two");
+ NDC::push("three");
+
+ LogString output1;
+ LogString expected1;
+
+ expected1
+ .append(LOG4CXX_STR(","))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("\"context_stack\": ["))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL2)
+ .append(LOG4CXX_STR("\"one two three\""))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("]"));
+
+ setPrettyPrint(true);
+ appendSerializedNDC(output1, event1);
+
+ LOGUNIT_ASSERT_EQUAL(expected1, output1);
+ }
+
+ /**
+ * Tests appendSerializedLocationInfo.
+ */
+ void testAppendSerializedLocationInfo()
+ {
+ Pool p;
+
+ LoggingEventPtr event1 = new LoggingEvent(LOG4CXX_STR("Logger"),
+ Level::getInfo(),
+ LOG4CXX_STR("A message goes here."),
+ spi::LocationInfo("FooFile", "BarFunc", 42));
+
+ LogString output1;
+ LogString expected1;
+
+ expected1
+ .append(LOG4CXX_STR("\"location_info\": { "))
+ .append(LOG4CXX_STR("\"file\": \"FooFile\", "))
+ .append(LOG4CXX_STR("\"line\": \"42\", "))
+ .append(LOG4CXX_STR("\"class\": \"\", "))
+ .append(LOG4CXX_STR("\"method\": \"BarFunc\" }"));
+
+ appendSerializedLocationInfo(output1, event1, p);
+ LOGUNIT_ASSERT_EQUAL(expected1, output1);
+ }
+
+ /**
+ * Tests appendSerializedLocationInfo with prettyPrint set to true.
+ */
+ void testAppendSerializedLocationInfoWithPrettyPrint()
+ {
+ Pool p;
+
+ LoggingEventPtr event1 = new LoggingEvent(LOG4CXX_STR("Logger"),
+ Level::getInfo(),
+ LOG4CXX_STR("A message goes here."),
+ spi::LocationInfo("FooFile", "BarFunc", 42));
+
+ LogString output1;
+ LogString expected1;
+
+ expected1
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("\"location_info\": {"))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL2)
+ .append(LOG4CXX_STR("\"file\": \"FooFile\","))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL2)
+ .append(LOG4CXX_STR("\"line\": \"42\","))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL2)
+ .append(LOG4CXX_STR("\"class\": \"\","))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL2)
+ .append(LOG4CXX_STR("\"method\": \"BarFunc\""))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("}"));
+
+ setPrettyPrint(true);
+ appendSerializedLocationInfo(output1, event1, p);
+
+ LOGUNIT_ASSERT_EQUAL(expected1, output1);
+ }
+
+ /**
+ * Tests format.
+ */
+ void testFormat()
+ {
+ Pool p;
+
+ LoggingEventPtr event1 = new LoggingEvent(LOG4CXX_STR("Logger"),
+ Level::getInfo(),
+ LOG4CXX_STR("A message goes here."),
+ spi::LocationInfo("FooFile", "BarFunc", 42));
+
+ LogString timestamp;
+ helpers::ISO8601DateFormat dateFormat;
+ dateFormat.format(timestamp, event1->getTimeStamp(), p);
+
+ NDC::push("one");
+ NDC::push("two");
+ NDC::push("three");
+
+ MDC::put("key1", "value1");
+ MDC::put("key2", "value2");
+
+ LogString output1;
+ LogString expected1;
+
+ expected1
+ .append(LOG4CXX_STR("{ \"timestamp\": \""))
+ .append(timestamp)
+ .append(LOG4CXX_STR("\", "))
+ .append(LOG4CXX_STR("\"level\": \"INFO\", "))
+ .append(LOG4CXX_STR("\"logger\": \"Logger\", "))
+ .append(LOG4CXX_STR("\"message\": \"A message goes here.\""));
+
+ setLocationInfo(true);
+
+ appendSerializedMDC(expected1, event1);
+ appendSerializedNDC(expected1, event1);
+ expected1.append(LOG4CXX_STR(", "));
+ appendSerializedLocationInfo(expected1, event1, p);
+
+ expected1.append(LOG4CXX_STR(" }"));
+ expected1.append(LOG4CXX_EOL);
+ format(output1, event1, p);
+
+ LOGUNIT_ASSERT_EQUAL(expected1, output1);
+ }
+
+ /**
+ * Tests format with PrettyPrint set to true.
+ */
+ void testFormatWithPrettyPrint()
+ {
+ Pool p;
+
+ LoggingEventPtr event1 = new LoggingEvent(LOG4CXX_STR("Logger"),
+ Level::getInfo(),
+ LOG4CXX_STR("A message goes here."),
+ spi::LocationInfo("FooFile", "BarFunc", 42));
+
+ LogString timestamp;
+ helpers::ISO8601DateFormat dateFormat;
+ dateFormat.format(timestamp, event1->getTimeStamp(), p);
+
+ NDC::push("one");
+ NDC::push("two");
+ NDC::push("three");
+
+ MDC::put("key1", "value1");
+ MDC::put("key2", "value2");
+
+ LogString output1;
+ LogString expected1;
+
+ expected1
+ .append(LOG4CXX_STR("{"))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("\"timestamp\": \""))
+ .append(timestamp)
+ .append(LOG4CXX_STR("\","))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("\"level\": \"INFO\","))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("\"logger\": \"Logger\","))
+ .append(LOG4CXX_EOL)
+ .append(ppIndentL1)
+ .append(LOG4CXX_STR("\"message\": \"A message goes here.\""));
+
+ setPrettyPrint(true);
+ setLocationInfo(true);
+
+ appendSerializedMDC(expected1, event1);
+ appendSerializedNDC(expected1, event1);
+ expected1.append(LOG4CXX_STR(","));
+ expected1.append(LOG4CXX_EOL);
+ appendSerializedLocationInfo(expected1, event1, p);
+
+ expected1.append(LOG4CXX_EOL);
+ expected1.append(LOG4CXX_STR("}"));
+ expected1.append(LOG4CXX_EOL);
+ format(output1, event1, p);
+
+ LOGUNIT_ASSERT_EQUAL(expected1, output1);
+ }
+
+ /**
+ * Tests getLocationInfo and setLocationInfo.
+ */
+ void testGetSetLocationInfo()
+ {
+ JSONLayout layout;
+ LOGUNIT_ASSERT_EQUAL(false, layout.getLocationInfo());
+ layout.setLocationInfo(true);
+ LOGUNIT_ASSERT_EQUAL(true, layout.getLocationInfo());
+ layout.setLocationInfo(false);
+ LOGUNIT_ASSERT_EQUAL(false, layout.getLocationInfo());
+ }
+
+ /**
+ * Tests getPrettyPrint and setPrettyPrint.
+ */
+ void testGetSetPrettyPrint()
+ {
+ JSONLayout layout;
+ LOGUNIT_ASSERT_EQUAL(false, layout.getPrettyPrint());
+ layout.setPrettyPrint(true);
+ LOGUNIT_ASSERT_EQUAL(true, layout.getPrettyPrint());
+ layout.setPrettyPrint(false);
+ LOGUNIT_ASSERT_EQUAL(false, layout.getPrettyPrint());
+ }
+};
+
+
+LOGUNIT_TEST_SUITE_REGISTRATION(JSONLayoutTest);
+