Coverage Report

Created: 2026-09-01 07:26

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