Coverage Report

Created: 2026-04-12 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/jsonlayout.cpp
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include <log4cxx/logstring.h>
19
#include <log4cxx/jsonlayout.h>
20
#include <log4cxx/spi/loggingevent.h>
21
#include <log4cxx/level.h>
22
#include <log4cxx/helpers/optionconverter.h>
23
#include <log4cxx/helpers/cacheddateformat.h>
24
#include <log4cxx/helpers/simpledateformat.h>
25
#include <log4cxx/helpers/stringhelper.h>
26
#include <log4cxx/helpers/transcoder.h>
27
28
#include <string.h>
29
30
using namespace LOG4CXX_NS;
31
using namespace LOG4CXX_NS::helpers;
32
using namespace LOG4CXX_NS::spi;
33
34
IMPLEMENT_LOG4CXX_OBJECT(JSONLayout)
35
36
struct JSONLayout::JSONLayoutPrivate
37
{
38
  JSONLayoutPrivate() :
39
0
    locationInfo(false),
40
0
    prettyPrint(false),
41
0
    ppIndentL1(LOG4CXX_STR("  ")),
42
0
    ppIndentL2(LOG4CXX_STR("    ")),
43
0
    expectedPatternLength(100),
44
0
    threadInfo(false) {}
45
46
  // Print no location info by default
47
  bool locationInfo; //= false
48
  bool prettyPrint; //= false
49
50
  pattern::CachedDateFormat dateFormat
51
    { std::make_shared<helpers::SimpleDateFormat>(LOG4CXX_STR("yyyy-MM-dd HH:mm:ss,SSS"))
52
    , pattern::CachedDateFormat::getMaximumCacheValidity(LOG4CXX_STR("yyyy-MM-dd HH:mm:ss,SSS"))
53
    };
54
55
  LogString ppIndentL1;
56
  LogString ppIndentL2;
57
58
  // Expected length of a formatted event excluding the message text
59
  size_t expectedPatternLength;
60
61
  // Thread info is not included by default
62
  bool threadInfo; //= false
63
};
64
65
JSONLayout::JSONLayout() :
66
0
  m_priv(std::make_unique<JSONLayoutPrivate>())
67
0
{
68
0
}
Unexecuted instantiation: log4cxx::JSONLayout::JSONLayout()
Unexecuted instantiation: log4cxx::JSONLayout::JSONLayout()
69
70
0
JSONLayout::~JSONLayout(){}
71
72
void JSONLayout::setLocationInfo(bool locationInfoFlag)
73
0
{
74
0
  m_priv->locationInfo = locationInfoFlag;
75
0
}
76
77
bool JSONLayout::getLocationInfo() const
78
0
{
79
0
  return m_priv->locationInfo;
80
0
}
81
82
void JSONLayout::setPrettyPrint(bool prettyPrintFlag)
83
0
{
84
0
  m_priv->prettyPrint = prettyPrintFlag;
85
0
}
86
87
bool JSONLayout::getPrettyPrint() const
88
0
{
89
0
  return m_priv->prettyPrint;
90
0
}
91
92
void JSONLayout::setThreadInfo(bool newValue)
93
0
{
94
0
  m_priv->threadInfo = newValue;
95
0
}
96
97
bool JSONLayout::getThreadInfo() const
98
0
{
99
0
  return m_priv->threadInfo;
100
0
}
101
102
LogString JSONLayout::getContentType() const
103
0
{
104
0
  return LOG4CXX_STR("application/json");
105
0
}
106
107
void JSONLayout::activateOptions(helpers::Pool& /* p */)
108
0
{
109
0
  m_priv->expectedPatternLength = getFormattedEventCharacterCount() * 2;
110
0
}
111
112
void JSONLayout::setOption(const LogString& option, const LogString& value)
113
0
{
114
0
  if (StringHelper::equalsIgnoreCase(option,
115
0
      LOG4CXX_STR("LOCATIONINFO"), LOG4CXX_STR("locationinfo")))
116
0
  {
117
0
    setLocationInfo(OptionConverter::toBoolean(value, false));
118
0
  }
119
0
  else if (StringHelper::equalsIgnoreCase(option,
120
0
      LOG4CXX_STR("THREADINFO"), LOG4CXX_STR("threadinfo")))
121
0
  {
122
0
    setThreadInfo(OptionConverter::toBoolean(value, false));
123
0
  }
124
0
  else if (StringHelper::equalsIgnoreCase(option,
125
0
      LOG4CXX_STR("PRETTYPRINT"), LOG4CXX_STR("prettyprint")))
126
0
  {
127
0
    setPrettyPrint(OptionConverter::toBoolean(value, false));
128
0
  }
129
0
}
130
131
void JSONLayout::format(LogString& output,
132
  const spi::LoggingEventPtr& event,
133
  Pool& p) const
