Coverage Report

Created: 2026-02-26 06:58

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
2.63k
    locationInfo(false),
40
2.63k
    prettyPrint(false),
41
2.63k
    ppIndentL1(LOG4CXX_STR("  ")),
42
2.63k
    ppIndentL2(LOG4CXX_STR("    ")),
43
2.63k
    expectedPatternLength(100),
44
2.63k
    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
2.63k
  m_priv(std::make_unique<JSONLayoutPrivate>())
67
2.63k
{
68
2.63k
}
Unexecuted instantiation: log4cxx::JSONLayout::JSONLayout()
log4cxx::JSONLayout::JSONLayout()
Line
Count
Source
66
2.63k
  m_priv(std::make_unique<JSONLayoutPrivate>())
67
2.63k
{
68
2.63k
}
69
70
2.63k
JSONLayout::~JSONLayout(){}
71
72
void JSONLayout::setLocationInfo(bool locationInfoFlag)
73
1.48k
{
74
1.48k
  m_priv->locationInfo = locationInfoFlag;
75
1.48k
}
76
77
bool JSONLayout::getLocationInfo() const
78
0
{
79
0
  return m_priv->locationInfo;
80
0
}
81
82
void JSONLayout::setPrettyPrint(bool prettyPrintFlag)
83
1.26k
{
84
1.26k
  m_priv->prettyPrint = prettyPrintFlag;
85
1.26k
}
86
87
bool JSONLayout::getPrettyPrint() const
88
0
{
89
0
  return m_priv->prettyPrint;
90
0
}
91
92
void JSONLayout::setThreadInfo(bool newValue)
93
1.46k
{
94
1.46k
  m_priv->threadInfo = newValue;
95
1.46k
}
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
4.20k
{
114
4.20k
  if (StringHelper::equalsIgnoreCase(option,
115
4.20k
      LOG4CXX_STR("LOCATIONINFO"), LOG4CXX_STR("locationinfo")))
116
1.48k
  {
117
1.48k
    setLocationInfo(OptionConverter::toBoolean(value, false));
118
1.48k
  }
119
2.72k
  else if (StringHelper::equalsIgnoreCase(option,
120
2.72k
      LOG4CXX_STR("THREADINFO"), LOG4CXX_STR("threadinfo")))
121
1.46k
  {
122
1.46k
    setThreadInfo(OptionConverter::toBoolean(value, false));
123
1.46k
  }
124
1.26k
  else if (StringHelper::equalsIgnoreCase(option,
125
1.26k
      LOG4CXX_STR("PRETTYPRINT"), LOG4CXX_STR("prettyprint")))
126
1.26k
  {
127
1.26k
    setPrettyPrint(OptionConverter::toBoolean(value, false));
128
1.26k
  }
129
4.20k
}
130
131
void JSONLayout::format(LogString& output,
132
  const spi::LoggingEventPtr& event,
133
  Pool& p) const
