Coverage Report

Created: 2025-07-01 06:08

/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/iso8601dateformat.h>
24
#include <log4cxx/helpers/stringhelper.h>
25
#include <log4cxx/helpers/transcoder.h>
26
27
#include <string.h>
28
29
using namespace LOG4CXX_NS;
30
using namespace LOG4CXX_NS::helpers;
31
using namespace LOG4CXX_NS::spi;
32
33
IMPLEMENT_LOG4CXX_OBJECT(JSONLayout)
34
35
struct JSONLayout::JSONLayoutPrivate
36
{
37
  JSONLayoutPrivate() :
38
0
    locationInfo(false),
39
0
    prettyPrint(false),
40
0
    dateFormat(),
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
  helpers::ISO8601DateFormat dateFormat;
51
52
  LogString ppIndentL1;
53
  LogString ppIndentL2;
54
55
  // Expected length of a formatted event excluding the message text
56
  size_t expectedPatternLength;
57
58
  // Thread info is not included by default
59
  bool threadInfo; //= false
60
};
61
62
JSONLayout::JSONLayout() :
63
0
  m_priv(std::make_unique<JSONLayoutPrivate>())
64
0
{
65
0
}
Unexecuted instantiation: log4cxx::JSONLayout::JSONLayout()
Unexecuted instantiation: log4cxx::JSONLayout::JSONLayout()
66
67
0
JSONLayout::~JSONLayout(){}
68
69
void JSONLayout::setLocationInfo(bool locationInfoFlag)
70
0
{
71
0
  m_priv->locationInfo = locationInfoFlag;
72
0
}
73
74
bool JSONLayout::getLocationInfo() const
75
0
{
76
0
  return m_priv->locationInfo;
77
0
}
78
79
void JSONLayout::setPrettyPrint(bool prettyPrintFlag)
80
0
{
81
0
  m_priv->prettyPrint = prettyPrintFlag;
82
0
}
83
84
bool JSONLayout::getPrettyPrint() const
85
0
{
86
0
  return m_priv->prettyPrint;
87
0
}
88
89
void JSONLayout::setThreadInfo(bool newValue)
90
0
{
91
0
  m_priv->threadInfo = newValue;
92
0
}
93
94
bool JSONLayout::getThreadInfo() const
95
0
{
96
0
  return m_priv->threadInfo;
97
0
}
98
99
LogString JSONLayout::getContentType() const
100
0
{
101
0
  return LOG4CXX_STR("application/json");
102
0
}
103
104
void JSONLayout::activateOptions(helpers::Pool& /* p */)
105
0
{
106
0
  m_priv->expectedPatternLength = getFormattedEventCharacterCount() * 2;
107
0
}
108
109
void JSONLayout::setOption(const LogString& option, const LogString& value)
110
0
{
111
0
  if (StringHelper::equalsIgnoreCase(option,
112
0
      LOG4CXX_STR("LOCATIONINFO"), LOG4CXX_STR("locationinfo")))
113
0
  {
114
0
    setLocationInfo(OptionConverter::toBoolean(value, false));
115
0
  }
116
0
  else if (StringHelper::equalsIgnoreCase(option,
117
0
      LOG4CXX_STR("THREADINFO"), LOG4CXX_STR("threadinfo")))
118
0
  {
119
0
    setThreadInfo(OptionConverter::toBoolean(value, false));
120
0
  }
121
0
  else if (StringHelper::equalsIgnoreCase(option,
122
0
      LOG4CXX_STR("PRETTYPRINT"), LOG4CXX_STR("prettyprint")))
123
0
  {
124
0
    setPrettyPrint(OptionConverter::toBoolean(value, false));
125
0
  }
126
0
}
127
128
void JSONLayout::format(LogString& output,
129
  const spi::LoggingEventPtr& event,
130
  Pool& p) const