134
0
{
135
0
  auto& lsMsg = event->getRenderedMessage();
136
0
  output.reserve(m_priv->expectedPatternLength + lsMsg.size());
137
0
  output.append(LOG4CXX_STR("{"));
138
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
139
140
0
  if (m_priv->prettyPrint)
141
0
  {
142
0
    output.append(m_priv->ppIndentL1);
143
0
  }
144
145
0
  output.append(LOG4CXX_STR("\"timestamp\": \""));
146
0
  m_priv->dateFormat.format(output, event->getTimeStamp(), p);
147
0
  output.append(LOG4CXX_STR("\","));
148
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
149
150
0
  if (m_priv->threadInfo)
151
0
  {
152
0
    if (m_priv->prettyPrint)
153
0
    {
154
0
      output.append(m_priv->ppIndentL1);
155
0
    }
156
0
    appendQuotedEscapedString(output, LOG4CXX_STR("thread"));
157
0
    output.append(LOG4CXX_STR(": "));
158
0
    appendQuotedEscapedString(output, event->getThreadName());
159
0
    output.append(LOG4CXX_STR(","));
160
0
    output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
161
0
  }
162
163
0
  if (m_priv->prettyPrint)
164
0
  {
165
0
    output.append(m_priv->ppIndentL1);
166
0
  }
167
168
0
  output.append(LOG4CXX_STR("\"level\": "));
169
0
  LogString level;
170
0
  event->getLevel()->toString(level);
171
0
  appendQuotedEscapedString(output, level);
172
0
  output.append(LOG4CXX_STR(","));
173
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
174
175
0
  if (m_priv->prettyPrint)
176
0
  {
177
0
    output.append(m_priv->ppIndentL1);
178
0
  }
179
180
0
  output.append(LOG4CXX_STR("\"logger\": "));
181
0
  appendQuotedEscapedString(output, event->getLoggerName());
182
0
  output.append(LOG4CXX_STR(","));
183
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
184
185
0
  if (m_priv->prettyPrint)
186
0
  {
187
0
    output.append(m_priv->ppIndentL1);
188
0
  }
189
190
0
  output.append(LOG4CXX_STR("\"message\": "));
191
0
  appendQuotedEscapedString(output, lsMsg);
192
193
0
  appendSerializedMDC(output, event);
194
0
  appendSerializedNDC(output, event);
195
196
0
  if (m_priv->locationInfo)
197
0
  {
198
0
    output.append(LOG4CXX_STR(","));
199
0
    output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
200
0
    appendSerializedLocationInfo(output, event, p);
201
0
  }
202
203
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
204
0
  output.append(LOG4CXX_STR("}"));
205
0
  output.append(LOG4CXX_EOL);
206
0
}
207
208
void JSONLayout::appendQuotedEscapedString(LogString& buf,
209
  const LogString& input) const
