Coverage Report

Created: 2025-08-26 06:48

/src/logging-log4cxx/src/main/cpp/jsonlayout.cpp
Line
Count
Source (jump to first uncovered line)
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
  output.reserve(m_priv->expectedPatternLength + event->getMessage().size());
136
0
  output.append(LOG4CXX_STR("{"));
137
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
138
139
0
  if (m_priv->prettyPrint)
140
0
  {
141
0
    output.append(m_priv->ppIndentL1);
142
0
  }
143
144
0
  output.append(LOG4CXX_STR("\"timestamp\": \""));
145
0
  m_priv->dateFormat.format(output, event->getTimeStamp(), p);
146
0
  output.append(LOG4CXX_STR("\","));
147
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
148
149
0
  if (m_priv->threadInfo)
150
0
  {
151
0
    if (m_priv->prettyPrint)
152
0
    {
153
0
      output.append(m_priv->ppIndentL1);
154
0
    }
155
0
    appendQuotedEscapedString(output, LOG4CXX_STR("thread"));
156
0
    output.append(LOG4CXX_STR(": "));
157
0
    appendQuotedEscapedString(output, event->getThreadName());
158
0
    output.append(LOG4CXX_STR(","));
159
0
    output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
160
0
  }
161
162
0
  if (m_priv->prettyPrint)
163
0
  {
164
0
    output.append(m_priv->ppIndentL1);
165
0
  }
166
167
0
  output.append(LOG4CXX_STR("\"level\": "));
168
0
  LogString level;
169
0
  event->getLevel()->toString(level);
170
0
  appendQuotedEscapedString(output, level);
171
0
  output.append(LOG4CXX_STR(","));
172
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
173
174
0
  if (m_priv->prettyPrint)
175
0
  {
176
0
    output.append(m_priv->ppIndentL1);
177
0
  }
178
179
0
  output.append(LOG4CXX_STR("\"logger\": "));
180
0
  appendQuotedEscapedString(output, event->getLoggerName());
181
0
  output.append(LOG4CXX_STR(","));
182
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
183
184
0
  if (m_priv->prettyPrint)
185
0
  {
186
0
    output.append(m_priv->ppIndentL1);
187
0
  }
188
189
0
  output.append(LOG4CXX_STR("\"message\": "));
190
0
  appendQuotedEscapedString(output, event->getMessage());
191
192
0
  appendSerializedMDC(output, event);
193
0
  appendSerializedNDC(output, event);
194
195
0
  if (m_priv->locationInfo)
196
0
  {
197
0
    output.append(LOG4CXX_STR(","));
198
0
    output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
199
0
    appendSerializedLocationInfo(output, event, p);
200
0
  }
201
202
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
203
0
  output.append(LOG4CXX_STR("}"));
204
0
  output.append(LOG4CXX_EOL);
205
0
}
206
207
void JSONLayout::appendQuotedEscapedString(LogString& buf,
208
  const LogString& input) const
209
0
{
210
0
  appendItem(input, buf);
211
0
}
212
213
void JSONLayout::appendItem(const LogString& input, LogString& buf)
214
0
{
215
0
  auto toHexDigit = [](int ch) -> int
216
0
  {
217
0
    return (10 <= ch ? (0x61 - 10) : 0x30) + ch;
218
0
  };
219
  /* add leading quote */
220
0
  buf.push_back(0x22);
221
222
0
  size_t start = 0;
223
0
  size_t index = 0;
224
225
0
  for (int ch : input)
226
0
  {
227
0
    if (0x22 == ch || 0x5c == ch)
228
0
      ;
229
0
    else if (0x20 <= ch)
230
0
    {
231
0
      ++index;
232
0
      continue;
233
0
    }
234
0
    if (start < index)
235
0
    {
236
0
      buf.append(input, start, index - start);
237
0
    }
238
239
0
    switch (ch)
240
0
    {
241
0
      case 0x08:
242
        /* \b backspace */
243
0
        buf.push_back(0x5c);
244
0
        buf.push_back('b');
245
0
        break;
246
247
0
      case 0x09:
248
        /* \t tab */
249
0
        buf.push_back(0x5c);
250
0
        buf.push_back('t');
251
0
        break;
252
253
0
      case 0x0a:
254
        /* \n newline */
255
0
        buf.push_back(0x5c);
256
0
        buf.push_back('n');
257
0
        break;
258
259
0
      case 0x0c:
260
        /* \f form feed */
261
0
        buf.push_back(0x5c);
262
0
        buf.push_back('f');
263
0
        break;
264
265
0
      case 0x0d:
266
        /* \r carriage return */
267
0
        buf.push_back(0x5c);
268
0
        buf.push_back('r');
269
0
        break;
270
271
0
      case 0x22:
272
        /* \" double quote */
273
0
        buf.push_back(0x5c);
274
0
        buf.push_back(0x22);
275
0
        break;
276
277
0
      case 0x5c:
278
        /* \\ backslash */
279
0
        buf.push_back(0x5c);
280
0
        buf.push_back(0x5c);
281
0
        break;
282
283
0
      default:
284
0
        buf.push_back(0x5c);
285
0
        buf.push_back(0x75); // 'u'
286
0
        buf.push_back(toHexDigit((ch & 0xF000) >> 12));
287
0
        buf.push_back(toHexDigit((ch & 0xF00) >> 8));
288
0
        buf.push_back(toHexDigit((ch & 0xF0) >> 4));
289
0
        buf.push_back(toHexDigit(ch & 0xF));
290
0
        break;
291
0
    }
292
0
    start = ++index;
293
0
  }
294
295
0
  if (start < input.size())
296
0
  {
297
0
    buf.append(input, start, input.size() - start);
298
0
  }
299
300
  /* add trailing quote */
301
0
  buf.push_back(0x22);
302
0
}
303
304
void JSONLayout::appendSerializedMDC(LogString& buf,
305
  const LoggingEventPtr& event) const