134
2.63k
{
135
2.63k
  auto& lsMsg = event->getRenderedMessage();
136
2.63k
  output.reserve(m_priv->expectedPatternLength + lsMsg.size());
137
2.63k
  output.append(LOG4CXX_STR("{"));
138
2.63k
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
139
140
2.63k
  if (m_priv->prettyPrint)
141
1.26k
  {
142
1.26k
    output.append(m_priv->ppIndentL1);
143
1.26k
  }
144
145
2.63k
  output.append(LOG4CXX_STR("\"timestamp\": \""));
146
2.63k
  m_priv->dateFormat.format(output, event->getTimeStamp(), p);
147
2.63k
  output.append(LOG4CXX_STR("\","));
148
2.63k
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
149
150
2.63k
  if (m_priv->threadInfo)
151
1.46k
  {
152
1.46k
    if (m_priv->prettyPrint)
153
911
    {
154
911
      output.append(m_priv->ppIndentL1);
155
911
    }
156
1.46k
    appendQuotedEscapedString(output, LOG4CXX_STR("thread"));
157
1.46k
    output.append(LOG4CXX_STR(": "));
158
1.46k
    appendQuotedEscapedString(output, event->getThreadName());
159
1.46k
    output.append(LOG4CXX_STR(","));
160
1.46k
    output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
161
1.46k
  }
162
163
2.63k
  if (m_priv->prettyPrint)
164
1.26k
  {
165
1.26k
    output.append(m_priv->ppIndentL1);
166
1.26k
  }
167
168
2.63k
  output.append(LOG4CXX_STR("\"level\": "));
169
2.63k
  LogString level;
170
2.63k
  event->getLevel()->toString(level);
171
2.63k
  appendQuotedEscapedString(output, level);
172
2.63k
  output.append(LOG4CXX_STR(","));
173
2.63k
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
174
175
2.63k
  if (m_priv->prettyPrint)
176
1.26k
  {
177
1.26k
    output.append(m_priv->ppIndentL1);
178
1.26k
  }
179
180
2.63k
  output.append(LOG4CXX_STR("\"logger\": "));
181
2.63k
  appendQuotedEscapedString(output, event->getLoggerName());
182
2.63k
  output.append(LOG4CXX_STR(","));
183
2.63k
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
184
185
2.63k
  if (m_priv->prettyPrint)
186
1.26k
  {
187
1.26k
    output.append(m_priv->ppIndentL1);
188
1.26k
  }
189
190
2.63k
  output.append(LOG4CXX_STR("\"message\": "));
191
2.63k
  appendQuotedEscapedString(output, lsMsg);
192
193
2.63k
  appendSerializedMDC(output, event);
194
2.63k
  appendSerializedNDC(output, event);
195
196
2.63k
  if (m_priv->locationInfo)
197
1.48k
  {
198
1.48k
    output.append(LOG4CXX_STR(","));
199
1.48k
    output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
200
1.48k
    appendSerializedLocationInfo(output, event, p);
201
1.48k
  }
202
203
2.63k
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
204
2.63k
  output.append(LOG4CXX_STR("}"));
205
2.63k
  output.append(LOG4CXX_EOL);
206
2.63k
}
207
208
void JSONLayout::appendQuotedEscapedString(LogString& buf,
209
  const LogString& input) const
210
39.3k
{
211
39.3k
  appendItem(input, buf);
212
39.3k
}
213
214
void JSONLayout::appendItem(const LogString& input, LogString& buf)
215
39.3k
{
216
39.3k
  auto toHexDigit = [](int ch) -> int
217
2.68M
  {
218
2.68M
    return (10 <= ch ? (0x61 - 10) : 0x30) + ch;
219
2.68M
  };
220
  /* add leading quote */
221
39.3k
  buf.push_back(0x22);
222
223
39.3k
  size_t start = 0;
224
39.3k
  size_t index = 0;
225
226
39.3k
  for (int ch : input)
227
1.28M
  {
228
1.28M
    if (0x22 == ch || 0x5c == ch)
229
10.2k
      ;
230
1.27M
    else if (0x20 <= ch)
231
509k
    {
232
509k
      ++index;
233
509k
      continue;
234
509k
    }
235
771k
    if (start < index)
236
25.1k
    {
237
25.1k
      buf.append(input, start, index - start);
238
25.1k
    }
239
240
771k
    switch (ch)
241
771k
    {
242
29.8k
      case 0x08:
243
        /* \b backspace */
244
29.8k
        buf.push_back(0x5c);
245
29.8k
        buf.push_back('b');
246
29.8k
        break;
247
248
1.67k
      case 0x09:
249
        /* \t tab */
250
1.67k
        buf.push_back(0x5c);
251
1.67k
        buf.push_back('t');
252
1.67k
        break;
253
254
13.7k
      case 0x0a:
255
        /* \n newline */
256
13.7k
        buf.push_back(0x5c);
257
13.7k
        buf.push_back('n');
258
13.7k
        break;
259
260
29.4k
      case 0x0c:
261
        /* \f form feed */
262
29.4k
        buf.push_back(0x5c);
263
29.4k
        buf.push_back('f');
264
29.4k
        break;
265
266
16.3k
      case 0x0d:
267
        /* \r carriage return */
268
16.3k
        buf.push_back(0x5c);
269
16.3k
        buf.push_back('r');
270
16.3k
        break;
271
272
5.64k
      case 0x22:
273
        /* \" double quote */
274
5.64k
        buf.push_back(0x5c);
275
5.64k
        buf.push_back(0x22);
276
5.64k
        break;
277
278
4.63k
      case 0x5c:
279
        /* \\ backslash */
280
4.63k
        buf.push_back(0x5c);
281
4.63k
        buf.push_back(0x5c);
282
4.63k
        break;
283
284
670k
      default:
285
670k
        buf.push_back(0x5c);
286
670k
        buf.push_back(0x75); // 'u'
287
670k
        buf.push_back(toHexDigit((ch & 0xF000) >> 12));
288
670k
        buf.push_back(toHexDigit((ch & 0xF00) >> 8));
289
670k
        buf.push_back(toHexDigit((ch & 0xF0) >> 4));
290
670k
        buf.push_back(toHexDigit(ch & 0xF));
291
670k
        break;
292
771k
    }
293
771k
    start = ++index;
294
771k
  }
295
296
39.3k
  if (start < input.size())
297
23.6k
  {
298
23.6k
    buf.append(input, start, input.size() - start);
299
23.6k
  }
300
301
  /* add trailing quote */
302
39.3k
  buf.push_back(0x22);
303
39.3k
}
304
305
void JSONLayout::appendSerializedMDC(LogString& buf,
306
  const LoggingEventPtr& event) const