210
0
{
211
0
  appendItem(input, buf);
212
0
}
213
214
void JSONLayout::appendItem(const LogString& input, LogString& buf)
215
0
{
216
0
  auto toHexDigit = [](int ch) -> int
217
0
  {
218
0
    return (10 <= ch ? (0x61 - 10) : 0x30) + ch;
219
0
  };
220
  /* add leading quote */
221
0
  buf.push_back(0x22);
222
223
0
  auto start = input.begin();
224
0
  for (auto nextCodePoint = start; input.end() != nextCodePoint; )
225
0
  {
226
0
    auto lastCodePoint = nextCodePoint;
227
0
    auto ch = Transcoder::decode(input, nextCodePoint);
228
0
    if (nextCodePoint == lastCodePoint) // failed to decode input?
229
0
    {
230
0
      nextCodePoint = input.end();
231
0
      ch = 0xFFFD; // The Unicode replacement character
232
0
    }
233
0
    else if (0x22 == ch || 0x5c == ch) // double quote or backslash?
234
0
      ;
235
0
    else if (0x20 <= ch) // not a control character?
236
0
      continue;
237
238
0
    if (start != lastCodePoint)
239
0
      buf.append(start, lastCodePoint);
240
0
    start = nextCodePoint;
241
0
    switch (ch)
242
0
    {
243
0
      case 0x08:
244
        /* \b backspace */
245
0
        buf.push_back(0x5c);
246
0
        buf.push_back('b');
247
0
        break;
248
249
0
      case 0x09:
250
        /* \t tab */
251
0
        buf.push_back(0x5c);
252
0
        buf.push_back('t');
253
0
        break;
254
255
0
      case 0x0a:
256
        /* \n newline */
257
0
        buf.push_back(0x5c);
258
0
        buf.push_back('n');
259
0
        break;
260
261
0
      case 0x0c:
262
        /* \f form feed */
263
0
        buf.push_back(0x5c);
264
0
        buf.push_back('f');
265
0
        break;
266
267
0
      case 0x0d:
268
        /* \r carriage return */
269
0
        buf.push_back(0x5c);
270
0
        buf.push_back('r');
271
0
        break;
272
273
0
      case 0x22:
274
        /* \" double quote */
275
0
        buf.push_back(0x5c);
276
0
        buf.push_back(0x22);
277
0
        break;
278
279
0
      case 0x5c:
280
        /* \\ backslash */
281
0
        buf.push_back(0x5c);
282
0
        buf.push_back(0x5c);
283
0
        break;
284
285
0
      default:
286
0
        buf.push_back(0x5c);
287
0
        buf.push_back(0x75); // 'u'
288
0
        buf.push_back(toHexDigit((ch & 0xF000) >> 12));
289
0
        buf.push_back(toHexDigit((ch & 0xF00) >> 8));
290
0
        buf.push_back(toHexDigit((ch & 0xF0) >> 4));
291
0
        buf.push_back(toHexDigit(ch & 0xF));
292
0
        break;
293
0
    }
294
0
  }
295
0
  buf.append(start, input.end());
296
297
  /* add trailing quote */
298
0
  buf.push_back(0x22);
299
0
}
300
301
void JSONLayout::appendSerializedMDC(LogString& buf,
302
  const LoggingEventPtr& event) const
303
0
{
304
0
  LoggingEvent::KeySet keys = event->getMDCKeySet();
305
306
0
  if (keys.empty())
307
0
  {
308
0
    return;
309
0
  }
310
311
0
  buf.append(LOG4CXX_STR(","));
312
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
313
314
0
  if (m_priv->prettyPrint)
315
0
  {
316
0
    buf.append(m_priv->ppIndentL1);
317
0
  }
318
319
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("context_map"));
320
0
  buf.append(LOG4CXX_STR(": {"));
321
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
322
323
0
  for (LoggingEvent::KeySet::iterator it = keys.begin();
324
0
    it != keys.end(); ++it)
325
0
  {
326
0
    if (m_priv->prettyPrint)
327
0
    {
328
0
      buf.append(m_priv->ppIndentL2);
329
0
    }
330
331
0
    appendQuotedEscapedString(buf, *it);
332
0
    buf.append(LOG4CXX_STR(": "));
333
0
    LogString value;
334
0
    event->getMDC(*it, value);
335
0
    appendQuotedEscapedString(buf, value);
336
337
    /* if this isn't the last k:v pair, we need a comma */
338
0
    if (it + 1 != keys.end())
339
0
    {
340
0
      buf.append(LOG4CXX_STR(","));
341
0
      buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
342
0
    }
343
0
    else
344
0
    {
345
0
      buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
346
0
    }
347
0
  }