306
0
{
307
0
  LoggingEvent::KeySet keys = event->getMDCKeySet();
308
309
0
  if (keys.empty())
310
0
  {
311
0
    return;
312
0
  }
313
314
0
  buf.append(LOG4CXX_STR(","));
315
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
316
317
0
  if (m_priv->prettyPrint)
318
0
  {
319
0
    buf.append(m_priv->ppIndentL1);
320
0
  }
321
322
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("context_map"));
323
0
  buf.append(LOG4CXX_STR(": {"));
324
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
325
326
0
  for (LoggingEvent::KeySet::iterator it = keys.begin();
327
0
    it != keys.end(); ++it)
328
0
  {
329
0
    if (m_priv->prettyPrint)
330
0
    {
331
0
      buf.append(m_priv->ppIndentL2);
332
0
    }
333
334
0
    appendQuotedEscapedString(buf, *it);
335
0
    buf.append(LOG4CXX_STR(": "));
336
0
    LogString value;
337
0
    event->getMDC(*it, value);
338
0
    appendQuotedEscapedString(buf, value);
339
340
    /* if this isn't the last k:v pair, we need a comma */
341
0
    if (it + 1 != keys.end())
342
0
    {
343
0
      buf.append(LOG4CXX_STR(","));
344
0
      buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
345
0
    }
346
0
    else
347
0
    {
348
0
      buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
349
0
    }
350
0
  }
351
352
0
  if (m_priv->prettyPrint)
353
0
  {
354
0
    buf.append(m_priv->ppIndentL1);
355
0
  }
356
357
0
  buf.append(LOG4CXX_STR("}"));
358
0
}
359
360
void JSONLayout::appendSerializedNDC(LogString& buf,
361
  const LoggingEventPtr& event) const
362
0
{
363
0
  LogString ndcVal;
364
365
0
  if (!event->getNDC(ndcVal))
366
0
  {
367
0
    return;
368
0
  }
369
370
0
  buf.append(LOG4CXX_STR(","));
371
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
372
373
0
  if (m_priv->prettyPrint)
374
0
  {
375
0
    buf.append(m_priv->ppIndentL1);
376
0
  }
377
378
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("context_stack"));
379
0
  buf.append(LOG4CXX_STR(": ["));
380
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
381
382
0
  if (m_priv->prettyPrint)
383
0
  {
384
0
    buf.append(m_priv->ppIndentL2);
385
0
  }
386
387
0
  appendQuotedEscapedString(buf, ndcVal);
388
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
389
390
0
  if (m_priv->prettyPrint)
391
0
  {
392
0
    buf.append(m_priv->ppIndentL1);
393
0
  }
394
395
0
  buf.append(LOG4CXX_STR("]"));
396
0
}
397
398
void JSONLayout::appendSerializedLocationInfo(LogString& buf,
399
  const LoggingEventPtr& event, Pool& p) const
400
0
{
401
0
  if (m_priv->prettyPrint)
402
0
  {
403
0
    buf.append(m_priv->ppIndentL1);
404
0
  }
405
406
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("location_info"));
407
0
  buf.append(LOG4CXX_STR(": {"));
408
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
409
0
  const LocationInfo& locInfo = event->getLocationInformation();
410
411
0
  if (m_priv->prettyPrint)
412
0
  {
413
0
    buf.append(m_priv->ppIndentL2);
414
0
  }
415
416
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("file"));
417
0
  buf.append(LOG4CXX_STR(": "));
418
0
  LOG4CXX_DECODE_CHAR(fileName, locInfo.getFileName());
419
0
  appendQuotedEscapedString(buf, fileName);
420
0
  buf.append(LOG4CXX_STR(","));
421
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
422
423
0
  if (m_priv->prettyPrint)
424
0
  {
425
0
    buf.append(m_priv->ppIndentL2);
426
0
  }
427
428
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("line"));
429
0
  buf.append(LOG4CXX_STR(": "));
430
0
  LogString lineNumber;
431
0
  StringHelper::toString(locInfo.getLineNumber(), p, lineNumber);
432
0
  appendQuotedEscapedString(buf, lineNumber);
433
0
  buf.append(LOG4CXX_STR(","));
434
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
435
436
0
  if (m_priv->prettyPrint)
437
0
  {
438
0
    buf.append(m_priv->ppIndentL2);
439
0
  }
440
441
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("class"));
442
0
  buf.append(LOG4CXX_STR(": "));
443
0
  LOG4CXX_DECODE_CHAR(className, locInfo.getClassName());
444
0
  appendQuotedEscapedString(buf, className);
445
0
  buf.append(LOG4CXX_STR(","));
446
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
447
448
0
  if (m_priv->prettyPrint)
449
0
  {
450
0
    buf.append(m_priv->ppIndentL2);
451
0
  }
452
453
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("method"));
454
0
  buf.append(LOG4CXX_STR(": "));
455
0
  LOG4CXX_DECODE_CHAR(methodName, locInfo.getMethodName());
456
0
  appendQuotedEscapedString(buf, methodName);
457
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
458
459
0
  if (m_priv->prettyPrint)
460
0
  {
461
0
    buf.append(m_priv->ppIndentL1);
462
0
  }
463
464
0
  buf.append(LOG4CXX_STR("}"));
465
0
}
466