131
0
{
132
0
  output.reserve(m_priv->expectedPatternLength + event->getMessage().size());
133
0
  output.append(LOG4CXX_STR("{"));
134
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
135
136
0
  if (m_priv->prettyPrint)
137
0
  {
138
0
    output.append(m_priv->ppIndentL1);
139
0
  }
140
141
0
  appendQuotedEscapedString(output, LOG4CXX_STR("timestamp"));
142
0
  output.append(LOG4CXX_STR(": "));
143
0
  LogString timestamp;
144
0
  m_priv->dateFormat.format(timestamp, event->getTimeStamp(), p);
145
0
  appendQuotedEscapedString(output, timestamp);
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
  appendQuotedEscapedString(output, LOG4CXX_STR("level"));
168
0
  output.append(LOG4CXX_STR(": "));
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
  appendQuotedEscapedString(output, LOG4CXX_STR("logger"));
181
0
  output.append(LOG4CXX_STR(": "));
182
0
  appendQuotedEscapedString(output, event->getLoggerName());
183
0
  output.append(LOG4CXX_STR(","));
184
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
185
186
0
  if (m_priv->prettyPrint)
187
0
  {
188
0
    output.append(m_priv->ppIndentL1);
189
0
  }
190
191
0
  appendQuotedEscapedString(output, LOG4CXX_STR("message"));
192
0
  output.append(LOG4CXX_STR(": "));
193
0
  appendQuotedEscapedString(output, event->getMessage());
194
195
0
  appendSerializedMDC(output, event);
196
0
  appendSerializedNDC(output, event);
197
198
0
  if (m_priv->locationInfo)
199
0
  {
200
0
    output.append(LOG4CXX_STR(","));
201
0
    output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
202
0
    appendSerializedLocationInfo(output, event, p);
203
0
  }
204
205
0
  output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
206
0
  output.append(LOG4CXX_STR("}"));
207
0
  output.append(LOG4CXX_EOL);
208
0
}
209
210
void JSONLayout::appendQuotedEscapedString(LogString& buf,
211
  const LogString& input) const
212
0
{
213
0
  appendItem(input, buf);
214
0
}
215
216
void JSONLayout::appendItem(const LogString& input, LogString& buf)
217
0
{
218
  /* add leading quote */
219
0
  buf.push_back(0x22);
220
221
0
  logchar specialChars[] =
222
0
  {
223
0
    0x08,   /* \b backspace         */
224
0
    0x09,   /* \t tab               */
225
0
    0x0a,   /* \n newline           */
226
0
    0x0c,   /* \f form feed         */
227
0
    0x0d,   /* \r carriage return   */
228
0
    0x22,   /* \" double quote      */
229
0
    0x5c,   /* \\ backslash         */
230
0
    0x00    /* terminating NULL for C-strings */
231
0
  };
232
233
0
  size_t start = 0;
234
0
  size_t found = input.find_first_of(specialChars, start);
235
236
0
  while (found != LogString::npos)
237
0
  {
238
0
    if (found > start)
239
0
    {
240
0
      buf.append(input, start, found - start);
241
0
    }
242
243
0
    switch (input[found])
244
0
    {
245
0
      case 0x08:
246
        /* \b backspace */
247
0
        buf.push_back(0x5c);
248
0
        buf.push_back('b');
249
0
        break;
250
251
0
      case 0x09:
252
        /* \t tab */
253
0
        buf.push_back(0x5c);
254
0
        buf.push_back('t');
255
0
        break;
256
257
0
      case 0x0a:
258
        /* \n newline */
259
0
        buf.push_back(0x5c);
260
0
        buf.push_back('n');
261
0
        break;
262
263
0
      case 0x0c:
264
        /* \f form feed */
265
0
        buf.push_back(0x5c);
266
0
        buf.push_back('f');
267
0
        break;
268
269
0
      case 0x0d:
270
        /* \r carriage return */
271
0
        buf.push_back(0x5c);
272
0
        buf.push_back('r');
273
0
        break;
274
275
0
      case 0x22:
276
        /* \" double quote */
277
0
        buf.push_back(0x5c);
278
0
        buf.push_back(0x22);
279
0
        break;
280
281
0
      case 0x5c:
282
        /* \\ backslash */
283
0
        buf.push_back(0x5c);
284
0
        buf.push_back(0x5c);
285
0
        break;
286
287
0
      default:
288
0
        buf.push_back(input[found]);
289
0
        break;
290
0
    }
291
292
0
    start = found + 1;
293
294
0
    if (found < input.size())
295
0
    {
296
0
      found = input.find_first_of(specialChars, start);
297
0
    }
298
0
    else
299
0
    {
300
0
      found = LogString::npos;
301
0
    }
302
0
  }
303
304
0
  if (start < input.size())
305
0
  {
306
0
    buf.append(input, start, input.size() - start);
307
0
  }
308
309
  /* add trailing quote */
310
0
  buf.push_back(0x22);
311
0
}
312
313
void JSONLayout::appendSerializedMDC(LogString& buf,
314
  const LoggingEventPtr& event) const