307
2.63k
{
308
2.63k
  LoggingEvent::KeySet keys = event->getMDCKeySet();
309
310
2.63k
  if (keys.empty())
311
0
  {
312
0
    return;
313
0
  }
314
315
2.63k
  buf.append(LOG4CXX_STR(","));
316
2.63k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
317
318
2.63k
  if (m_priv->prettyPrint)
319
1.26k
  {
320
1.26k
    buf.append(m_priv->ppIndentL1);
321
1.26k
  }
322
323
2.63k
  appendQuotedEscapedString(buf, LOG4CXX_STR("context_map"));
324
2.63k
  buf.append(LOG4CXX_STR(": {"));
325
2.63k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
326
327
2.63k
  for (LoggingEvent::KeySet::iterator it = keys.begin();
328
6.28k
    it != keys.end(); ++it)
329
3.64k
  {
330
3.64k
    if (m_priv->prettyPrint)
331
1.88k
    {
332
1.88k
      buf.append(m_priv->ppIndentL2);
333
1.88k
    }
334
335
3.64k
    appendQuotedEscapedString(buf, *it);
336
3.64k
    buf.append(LOG4CXX_STR(": "));
337
3.64k
    LogString value;
338
3.64k
    event->getMDC(*it, value);
339
3.64k
    appendQuotedEscapedString(buf, value);
340
341
    /* if this isn't the last k:v pair, we need a comma */
342
3.64k
    if (it + 1 != keys.end())
343
1.01k
    {
344
1.01k
      buf.append(LOG4CXX_STR(","));
345
1.01k
      buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
346
1.01k
    }
347
2.63k
    else
348
2.63k
    {
349
2.63k
      buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
350
2.63k
    }
351
3.64k
  }
352
353
2.63k
  if (m_priv->prettyPrint)
354
1.26k
  {
355
1.26k
    buf.append(m_priv->ppIndentL1);
356
1.26k
  }
357
358
2.63k
  buf.append(LOG4CXX_STR("}"));
359
2.63k
}
360
361
void JSONLayout::appendSerializedNDC(LogString& buf,
362
  const LoggingEventPtr& event) const