348
349
0
  if (m_priv->prettyPrint)
350
0
  {
351
0
    buf.append(m_priv->ppIndentL1);
352
0
  }
353
354
0
  buf.append(LOG4CXX_STR("}"));
355
0
}
356
357
void JSONLayout::appendSerializedNDC(LogString& buf,
358
  const LoggingEventPtr& event) const
359
0
{
360
0
  LogString ndcVal;
361
362
0
  if (!event->getNDC(ndcVal))
363
0
  {
364
0
    return;
365
0
  }
366
367
0
  buf.append(LOG4CXX_STR(","));
368
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
369
370
0
  if (m_priv->prettyPrint)
371
0
  {
372
0
    buf.append(m_priv->ppIndentL1);
373
0
  }
374
375
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("context_stack"));
376
0
  buf.append(LOG4CXX_STR(": ["));
377
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
378
379
0
  if (m_priv->prettyPrint)
380
0
  {
381
0
    buf.append(m_priv->ppIndentL2);
382
0
  }
383
384
0
  appendQuotedEscapedString(buf, ndcVal);
385
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
386
387
0
  if (m_priv->prettyPrint)
388
0
  {
389
0
    buf.append(m_priv->ppIndentL1);
390
0
  }
391
392
0
  buf.append(LOG4CXX_STR("]"));
393
0
}
394
395
void JSONLayout::appendSerializedLocationInfo(LogString& buf,
396
  const LoggingEventPtr& event, Pool& p) const
397
0
{
398
0
  if (m_priv->prettyPrint)
399
0
  {
400
0
    buf.append(m_priv->ppIndentL1);
401
0
  }
402
403
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("location_info"));
404
0
  buf.append(LOG4CXX_STR(": {"));
405
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
406
0
  const LocationInfo& locInfo = event->getLocationInformation();
407
408
0
  if (m_priv->prettyPrint)
409
0
  {
410
0
    buf.append(m_priv->ppIndentL2);
411
0
  }
412
413
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("file"));
414
0
  buf.append(LOG4CXX_STR(": "));
415
0
  LOG4CXX_DECODE_CHAR(fileName, locInfo.getFileName());
416
0
  appendQuotedEscapedString(buf, fileName);
417
0
  buf.append(LOG4CXX_STR(","));
418
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
419
420
0
  if (m_priv->prettyPrint)
421
0
  {
422
0
    buf.append(m_priv->ppIndentL2);
423
0
  }
424
425
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("line"));
426
0
  buf.append(LOG4CXX_STR(": "));
427
0
  LogString lineNumber;
428
0
  StringHelper::toString(locInfo.getLineNumber(), p, lineNumber);
429
0
  appendQuotedEscapedString(buf, lineNumber);
430
0
  buf.append(LOG4CXX_STR(","));
431
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
432
433
0
  if (m_priv->prettyPrint)
434
0
  {
435
0
    buf.append(m_priv->ppIndentL2);
436
0
  }
437
438
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("class"));
439
0
  buf.append(LOG4CXX_STR(": "));
440
0
  LOG4CXX_DECODE_CHAR(className, locInfo.getClassName());
441
0
  appendQuotedEscapedString(buf, className);
442
0
  buf.append(LOG4CXX_STR(","));
443
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
444
445
0
  if (m_priv->prettyPrint)
446
0
  {
447
0
    buf.append(m_priv->ppIndentL2);
448
0
  }
449
450
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("method"));
451
0
  buf.append(LOG4CXX_STR(": "));
452
0
  LOG4CXX_DECODE_CHAR(methodName, locInfo.getMethodName());
453
0
  appendQuotedEscapedString(buf, methodName);
454
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
455
456
0
  if (m_priv->prettyPrint)
457
0
  {
458
0
    buf.append(m_priv->ppIndentL1);
459
0
  }
460
461
0
  buf.append(LOG4CXX_STR("}"));
462
0
}
463