315
0
{
316
0
  LoggingEvent::KeySet keys = event->getMDCKeySet();
317
318
0
  if (keys.empty())
319
0
  {
320
0
    return;
321
0
  }
322
323
0
  buf.append(LOG4CXX_STR(","));
324
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
325
326
0
  if (m_priv->prettyPrint)
327
0
  {
328
0
    buf.append(m_priv->ppIndentL1);
329
0
  }
330
331
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("context_map"));
332
0
  buf.append(LOG4CXX_STR(": {"));
333
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
334
335
0
  for (LoggingEvent::KeySet::iterator it = keys.begin();
336
0
    it != keys.end(); ++it)
337
0
  {
338
0
    if (m_priv->prettyPrint)
339
0
    {
340
0
      buf.append(m_priv->ppIndentL2);
341
0
    }
342
343
0
    appendQuotedEscapedString(buf, *it);
344
0
    buf.append(LOG4CXX_STR(": "));
345
0
    LogString value;
346
0
    event->getMDC(*it, value);
347
0
    appendQuotedEscapedString(buf, value);
348
349
    /* if this isn't the last k:v pair, we need a comma */
350
0
    if (it + 1 != keys.end())
351
0
    {
352
0
      buf.append(LOG4CXX_STR(","));
353
0
      buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
354
0
    }
355
0
    else
356
0
    {
357
0
      buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
358
0
    }
359
0
  }
360
361
0
  if (m_priv->prettyPrint)
362
0
  {
363
0
    buf.append(m_priv->ppIndentL1);
364
0
  }
365
366
0
  buf.append(LOG4CXX_STR("}"));
367
0
}
368
369
void JSONLayout::appendSerializedNDC(LogString& buf,
370
  const LoggingEventPtr& event) const
371
0
{
372
0
  LogString ndcVal;
373
374
0
  if (!event->getNDC(ndcVal))
375
0
  {
376
0
    return;
377
0
  }
378
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->ppIndentL1);
385
0
  }
386
387
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("context_stack"));
388
0
  buf.append(LOG4CXX_STR(": ["));
389
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
390
391
0
  if (m_priv->prettyPrint)
392
0
  {
393
0
    buf.append(m_priv->ppIndentL2);
394
0
  }
395
396
0
  appendQuotedEscapedString(buf, ndcVal);
397
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
398
399
0
  if (m_priv->prettyPrint)
400
0
  {
401
0
    buf.append(m_priv->ppIndentL1);
402
0
  }
403
404
0
  buf.append(LOG4CXX_STR("]"));
405
0
}
406
407
void JSONLayout::appendSerializedLocationInfo(LogString& buf,
408
  const LoggingEventPtr& event, Pool& p) const
409
0
{
410
0
  if (m_priv->prettyPrint)
411
0
  {
412
0
    buf.append(m_priv->ppIndentL1);
413
0
  }
414
415
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("location_info"));
416
0
  buf.append(LOG4CXX_STR(": {"));
417
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
418
0
  const LocationInfo& locInfo = event->getLocationInformation();
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("file"));
426
0
  buf.append(LOG4CXX_STR(": "));
427
0
  LOG4CXX_DECODE_CHAR(fileName, locInfo.getFileName());
428
0
  appendQuotedEscapedString(buf, fileName);
429
0
  buf.append(LOG4CXX_STR(","));
430
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
431
432
0
  if (m_priv->prettyPrint)
433
0
  {
434
0
    buf.append(m_priv->ppIndentL2);
435
0
  }
436
437
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("line"));
438
0
  buf.append(LOG4CXX_STR(": "));
439
0
  LogString lineNumber;
440
0
  StringHelper::toString(locInfo.getLineNumber(), p, lineNumber);
441
0
  appendQuotedEscapedString(buf, lineNumber);
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("class"));
451
0
  buf.append(LOG4CXX_STR(": "));
452
0
  LOG4CXX_DECODE_CHAR(className, locInfo.getClassName());
453
0
  appendQuotedEscapedString(buf, className);
454
0
  buf.append(LOG4CXX_STR(","));
455
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
456
457
0
  if (m_priv->prettyPrint)
458
0
  {
459
0
    buf.append(m_priv->ppIndentL2);
460
0
  }
461
462
0
  appendQuotedEscapedString(buf, LOG4CXX_STR("method"));
463
0
  buf.append(LOG4CXX_STR(": "));
464
0
  LOG4CXX_DECODE_CHAR(methodName, locInfo.getMethodName());
465
0
  appendQuotedEscapedString(buf, methodName);
466
0
  buf.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
467
468
0
  if (m_priv->prettyPrint)
469
0
  {
470
0
    buf.append(m_priv->ppIndentL1);
471
0
  }
472
473
0
  buf.append(LOG4CXX_STR("}"));
474
0
}
475