363
2.63k
{
364
2.63k
  LogString ndcVal;
365
366
2.63k
  if (!event->getNDC(ndcVal))
367
0
  {
368
0
    return;
369
0
  }
370
371
2.63k
  buf.append(LOG4CXX_STR(","));
372
2.63k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
373
374
2.63k
  if (m_priv->prettyPrint)
375
1.26k
  {
376
1.26k
    buf.append(m_priv->ppIndentL1);
377
1.26k
  }
378
379
2.63k
  appendQuotedEscapedString(buf, LOG4CXX_STR("context_stack"));
380
2.63k
  buf.append(LOG4CXX_STR(": ["));
381
2.63k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
382
383
2.63k
  if (m_priv->prettyPrint)
384
1.26k
  {
385
1.26k
    buf.append(m_priv->ppIndentL2);
386
1.26k
  }
387
388
2.63k
  appendQuotedEscapedString(buf, ndcVal);
389
2.63k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
390
391
2.63k
  if (m_priv->prettyPrint)
392
1.26k
  {
393
1.26k
    buf.append(m_priv->ppIndentL1);
394
1.26k
  }
395
396
2.63k
  buf.append(LOG4CXX_STR("]"));
397
2.63k
}
398
399
void JSONLayout::appendSerializedLocationInfo(LogString& buf,
400
  const LoggingEventPtr& event, Pool& p) const
401
1.48k
{
402
1.48k
  if (m_priv->prettyPrint)
403
886
  {
404
886
    buf.append(m_priv->ppIndentL1);
405
886
  }
406
407
1.48k
  appendQuotedEscapedString(buf, LOG4CXX_STR("location_info"));
408
1.48k
  buf.append(LOG4CXX_STR(": {"));
409
1.48k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
410
1.48k
  const LocationInfo& locInfo = event->getLocationInformation();
411
412
1.48k
  if (m_priv->prettyPrint)
413
886
  {
414
886
    buf.append(m_priv->ppIndentL2);
415
886
  }
416
417
1.48k
  appendQuotedEscapedString(buf, LOG4CXX_STR("file"));
418
1.48k
  buf.append(LOG4CXX_STR(": "));
419
1.48k
  LOG4CXX_DECODE_CHAR(fileName, locInfo.getFileName());
420
1.48k
  appendQuotedEscapedString(buf, fileName);
421
1.48k
  buf.append(LOG4CXX_STR(","));
422
1.48k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
423
424
1.48k
  if (m_priv->prettyPrint)
425
886
  {
426
886
    buf.append(m_priv->ppIndentL2);
427
886
  }
428
429
1.48k
  appendQuotedEscapedString(buf, LOG4CXX_STR("line"));
430
1.48k
  buf.append(LOG4CXX_STR(": "));
431
1.48k
  LogString lineNumber;
432
1.48k
  StringHelper::toString(locInfo.getLineNumber(), p, lineNumber);
433
1.48k
  appendQuotedEscapedString(buf, lineNumber);
434
1.48k
  buf.append(LOG4CXX_STR(","));
435
1.48k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
436
437
1.48k
  if (m_priv->prettyPrint)
438
886
  {
439
886
    buf.append(m_priv->ppIndentL2);
440
886
  }
441
442
1.48k
  appendQuotedEscapedString(buf, LOG4CXX_STR("class"));
443
1.48k
  buf.append(LOG4CXX_STR(": "));
444
1.48k
  LOG4CXX_DECODE_CHAR(className, locInfo.getClassName());
445
1.48k
  appendQuotedEscapedString(buf, className);
446
1.48k
  buf.append(LOG4CXX_STR(","));
447
1.48k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
448
449
1.48k
  if (m_priv->prettyPrint)
450
886
  {
451
886
    buf.append(m_priv->ppIndentL2);
452
886
  }
453
454
1.48k
  appendQuotedEscapedString(buf, LOG4CXX_STR("method"));
455
1.48k
  buf.append(LOG4CXX_STR(": "));
456
1.48k
  LOG4CXX_DECODE_CHAR(methodName, locInfo.getMethodName());
457
1.48k
  appendQuotedEscapedString(buf, methodName);
458
1.48k
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
459
460
1.48k
  if (m_priv->prettyPrint)
461
886
  {
462
886
    buf.append(m_priv->ppIndentL1);
463
886
  }
464
465
1.48k
  buf.append(LOG4CXX_STR("}"));
466
1.48k
}
467