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 package org.apache.logging.log4j.core.layout; 18 19 import java.io.IOException; 20 import java.io.Writer; 21 import java.nio.charset.Charset; 22 import java.nio.charset.StandardCharsets; 23 import java.util.HashMap; 24 import java.util.Map; 25 26 import org.apache.logging.log4j.core.Layout; 27 import org.apache.logging.log4j.core.LogEvent; 28 import org.apache.logging.log4j.core.config.Configuration; 29 import org.apache.logging.log4j.core.config.DefaultConfiguration; 30 import org.apache.logging.log4j.core.config.Node; 31 import org.apache.logging.log4j.core.config.plugins.Plugin; 32 import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 33 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 34 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 35 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 36 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 37 import org.apache.logging.log4j.core.layout.SyslogLayout.Builder; 38 39 /** 40 * Appends a series of JSON events as strings serialized as bytes. 41 * 42 * <h3>Complete well-formed JSON vs. fragment JSON</h3> 43 * <p> 44 * If you configure {@code complete="true"}, the appender outputs a well-formed JSON document. By default, with 45 * {@code complete="false"}, you should include the output as an <em>external file</em> in a separate file to form a 46 * well-formed JSON document. 47 * </p> 48 * <p> 49 * A well-formed JSON event follows this pattern: 50 * </p> 51 * 52 * <pre> 53 * { 54 "timeMillis": 1, 55 "thread": "MyThreadName", 56 "level": "DEBUG", 57 "loggerName": "a.B", 58 "marker": { 59 "name": "Marker1", 60 "parents": [{ 61 "name": "ParentMarker1", 62 "parents": [{ 63 "name": "GrandMotherMarker" 64 }, { 65 "name": "GrandFatherMarker" 66 }] 67 }, { 68 "name": "GrandFatherMarker" 69 }] 70 }, 71 "message": "Msg", 72 "thrown": { 73 "cause": { 74 "commonElementCount": 27, 75 "extendedStackTrace": [{ 76 "class": "org.apache.logging.log4j.core.layout.LogEventFixtures", 77 "method": "createLogEvent", 78 "file": "LogEventFixtures.java", 79 "line": 53, 80 "exact": false, 81 "location": "test-classes/", 82 "version": "?" 83 }], 84 "localizedMessage": "testNPEx", 85 "message": "testNPEx", 86 "name": "java.lang.NullPointerException" 87 }, 88 "commonElementCount": 0, 89 "extendedStackTrace": [{ 90 "class": "org.apache.logging.log4j.core.layout.LogEventFixtures", 91 "method": "createLogEvent", 92 "file": "LogEventFixtures.java", 93 "line": 56, 94 "exact": true, 95 "location": "test-classes/", 96 "version": "?" 97 }, { 98 "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest", 99 "method": "testAllFeatures", 100 "file": "JsonLayoutTest.java", 101 "line": 105, 102 "exact": true, 103 "location": "test-classes/", 104 "version": "?" 105 }, { 106 "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest", 107 "method": "testLocationOnCompactOnMdcOn", 108 "file": "JsonLayoutTest.java", 109 "line": 268, 110 "exact": true, 111 "location": "test-classes/", 112 "version": "?" 113 }, { 114 "class": "sun.reflect.NativeMethodAccessorImpl", 115 "method": "invoke", 116 "line": -1, 117 "exact": false, 118 "location": "?", 119 "version": "1.7.0_55" 120 }, { 121 "class": "sun.reflect.NativeMethodAccessorImpl", 122 "method": "invoke", 123 "line": -1, 124 "exact": false, 125 "location": "?", 126 "version": "1.7.0_55" 127 }, { 128 "class": "sun.reflect.DelegatingMethodAccessorImpl", 129 "method": "invoke", 130 "line": -1, 131 "exact": false, 132 "location": "?", 133 "version": "1.7.0_55" 134 }, { 135 "class": "java.lang.reflect.Method", 136 "method": "invoke", 137 "line": -1, 138 "exact": false, 139 "location": "?", 140 "version": "1.7.0_55" 141 }, { 142 "class": "org.junit.runners.model.FrameworkMethod$1", 143 "method": "runReflectiveCall", 144 "file": "FrameworkMethod.java", 145 "line": 47, 146 "exact": true, 147 "location": "junit-4.11.jar", 148 "version": "?" 149 }, { 150 "class": "org.junit.internal.runners.model.ReflectiveCallable", 151 "method": "run", 152 "file": "ReflectiveCallable.java", 153 "line": 12, 154 "exact": true, 155 "location": "junit-4.11.jar", 156 "version": "?" 157 }, { 158 "class": "org.junit.runners.model.FrameworkMethod", 159 "method": "invokeExplosively", 160 "file": "FrameworkMethod.java", 161 "line": 44, 162 "exact": true, 163 "location": "junit-4.11.jar", 164 "version": "?" 165 }, { 166 "class": "org.junit.internal.runners.statements.InvokeMethod", 167 "method": "evaluate", 168 "file": "InvokeMethod.java", 169 "line": 17, 170 "exact": true, 171 "location": "junit-4.11.jar", 172 "version": "?" 173 }, { 174 "class": "org.junit.runners.ParentRunner", 175 "method": "runLeaf", 176 "file": "ParentRunner.java", 177 "line": 271, 178 "exact": true, 179 "location": "junit-4.11.jar", 180 "version": "?" 181 }, { 182 "class": "org.junit.runners.BlockJUnit4ClassRunner", 183 "method": "runChild", 184 "file": "BlockJUnit4ClassRunner.java", 185 "line": 70, 186 "exact": true, 187 "location": "junit-4.11.jar", 188 "version": "?" 189 }, { 190 "class": "org.junit.runners.BlockJUnit4ClassRunner", 191 "method": "runChild", 192 "file": "BlockJUnit4ClassRunner.java", 193 "line": 50, 194 "exact": true, 195 "location": "junit-4.11.jar", 196 "version": "?" 197 }, { 198 "class": "org.junit.runners.ParentRunner$3", 199 "method": "run", 200 "file": "ParentRunner.java", 201 "line": 238, 202 "exact": true, 203 "location": "junit-4.11.jar", 204 "version": "?" 205 }, { 206 "class": "org.junit.runners.ParentRunner$1", 207 "method": "schedule", 208 "file": "ParentRunner.java", 209 "line": 63, 210 "exact": true, 211 "location": "junit-4.11.jar", 212 "version": "?" 213 }, { 214 "class": "org.junit.runners.ParentRunner", 215 "method": "runChildren", 216 "file": "ParentRunner.java", 217 "line": 236, 218 "exact": true, 219 "location": "junit-4.11.jar", 220 "version": "?" 221 }, { 222 "class": "org.junit.runners.ParentRunner", 223 "method": "access$000", 224 "file": "ParentRunner.java", 225 "line": 53, 226 "exact": true, 227 "location": "junit-4.11.jar", 228 "version": "?" 229 }, { 230 "class": "org.junit.runners.ParentRunner$2", 231 "method": "evaluate", 232 "file": "ParentRunner.java", 233 "line": 229, 234 "exact": true, 235 "location": "junit-4.11.jar", 236 "version": "?" 237 }, { 238 "class": "org.junit.internal.runners.statements.RunBefores", 239 "method": "evaluate", 240 "file": "RunBefores.java", 241 "line": 26, 242 "exact": true, 243 "location": "junit-4.11.jar", 244 "version": "?" 245 }, { 246 "class": "org.junit.internal.runners.statements.RunAfters", 247 "method": "evaluate", 248 "file": "RunAfters.java", 249 "line": 27, 250 "exact": true, 251 "location": "junit-4.11.jar", 252 "version": "?" 253 }, { 254 "class": "org.junit.runners.ParentRunner", 255 "method": "run", 256 "file": "ParentRunner.java", 257 "line": 309, 258 "exact": true, 259 "location": "junit-4.11.jar", 260 "version": "?" 261 }, { 262 "class": "org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference", 263 "method": "run", 264 "file": "JUnit4TestReference.java", 265 "line": 50, 266 "exact": true, 267 "location": ".cp/", 268 "version": "?" 269 }, { 270 "class": "org.eclipse.jdt.internal.junit.runner.TestExecution", 271 "method": "run", 272 "file": "TestExecution.java", 273 "line": 38, 274 "exact": true, 275 "location": ".cp/", 276 "version": "?" 277 }, { 278 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 279 "method": "runTests", 280 "file": "RemoteTestRunner.java", 281 "line": 467, 282 "exact": true, 283 "location": ".cp/", 284 "version": "?" 285 }, { 286 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 287 "method": "runTests", 288 "file": "RemoteTestRunner.java", 289 "line": 683, 290 "exact": true, 291 "location": ".cp/", 292 "version": "?" 293 }, { 294 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 295 "method": "run", 296 "file": "RemoteTestRunner.java", 297 "line": 390, 298 "exact": true, 299 "location": ".cp/", 300 "version": "?" 301 }, { 302 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 303 "method": "main", 304 "file": "RemoteTestRunner.java", 305 "line": 197, 306 "exact": true, 307 "location": ".cp/", 308 "version": "?" 309 }], 310 "localizedMessage": "testIOEx", 311 "message": "testIOEx", 312 "name": "java.io.IOException", 313 "suppressed": [{ 314 "commonElementCount": 0, 315 "extendedStackTrace": [{ 316 "class": "org.apache.logging.log4j.core.layout.LogEventFixtures", 317 "method": "createLogEvent", 318 "file": "LogEventFixtures.java", 319 "line": 57, 320 "exact": true, 321 "location": "test-classes/", 322 "version": "?" 323 }, { 324 "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest", 325 "method": "testAllFeatures", 326 "file": "JsonLayoutTest.java", 327 "line": 105, 328 "exact": true, 329 "location": "test-classes/", 330 "version": "?" 331 }, { 332 "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest", 333 "method": "testLocationOnCompactOnMdcOn", 334 "file": "JsonLayoutTest.java", 335 "line": 268, 336 "exact": true, 337 "location": "test-classes/", 338 "version": "?" 339 }, { 340 "class": "sun.reflect.NativeMethodAccessorImpl", 341 "method": "invoke", 342 "line": -1, 343 "exact": false, 344 "location": "?", 345 "version": "1.7.0_55" 346 }, { 347 "class": "sun.reflect.NativeMethodAccessorImpl", 348 "method": "invoke", 349 "line": -1, 350 "exact": false, 351 "location": "?", 352 "version": "1.7.0_55" 353 }, { 354 "class": "sun.reflect.DelegatingMethodAccessorImpl", 355 "method": "invoke", 356 "line": -1, 357 "exact": false, 358 "location": "?", 359 "version": "1.7.0_55" 360 }, { 361 "class": "java.lang.reflect.Method", 362 "method": "invoke", 363 "line": -1, 364 "exact": false, 365 "location": "?", 366 "version": "1.7.0_55" 367 }, { 368 "class": "org.junit.runners.model.FrameworkMethod$1", 369 "method": "runReflectiveCall", 370 "file": "FrameworkMethod.java", 371 "line": 47, 372 "exact": true, 373 "location": "junit-4.11.jar", 374 "version": "?" 375 }, { 376 "class": "org.junit.internal.runners.model.ReflectiveCallable", 377 "method": "run", 378 "file": "ReflectiveCallable.java", 379 "line": 12, 380 "exact": true, 381 "location": "junit-4.11.jar", 382 "version": "?" 383 }, { 384 "class": "org.junit.runners.model.FrameworkMethod", 385 "method": "invokeExplosively", 386 "file": "FrameworkMethod.java", 387 "line": 44, 388 "exact": true, 389 "location": "junit-4.11.jar", 390 "version": "?" 391 }, { 392 "class": "org.junit.internal.runners.statements.InvokeMethod", 393 "method": "evaluate", 394 "file": "InvokeMethod.java", 395 "line": 17, 396 "exact": true, 397 "location": "junit-4.11.jar", 398 "version": "?" 399 }, { 400 "class": "org.junit.runners.ParentRunner", 401 "method": "runLeaf", 402 "file": "ParentRunner.java", 403 "line": 271, 404 "exact": true, 405 "location": "junit-4.11.jar", 406 "version": "?" 407 }, { 408 "class": "org.junit.runners.BlockJUnit4ClassRunner", 409 "method": "runChild", 410 "file": "BlockJUnit4ClassRunner.java", 411 "line": 70, 412 "exact": true, 413 "location": "junit-4.11.jar", 414 "version": "?" 415 }, { 416 "class": "org.junit.runners.BlockJUnit4ClassRunner", 417 "method": "runChild", 418 "file": "BlockJUnit4ClassRunner.java", 419 "line": 50, 420 "exact": true, 421 "location": "junit-4.11.jar", 422 "version": "?" 423 }, { 424 "class": "org.junit.runners.ParentRunner$3", 425 "method": "run", 426 "file": "ParentRunner.java", 427 "line": 238, 428 "exact": true, 429 "location": "junit-4.11.jar", 430 "version": "?" 431 }, { 432 "class": "org.junit.runners.ParentRunner$1", 433 "method": "schedule", 434 "file": "ParentRunner.java", 435 "line": 63, 436 "exact": true, 437 "location": "junit-4.11.jar", 438 "version": "?" 439 }, { 440 "class": "org.junit.runners.ParentRunner", 441 "method": "runChildren", 442 "file": "ParentRunner.java", 443 "line": 236, 444 "exact": true, 445 "location": "junit-4.11.jar", 446 "version": "?" 447 }, { 448 "class": "org.junit.runners.ParentRunner", 449 "method": "access$000", 450 "file": "ParentRunner.java", 451 "line": 53, 452 "exact": true, 453 "location": "junit-4.11.jar", 454 "version": "?" 455 }, { 456 "class": "org.junit.runners.ParentRunner$2", 457 "method": "evaluate", 458 "file": "ParentRunner.java", 459 "line": 229, 460 "exact": true, 461 "location": "junit-4.11.jar", 462 "version": "?" 463 }, { 464 "class": "org.junit.internal.runners.statements.RunBefores", 465 "method": "evaluate", 466 "file": "RunBefores.java", 467 "line": 26, 468 "exact": true, 469 "location": "junit-4.11.jar", 470 "version": "?" 471 }, { 472 "class": "org.junit.internal.runners.statements.RunAfters", 473 "method": "evaluate", 474 "file": "RunAfters.java", 475 "line": 27, 476 "exact": true, 477 "location": "junit-4.11.jar", 478 "version": "?" 479 }, { 480 "class": "org.junit.runners.ParentRunner", 481 "method": "run", 482 "file": "ParentRunner.java", 483 "line": 309, 484 "exact": true, 485 "location": "junit-4.11.jar", 486 "version": "?" 487 }, { 488 "class": "org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference", 489 "method": "run", 490 "file": "JUnit4TestReference.java", 491 "line": 50, 492 "exact": true, 493 "location": ".cp/", 494 "version": "?" 495 }, { 496 "class": "org.eclipse.jdt.internal.junit.runner.TestExecution", 497 "method": "run", 498 "file": "TestExecution.java", 499 "line": 38, 500 "exact": true, 501 "location": ".cp/", 502 "version": "?" 503 }, { 504 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 505 "method": "runTests", 506 "file": "RemoteTestRunner.java", 507 "line": 467, 508 "exact": true, 509 "location": ".cp/", 510 "version": "?" 511 }, { 512 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 513 "method": "runTests", 514 "file": "RemoteTestRunner.java", 515 "line": 683, 516 "exact": true, 517 "location": ".cp/", 518 "version": "?" 519 }, { 520 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 521 "method": "run", 522 "file": "RemoteTestRunner.java", 523 "line": 390, 524 "exact": true, 525 "location": ".cp/", 526 "version": "?" 527 }, { 528 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 529 "method": "main", 530 "file": "RemoteTestRunner.java", 531 "line": 197, 532 "exact": true, 533 "location": ".cp/", 534 "version": "?" 535 }], 536 "localizedMessage": "I am suppressed exception 1", 537 "message": "I am suppressed exception 1", 538 "name": "java.lang.IndexOutOfBoundsException" 539 }, { 540 "commonElementCount": 0, 541 "extendedStackTrace": [{ 542 "class": "org.apache.logging.log4j.core.layout.LogEventFixtures", 543 "method": "createLogEvent", 544 "file": "LogEventFixtures.java", 545 "line": 58, 546 "exact": true, 547 "location": "test-classes/", 548 "version": "?" 549 }, { 550 "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest", 551 "method": "testAllFeatures", 552 "file": "JsonLayoutTest.java", 553 "line": 105, 554 "exact": true, 555 "location": "test-classes/", 556 "version": "?" 557 }, { 558 "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest", 559 "method": "testLocationOnCompactOnMdcOn", 560 "file": "JsonLayoutTest.java", 561 "line": 268, 562 "exact": true, 563 "location": "test-classes/", 564 "version": "?" 565 }, { 566 "class": "sun.reflect.NativeMethodAccessorImpl", 567 "method": "invoke", 568 "line": -1, 569 "exact": false, 570 "location": "?", 571 "version": "1.7.0_55" 572 }, { 573 "class": "sun.reflect.NativeMethodAccessorImpl", 574 "method": "invoke", 575 "line": -1, 576 "exact": false, 577 "location": "?", 578 "version": "1.7.0_55" 579 }, { 580 "class": "sun.reflect.DelegatingMethodAccessorImpl", 581 "method": "invoke", 582 "line": -1, 583 "exact": false, 584 "location": "?", 585 "version": "1.7.0_55" 586 }, { 587 "class": "java.lang.reflect.Method", 588 "method": "invoke", 589 "line": -1, 590 "exact": false, 591 "location": "?", 592 "version": "1.7.0_55" 593 }, { 594 "class": "org.junit.runners.model.FrameworkMethod$1", 595 "method": "runReflectiveCall", 596 "file": "FrameworkMethod.java", 597 "line": 47, 598 "exact": true, 599 "location": "junit-4.11.jar", 600 "version": "?" 601 }, { 602 "class": "org.junit.internal.runners.model.ReflectiveCallable", 603 "method": "run", 604 "file": "ReflectiveCallable.java", 605 "line": 12, 606 "exact": true, 607 "location": "junit-4.11.jar", 608 "version": "?" 609 }, { 610 "class": "org.junit.runners.model.FrameworkMethod", 611 "method": "invokeExplosively", 612 "file": "FrameworkMethod.java", 613 "line": 44, 614 "exact": true, 615 "location": "junit-4.11.jar", 616 "version": "?" 617 }, { 618 "class": "org.junit.internal.runners.statements.InvokeMethod", 619 "method": "evaluate", 620 "file": "InvokeMethod.java", 621 "line": 17, 622 "exact": true, 623 "location": "junit-4.11.jar", 624 "version": "?" 625 }, { 626 "class": "org.junit.runners.ParentRunner", 627 "method": "runLeaf", 628 "file": "ParentRunner.java", 629 "line": 271, 630 "exact": true, 631 "location": "junit-4.11.jar", 632 "version": "?" 633 }, { 634 "class": "org.junit.runners.BlockJUnit4ClassRunner", 635 "method": "runChild", 636 "file": "BlockJUnit4ClassRunner.java", 637 "line": 70, 638 "exact": true, 639 "location": "junit-4.11.jar", 640 "version": "?" 641 }, { 642 "class": "org.junit.runners.BlockJUnit4ClassRunner", 643 "method": "runChild", 644 "file": "BlockJUnit4ClassRunner.java", 645 "line": 50, 646 "exact": true, 647 "location": "junit-4.11.jar", 648 "version": "?" 649 }, { 650 "class": "org.junit.runners.ParentRunner$3", 651 "method": "run", 652 "file": "ParentRunner.java", 653 "line": 238, 654 "exact": true, 655 "location": "junit-4.11.jar", 656 "version": "?" 657 }, { 658 "class": "org.junit.runners.ParentRunner$1", 659 "method": "schedule", 660 "file": "ParentRunner.java", 661 "line": 63, 662 "exact": true, 663 "location": "junit-4.11.jar", 664 "version": "?" 665 }, { 666 "class": "org.junit.runners.ParentRunner", 667 "method": "runChildren", 668 "file": "ParentRunner.java", 669 "line": 236, 670 "exact": true, 671 "location": "junit-4.11.jar", 672 "version": "?" 673 }, { 674 "class": "org.junit.runners.ParentRunner", 675 "method": "access$000", 676 "file": "ParentRunner.java", 677 "line": 53, 678 "exact": true, 679 "location": "junit-4.11.jar", 680 "version": "?" 681 }, { 682 "class": "org.junit.runners.ParentRunner$2", 683 "method": "evaluate", 684 "file": "ParentRunner.java", 685 "line": 229, 686 "exact": true, 687 "location": "junit-4.11.jar", 688 "version": "?" 689 }, { 690 "class": "org.junit.internal.runners.statements.RunBefores", 691 "method": "evaluate", 692 "file": "RunBefores.java", 693 "line": 26, 694 "exact": true, 695 "location": "junit-4.11.jar", 696 "version": "?" 697 }, { 698 "class": "org.junit.internal.runners.statements.RunAfters", 699 "method": "evaluate", 700 "file": "RunAfters.java", 701 "line": 27, 702 "exact": true, 703 "location": "junit-4.11.jar", 704 "version": "?" 705 }, { 706 "class": "org.junit.runners.ParentRunner", 707 "method": "run", 708 "file": "ParentRunner.java", 709 "line": 309, 710 "exact": true, 711 "location": "junit-4.11.jar", 712 "version": "?" 713 }, { 714 "class": "org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference", 715 "method": "run", 716 "file": "JUnit4TestReference.java", 717 "line": 50, 718 "exact": true, 719 "location": ".cp/", 720 "version": "?" 721 }, { 722 "class": "org.eclipse.jdt.internal.junit.runner.TestExecution", 723 "method": "run", 724 "file": "TestExecution.java", 725 "line": 38, 726 "exact": true, 727 "location": ".cp/", 728 "version": "?" 729 }, { 730 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 731 "method": "runTests", 732 "file": "RemoteTestRunner.java", 733 "line": 467, 734 "exact": true, 735 "location": ".cp/", 736 "version": "?" 737 }, { 738 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 739 "method": "runTests", 740 "file": "RemoteTestRunner.java", 741 "line": 683, 742 "exact": true, 743 "location": ".cp/", 744 "version": "?" 745 }, { 746 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 747 "method": "run", 748 "file": "RemoteTestRunner.java", 749 "line": 390, 750 "exact": true, 751 "location": ".cp/", 752 "version": "?" 753 }, { 754 "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", 755 "method": "main", 756 "file": "RemoteTestRunner.java", 757 "line": 197, 758 "exact": true, 759 "location": ".cp/", 760 "version": "?" 761 }], 762 "localizedMessage": "I am suppressed exception 2", 763 "message": "I am suppressed exception 2", 764 "name": "java.lang.IndexOutOfBoundsException" 765 }] 766 }, 767 "loggerFQCN": "f.q.c.n", 768 "endOfBatch": false, 769 "contextMap": [{ 770 "key": "MDC.B", 771 "value": "B_Value" 772 }, { 773 "key": "MDC.A", 774 "value": "A_Value" 775 }], 776 "contextStack": ["stack_msg1", "stack_msg2"], 777 "source": { 778 "class": "org.apache.logging.log4j.core.layout.LogEventFixtures", 779 "method": "createLogEvent", 780 "file": "LogEventFixtures.java", 781 "line": 54 782 } 783 } 784 * </pre> 785 * <p> 786 * If {@code complete="false"}, the appender does not write the JSON open array character "[" at the start 787 * of the document, "]" and the end, nor comma "," between records. 788 * </p> 789 * <p> 790 * This approach enforces the independence of the JsonLayout and the appender where you embed it. 791 * </p> 792 * <h3>Encoding</h3> 793 * <p> 794 * Appenders using this layout should have their {@code charset} set to {@code UTF-8} or {@code UTF-16}, otherwise 795 * events containing non ASCII characters could result in corrupted log files. 796 * </p> 797 * <h3>Pretty vs. compact XML</h3> 798 * <p> 799 * By default, the JSON layout is not compact (a.k.a. "pretty") with {@code compact="false"}, which means the 800 * appender uses end-of-line characters and indents lines to format the text. If {@code compact="true"}, then no 801 * end-of-line or indentation is used. Message content may contain, of course, escaped end-of-lines. 802 * </p> 803 */ 804 @Plugin(name = "JsonLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true) 805 public final class JsonLayout extends AbstractJacksonLayout { 806 807 private static final String DEFAULT_FOOTER = "]"; 808 809 private static final String DEFAULT_HEADER = "["; 810 811 static final String CONTENT_TYPE = "application/json"; 812 813 public static class Builder<B extends Builder<B>> extends AbstractJacksonLayout.Builder<B> 814 implements org.apache.logging.log4j.core.util.Builder<JsonLayout> { 815 816 @PluginBuilderAttribute 817 private boolean locationInfo; 818 819 @PluginBuilderAttribute 820 private boolean properties; 821 822 @PluginBuilderAttribute 823 private boolean propertiesAsList; 824 825 @PluginBuilderAttribute 826 private boolean includeStacktrace = true; 827 828 public Builder() { 829 super(); 830 setCharset(StandardCharsets.UTF_8); 831 } 832 833 @Override 834 public JsonLayout build() { 835 final boolean encodeThreadContextAsList = properties && propertiesAsList; 836 final String headerPattern = toStringOrNull(getHeader()); 837 final String footerPattern = toStringOrNull(getFooter()); 838 return new JsonLayout(getConfiguration(), locationInfo, properties, encodeThreadContextAsList, isComplete(), 839 isCompact(), getEventEol(), headerPattern, footerPattern, getCharset(), includeStacktrace); 840 } 841 842 private String toStringOrNull(final byte[] header) { 843 return header == null ? null : new String(header, Charset.defaultCharset()); 844 } 845 846 public boolean isLocationInfo() { 847 return locationInfo; 848 } 849 850 public boolean isProperties() { 851 return properties; 852 } 853 854 public boolean isPropertiesAsList() { 855 return propertiesAsList; 856 } 857 858 /** 859 * If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". 860 * @return If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". 861 */ 862 public boolean isIncludeStacktrace() { 863 return includeStacktrace; 864 } 865 866 public B setLocationInfo(boolean locationInfo) { 867 this.locationInfo = locationInfo; 868 return asBuilder(); 869 } 870 871 public B setProperties(boolean properties) { 872 this.properties = properties; 873 return asBuilder(); 874 } 875 876 public B setPropertiesAsList(boolean propertiesAsList) { 877 this.propertiesAsList = propertiesAsList; 878 return asBuilder(); 879 } 880 881 /** 882 * If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". 883 * @param includeStacktrace If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". 884 * @return this builder 885 */ 886 public B setIncludeStacktrace(boolean includeStacktrace) { 887 this.includeStacktrace = includeStacktrace; 888 return asBuilder(); 889 } 890 } 891 892 protected JsonLayout(final Configuration config, final boolean locationInfo, final boolean properties, 893 final boolean encodeThreadContextAsList, 894 final boolean complete, final boolean compact, final boolean eventEol, final String headerPattern, 895 final String footerPattern, final Charset charset, final boolean includeStacktrace) { 896 super(config, new JacksonFactory.JSON(encodeThreadContextAsList, includeStacktrace).newWriter( 897 locationInfo, properties, compact), 898 charset, compact, complete, eventEol, 899 PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(headerPattern).setDefaultPattern(DEFAULT_HEADER).build(), 900 PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(footerPattern).setDefaultPattern(DEFAULT_FOOTER).build()); 901 } 902 903 /** 904 * Returns appropriate JSON header. 905 * 906 * @return a byte array containing the header, opening the JSON array. 907 */ 908 @Override 909 public byte[] getHeader() { 910 if (!this.complete) { 911 return null; 912 } 913 final StringBuilder buf = new StringBuilder(); 914 final String str = serializeToString(getHeaderSerializer()); 915 if (str != null) { 916 buf.append(str); 917 } 918 buf.append(this.eol); 919 return getBytes(buf.toString()); 920 } 921 922 /** 923 * Returns appropriate JSON footer. 924 * 925 * @return a byte array containing the footer, closing the JSON array. 926 */ 927 @Override 928 public byte[] getFooter() { 929 if (!this.complete) { 930 return null; 931 } 932 final StringBuilder buf = new StringBuilder(); 933 buf.append(this.eol); 934 final String str = serializeToString(getFooterSerializer()); 935 if (str != null) { 936 buf.append(str); 937 } 938 buf.append(this.eol); 939 return getBytes(buf.toString()); 940 } 941 942 @Override 943 public Map<String, String> getContentFormat() { 944 final Map<String, String> result = new HashMap<>(); 945 result.put("version", "2.0"); 946 return result; 947 } 948 949 @Override 950 /** 951 * @return The content type. 952 */ 953 public String getContentType() { 954 return CONTENT_TYPE + "; charset=" + this.getCharset(); 955 } 956 957 /** 958 * Creates a JSON Layout. 959 * @param config 960 * The plugin configuration. 961 * @param locationInfo 962 * If "true", includes the location information in the generated JSON. 963 * @param properties 964 * If "true", includes the thread context map in the generated JSON. 965 * @param propertiesAsList 966 * If true, the thread context map is included as a list of map entry objects, where each entry has 967 * a "key" attribute (whose value is the key) and a "value" attribute (whose value is the value). 968 * Defaults to false, in which case the thread context map is included as a simple map of key-value 969 * pairs. 970 * @param complete 971 * If "true", includes the JSON header and footer, and comma between records. 972 * @param compact 973 * If "true", does not use end-of-lines and indentation, defaults to "false". 974 * @param eventEol 975 * If "true", forces an EOL after each log event (even if compact is "true"), defaults to "false". This 976 * allows one even per line, even in compact mode. 977 * @param headerPattern 978 * The header pattern, defaults to {@code "["} if null. 979 * @param footerPattern 980 * The header pattern, defaults to {@code "]"} if null. 981 * @param charset 982 * The character set to use, if {@code null}, uses "UTF-8". 983 * @param includeStacktrace 984 * If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". 985 * @return A JSON Layout. 986 */ 987 @Deprecated 988 public static JsonLayout createLayout( 989 // @formatter:off 990 @PluginConfiguration final Configuration config, 991 @PluginAttribute(value = "locationInfo") final boolean locationInfo, 992 @PluginAttribute(value = "properties") final boolean properties, 993 @PluginAttribute(value = "propertiesAsList") final boolean propertiesAsList, 994 @PluginAttribute(value = "complete") final boolean complete, 995 @PluginAttribute(value = "compact") final boolean compact, 996 @PluginAttribute(value = "eventEol") final boolean eventEol, 997 @PluginAttribute(value = "header", defaultString = DEFAULT_HEADER) final String headerPattern, 998 @PluginAttribute(value = "footer", defaultString = DEFAULT_FOOTER) final String footerPattern, 999 @PluginAttribute(value = "charset", defaultString = "UTF-8") final Charset charset, 1000 @PluginAttribute(value = "includeStacktrace", defaultBoolean = true) final boolean includeStacktrace 1001 // @formatter:on 1002 ) { 1003 final boolean encodeThreadContextAsList = properties && propertiesAsList; 1004 return new JsonLayout(config, locationInfo, properties, encodeThreadContextAsList, complete, compact, eventEol, 1005 headerPattern, footerPattern, charset, includeStacktrace); 1006 } 1007 1008 @PluginBuilderFactory 1009 public static <B extends Builder<B>> B newBuilder() { 1010 return new Builder<B>().asBuilder(); 1011 } 1012 1013 /** 1014 * Creates a JSON Layout using the default settings. Useful for testing. 1015 * 1016 * @return A JSON Layout. 1017 */ 1018 public static JsonLayout createDefaultLayout() { 1019 return new JsonLayout(new DefaultConfiguration(), false, false, false, false, false, false, 1020 DEFAULT_HEADER, DEFAULT_FOOTER, StandardCharsets.UTF_8, true); 1021 } 1022 1023 @Override 1024 public void toSerializable(final LogEvent event, final Writer writer) throws IOException { 1025 if (complete && eventCount > 0) { 1026 writer.append(", "); 1027 } 1028 super.toSerializable(event, writer); 1029 } 1030 }