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