| <!DOCTYPE html> |
| |
| |
| <!-- |
| | Generated by Apache Maven Doxia Site Renderer 1.11.1 from target/generated-sources/site/asciidoc/manual/json-template-layout.adoc at 2024-03-06 |
| | Rendered using Apache Maven Fluido Skin 1.11.2 |
| --> |
| <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| <meta name="generator" content="Apache Maven Doxia Site Renderer 1.11.1" /> |
| <title>Log4j – </title> |
| <link rel="stylesheet" href="../css/apache-maven-fluido-1.11.2.min.css" /> |
| <link rel="stylesheet" href="../css/site.css" /> |
| <link rel="stylesheet" href="../css/print.css" media="print" /> |
| <script src="../js/apache-maven-fluido-1.11.2.min.js"></script> |
| </head> |
| <body class="topBarDisabled"> |
| <div class="container-fluid"> |
| <header> |
| <div id="banner"> |
| <div class="pull-left"><a href="../../.." id="bannerLeft"><img src="../images/ls-logo.jpg" alt="" style="" /></a></div> |
| <div class="pull-right"><a href=".././" id="bannerRight"><img src="../images/logo.png" alt="" style="" /></a></div> |
| <div class="clear"><hr/></div> |
| </div> |
| |
| <div id="breadcrumbs"> |
| <ul class="breadcrumb"> |
| <li id="publishDate">Last Published: 2024-03-06<span class="divider">|</span> |
| </li> |
| <li id="projectVersion">Version: 2.23.1</li> |
| <li class="pull-right"><span class="divider">|</span> |
| <a href="https://github.com/apache/logging-log4j2" class="externalLink" title="GitHub">GitHub</a></li> |
| <li class="pull-right"><span class="divider">|</span> |
| <a href="../../../" title="Logging Services">Logging Services</a></li> |
| <li class="pull-right"><span class="divider">|</span> |
| <a href="https://www.apache.org/" class="externalLink" title="Apache">Apache</a></li> |
| <li class="pull-right"><a href="https://cwiki.apache.org/confluence/display/LOGGING/Log4j" class="externalLink" title="Logging Wiki">Logging Wiki</a></li> |
| </ul> |
| </div> |
| </header> |
| <div class="row-fluid"> |
| <header id="leftColumn" class="span2"> |
| <nav class="well sidebar-nav"> |
| <ul class="nav nav-list"> |
| <li class="nav-header"><img class="imageLink" src="../img/glyphicons/home.png" alt="Apache Log4j™ 2" style="border: 0;" /> Apache Log4j™ 2</li> |
| <li><a href="../index.html" title="About"><span class="none"></span>About</a></li> |
| <li><a href="../download.html" title="Download"><span class="none"></span>Download</a></li> |
| <li><a href="../support.html" title="Support"><span class="none"></span>Support</a></li> |
| <li><a href="../maven-artifacts.html" title="Maven, Ivy, Gradle Artifacts"><span class="icon-chevron-right"></span>Maven, Ivy, Gradle Artifacts</a></li> |
| <li><a href="../release-notes.html" title="Release Notes"><span class="none"></span>Release Notes</a></li> |
| <li><a href="../faq.html" title="FAQ"><span class="none"></span>FAQ</a></li> |
| <li><a href="../performance.html" title="Performance"><span class="icon-chevron-right"></span>Performance</a></li> |
| <li><a href="../articles.html" title="Articles and Tutorials"><span class="none"></span>Articles and Tutorials</a></li> |
| <li><a href="../security.html" title="Security"><span class="icon-chevron-right"></span>Security</a></li> |
| <li class="nav-header"><img class="imageLink" src="../img/glyphicons/book.png" alt="Manual" style="border: 0;" /> Manual</li> |
| <li><a href="../manual/index.html" title="Introduction"><span class="none"></span>Introduction</a></li> |
| <li><a href="../manual/architecture.html" title="Architecture"><span class="none"></span>Architecture</a></li> |
| <li><a href="../manual/api-separation.html" title="API Separation"><span class="none"></span>API Separation</a></li> |
| <li><a href="../manual/migration.html" title="Log4j 1.x Migration"><span class="icon-chevron-right"></span>Log4j 1.x Migration</a></li> |
| <li><a href="../manual/api.html" title="Java API"><span class="icon-chevron-right"></span>Java API</a></li> |
| <li><a href="../../kotlin" title="Kotlin API"><span class="none"></span>Kotlin API</a></li> |
| <li><a href="../../scala" title="Scala API"><span class="none"></span>Scala API</a></li> |
| <li><a href="../manual/configuration.html" title="Configuration"><span class="icon-chevron-right"></span>Configuration</a></li> |
| <li><a href="../manual/usage.html" title="Usage"><span class="icon-chevron-right"></span>Usage</a></li> |
| <li><a href="../manual/webapp.html" title="Web Applications and JSPs"><span class="icon-chevron-right"></span>Web Applications and JSPs</a></li> |
| <li><a href="../manual/lookups.html" title="Lookups"><span class="icon-chevron-right"></span>Lookups</a></li> |
| <li><a href="../manual/appenders.html" title="Appenders"><span class="icon-chevron-right"></span>Appenders</a></li> |
| <li><a href="../manual/layouts.html" title="Layouts"><span class="icon-chevron-down"></span>Layouts</a> |
| <ul class="nav nav-list"> |
| <li><a href="../manual/layouts.html#CSVLayouts" title="CSV"><span class="none"></span>CSV</a></li> |
| <li><a href="../manual/layouts.html#GELFLayout" title="GELF"><span class="none"></span>GELF</a></li> |
| <li><a href="../manual/layouts.html#HTMLLayout" title="HTML"><span class="none"></span>HTML</a></li> |
| <li><a href="../manual/layouts.html#JSONLayout" title="JSON"><span class="none"></span>JSON</a></li> |
| <li class="active"><a><span class="none"></span>JSON Template</a></li> |
| <li><a href="../manual/layouts.html#PatternLayout" title="Pattern"><span class="none"></span>Pattern</a></li> |
| <li><a href="../manual/layouts.html#RFC5424Layout" title="RFC-5424"><span class="none"></span>RFC-5424</a></li> |
| <li><a href="../manual/layouts.html#SerializedLayout" title="Serialized"><span class="none"></span>Serialized</a></li> |
| <li><a href="../manual/layouts.html#SyslogLayout" title="Syslog"><span class="none"></span>Syslog</a></li> |
| <li><a href="../manual/layouts.html#XMLLayout" title="XML"><span class="none"></span>XML</a></li> |
| <li><a href="../manual/layouts.html#YamlLayout" title="YAML"><span class="none"></span>YAML</a></li> |
| <li><a href="../manual/layouts.html#LocationInformation" title="Location Information"><span class="none"></span>Location Information</a></li> |
| </ul></li> |
| <li><a href="../manual/filters.html" title="Filters"><span class="icon-chevron-right"></span>Filters</a></li> |
| <li><a href="../manual/async.html" title="Async Loggers"><span class="icon-chevron-right"></span>Async Loggers</a></li> |
| <li><a href="../manual/garbagefree.html" title="Garbage-free Logging"><span class="icon-chevron-right"></span>Garbage-free Logging</a></li> |
| <li><a href="../manual/jmx.html" title="JMX"><span class="none"></span>JMX</a></li> |
| <li><a href="../manual/logsep.html" title="Logging Separation"><span class="none"></span>Logging Separation</a></li> |
| <li><a href="../manual/extending.html" title="Extending Log4j"><span class="icon-chevron-right"></span>Extending Log4j</a></li> |
| <li><a href="../manual/plugins.html" title="Plugins"><span class="icon-chevron-right"></span>Plugins</a></li> |
| <li><a href="../manual/customconfig.html" title="Programmatic Log4j Configuration"><span class="icon-chevron-right"></span>Programmatic Log4j Configuration</a></li> |
| <li><a href="../manual/customloglevels.html" title="Custom Log Levels"><span class="icon-chevron-right"></span>Custom Log Levels</a></li> |
| <li class="nav-header"><img class="imageLink" src="../img/glyphicons/pencil.png" alt="For Contributors" style="border: 0;" /> For Contributors</li> |
| <li><a href="../guidelines.html" title="Guidelines"><span class="none"></span>Guidelines</a></li> |
| <li><a href="../javastyle.html" title="Style Guide"><span class="none"></span>Style Guide</a></li> |
| <li class="nav-header"><img class="imageLink" src="../img/glyphicons/cog.png" alt="Components" style="border: 0;" /> Components</li> |
| <li><a href="../log4j-api.html" title="API"><span class="none"></span>API</a></li> |
| <li><a href="../log4j-jcl.html" title="Commons Logging Bridge"><span class="none"></span>Commons Logging Bridge</a></li> |
| <li><a href="../log4j-1.2-api.html" title="Log4j 1.2 API"><span class="none"></span>Log4j 1.2 API</a></li> |
| <li><a href="../log4j-slf4j-impl.html" title="SLF4J Binding"><span class="none"></span>SLF4J Binding</a></li> |
| <li><a href="../log4j-jul.html" title="JUL Adapter"><span class="none"></span>JUL Adapter</a></li> |
| <li><a href="../log4j-jpl.html" title="JDK Platform Logger"><span class="none"></span>JDK Platform Logger</a></li> |
| <li><a href="../log4j-to-slf4j.html" title="Log4j 2 to SLF4J Adapter"><span class="none"></span>Log4j 2 to SLF4J Adapter</a></li> |
| <li><a href="../log4j-flume-ng.html" title="Apache Flume Appender"><span class="none"></span>Apache Flume Appender</a></li> |
| <li><a href="../log4j-taglib.html" title="Log4j Tag Library"><span class="none"></span>Log4j Tag Library</a></li> |
| <li><a href="../log4j-jmx-gui.html" title="Log4j JMX GUI"><span class="none"></span>Log4j JMX GUI</a></li> |
| <li><a href="../log4j-web.html" title="Log4j Web Application Support"><span class="none"></span>Log4j Web Application Support</a></li> |
| <li><a href="../log4j-jakarta-web.html" title="Log4j Jakarta Web Application Support"><span class="none"></span>Log4j Jakarta Web Application Support</a></li> |
| <li><a href="../log4j-appserver.html" title="Log4j Application Server Integration"><span class="none"></span>Log4j Application Server Integration</a></li> |
| <li><a href="../log4j-couchdb.html" title="Log4j CouchDB appender"><span class="none"></span>Log4j CouchDB appender</a></li> |
| <li><a href="../log4j-mongodb3.html" title="Log4j MongoDB3 appender"><span class="none"></span>Log4j MongoDB3 appender</a></li> |
| <li><a href="../log4j-mongodb4.html" title="Log4j MongoDB4 appender"><span class="none"></span>Log4j MongoDB4 appender</a></li> |
| <li><a href="../log4j-cassandra.html" title="Log4j Cassandra appender"><span class="none"></span>Log4j Cassandra appender</a></li> |
| <li><a href="../log4j-iostreams.html" title="Log4j IO Streams"><span class="none"></span>Log4j IO Streams</a></li> |
| <li><a href="../log4j-docker.html" title="Log4j Docker Support"><span class="none"></span>Log4j Docker Support</a></li> |
| <li><a href="../log4j-kubernetes.html" title="Log4j Kubernetes Support"><span class="none"></span>Log4j Kubernetes Support</a></li> |
| <li><a href="../log4j-spring-boot.html" title="Log4j Spring Boot"><span class="none"></span>Log4j Spring Boot</a></li> |
| <li><a href="../log4j-spring-cloud-config-client.html" title="Log4j Spring Cloud Config Client"><span class="none"></span>Log4j Spring Cloud Config Client</a></li> |
| <li class="nav-header"><img class="imageLink" src="../img/glyphicons/tag.png" alt="Related Projects" style="border: 0;" /> Related Projects</li> |
| <li><a href="../../../chainsaw/2.x/index.html" title="Chainsaw"><span class="none"></span>Chainsaw</a></li> |
| <li><a href="../../../log4cxx/latest_stable/index.html" title="Log4Cxx"><span class="none"></span>Log4Cxx</a></li> |
| <li><a href="../../../log4j-audit/latest/index.html" title="Log4j Audit"><span class="none"></span>Log4j Audit</a></li> |
| <li><a href="../../kotlin" title="Log4j Kotlin"><span class="none"></span>Log4j Kotlin</a></li> |
| <li><a href="../../scala" title="Log4j Scala"><span class="none"></span>Log4j Scala</a></li> |
| <li><a href="../../transform" title="Log4j Transform"><span class="none"></span>Log4j Transform</a></li> |
| <li><a href="../../../log4net/index.html" title="Log4Net"><span class="none"></span>Log4Net</a></li> |
| <li class="nav-header"><img class="imageLink" src="../img/glyphicons/link.png" alt="Legacy Sites" style="border: 0;" /> Legacy Sites</li> |
| <li><a href="../../log4j-2.12.4/" title="Log4j 2.12.4 - Java 7"><span class="none"></span>Log4j 2.12.4 - Java 7</a></li> |
| <li><a href="../../log4j-2.3.2/" title="Log4j 2.3.2 - Java 6"><span class="none"></span>Log4j 2.3.2 - Java 6</a></li> |
| <li><a href="../../1.2/" title="Log4j 1.2 - End of Life"><span class="none"></span>Log4j 1.2 - End of Life</a></li> |
| <li class="nav-header"><img class="imageLink" src="../img/glyphicons/info.png" alt="Project Information" style="border: 0;" /> Project Information</li> |
| <li><a href="../team.html" title="Project Team"><span class="none"></span>Project Team</a></li> |
| <li><a href="https://www.apache.org/licenses/LICENSE-2.0" class="externalLink" title="Project License"><span class="none"></span>Project License</a></li> |
| <li><a href="https://github.com/apache/logging-log4j2" class="externalLink" title="Source Repository"><span class="none"></span>Source Repository</a></li> |
| <li><a href="../runtime-dependencies.html" title="Runtime Dependencies"><span class="none"></span>Runtime Dependencies</a></li> |
| <li><a href="../javadoc.html" title="Javadoc"><span class="none"></span>Javadoc</a></li> |
| <li><a href="../thanks.html" title="Thanks"><span class="none"></span>Thanks</a></li> |
| </ul> |
| </nav> |
| <div class="well sidebar-nav"> |
| <div id="poweredBy"> |
| <div class="clear"></div> |
| <div class="clear"></div> |
| <div class="clear"></div> |
| <a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="../images/logos/maven-feather.png" /></a> |
| </div> |
| </div> |
| </header> |
| <main id="bodyColumn" class="span10" > |
| <h1>JSON Template Layout</h1> |
| <div id="preamble"> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p><code>JsonTemplateLayout</code> is a customizable, efficient, and garbage-free JSON |
| generating layout. It encodes <code>LogEvent</code>s according to the structure described |
| by the JSON template provided. In a nutshell, it shines with its</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Customizable JSON structure (see <code>eventTemplate[Uri]</code> and |
| <code>stackTraceElementTemplate[Uri]</code> <a href="#layout-config">layout configuration</a> parameters)</p> |
| </li> |
| <li> |
| <p>Customizable timestamp formatting (see <a href="#event-template-resolver-timestamp"><code>timestamp</code></a> |
| event template resolver)</p> |
| </li> |
| <li> |
| <p>Feature rich exception formatting (see <a href="#event-template-resolver-exception"><code>exception</code></a> |
| and <a href="#event-template-resolver-exceptionRootCause"><code>exceptionRootCause</code></a> event template resolvers)</p> |
| </li> |
| <li> |
| <p><a href="#extending">Extensible plugin support</a></p> |
| </li> |
| <li> |
| <p>Customizable object <a href="#recycling-strategy">recycling strategy</a></p> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="usage">Usage</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Adding <code>log4j-layout-template-json</code> artifact to your list of dependencies is |
| enough to enable access to <code>JsonTemplateLayout</code> in your Log4j configuration:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-xml" data-lang="xml"><dependency> |
| <groupId>org.apache.logging.log4j</groupId> |
| <artifactId>log4j-layout-template-json</artifactId> |
| <version>2.23.1</version> |
| </dependency></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>For instance, given the following JSON template modelling |
| <a href="https://www.elastic.co/guide/en/ecs/current/ecs-reference.html">the Elastic Common Schema (ECS) specification</a> |
| (accessible via <code>classpath:EcsLayout.json</code>)</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "@timestamp": { |
| "$resolver": "timestamp", |
| "pattern": { |
| "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", |
| "timeZone": "UTC" |
| } |
| }, |
| "ecs.version": "1.2.0", |
| "log.level": { |
| "$resolver": "level", |
| "field": "name" |
| }, |
| "message": { |
| "$resolver": "message", |
| "stringified": true |
| }, |
| "process.thread.name": { |
| "$resolver": "thread", |
| "field": "name" |
| }, |
| "log.logger": { |
| "$resolver": "logger", |
| "field": "name" |
| }, |
| "labels": { |
| "$resolver": "mdc", |
| "flatten": true, |
| "stringified": true |
| }, |
| "tags": { |
| "$resolver": "ndc" |
| }, |
| "error.type": { |
| "$resolver": "exception", |
| "field": "className" |
| }, |
| "error.message": { |
| "$resolver": "exception", |
| "field": "message" |
| }, |
| "error.stack_trace": { |
| "$resolver": "exception", |
| "field": "stackTrace", |
| "stackTrace": { |
| "stringified": true |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>in combination with the below <code>log4j2.xml</code> configuration:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-xml" data-lang="xml"><JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>or with the below <code>log4j2.properties</code> configuration:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-ini" data-lang="ini">appender.console.layout.type = JsonTemplateLayout |
| appender.console.layout.eventTemplateUri = classpath:EcsLayout.json</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p><code>JsonTemplateLayout</code> generates JSON as follows:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "@timestamp": "2017-05-25T19:56:23.370Z", |
| "ecs.version": "1.2.0", |
| "log.level": "ERROR", |
| "message": "Hello, error!", |
| "process.thread.name": "main", |
| "log.logger": "org.apache.logging.log4j.JsonTemplateLayoutDemo", |
| "error.type": "java.lang.RuntimeException", |
| "error.message": "test", |
| "error.stack_trace": "java.lang.RuntimeException: test\n\tat org.apache.logging.log4j.JsonTemplateLayoutDemo.main(JsonTemplateLayoutDemo.java:11)\n" |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="layout-config">Layout Configuration</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p><code>JsonTemplateLayout</code> is configured with the following parameters:</p> |
| </div> |
| <table class="tableblock frame-all grid-all stretch"> |
| <caption class="title">Table 1. <code>JsonTemplateLayout</code> parameters</caption> |
| <colgroup> |
| <col style="width: 16.6666%;"/> |
| <col style="width: 16.6666%;"/> |
| <col style="width: 66.6668%;"/> |
| </colgroup> |
| <tbody> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Parameter Name</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Type</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>charset</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Charset</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Charset</code> used for <code>String</code> encoding</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>locationInfoEnabled</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>boolean</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">toggles access to the <code>LogEvent</code> source; file name, line number, etc. |
| (defaults to <code>false</code> set by <code>log4j.layout.jsonTemplate.locationInfoEnabled</code> |
| property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>stackTraceEnabled</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>boolean</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">toggles access to the stack traces (defaults to <code>true</code> set by |
| <code>log4j.layout.jsonTemplate.stackTraceEnabled</code> property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>eventTemplate</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>String</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">inline JSON template for rendering <code>LogEvent</code>s (has priority over |
| <code>eventTemplateUri</code>, defaults to <code>null</code> set by |
| <code>log4j.layout.jsonTemplate.eventTemplate</code> property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>eventTemplateUri</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>String</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">URI pointing to the JSON template for rendering <code>LogEvent</code>s (defaults to |
| <code>classpath:EcsLayout.json</code> set by <code>log4j.layout.jsonTemplate.eventTemplateUri</code> |
| property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>eventTemplateRootObjectKey</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>String</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">if present, the event template is put into a JSON object composed of a single |
| member with the provided key (defaults to <code>null</code> set by |
| <code>log4j.layout.jsonTemplate.eventTemplateRootObjectKey</code> |
| property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>eventTemplateAdditionalField</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>EventTemplateAdditionalField[]</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">additional key-value pairs appended to the root of the event template</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>stackTraceElementTemplate</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>String</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">inline JSON template for rendering <code>StackTraceElement</code>s (has priority over |
| <code>stackTraceElementTemplateUri</code>, defaults to <code>null</code> set by |
| <code>log4j.layout.jsonTemplate.stackTraceElementTemplate</code> property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>stackTraceElementTemplateUri</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>String</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">URI pointing to the JSON template for rendering <code>StackTraceElement</code>s |
| (defaults to <code>classpath:StackTraceElementLayout.json</code> set by |
| <code>log4j.layout.jsonTemplate.stackTraceElementTemplateUri</code> property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>eventDelimiter</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>String</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">delimiter used for separating rendered <code>LogEvent</code>s (defaults to |
| <code>System.lineSeparator()</code> set by <code>log4j.layout.jsonTemplate.eventDelimiter</code> |
| property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>nullEventDelimiterEnabled</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>boolean</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">append <code>\0</code> (<code>null</code>) character to the end of every <code>eventDelimiter</code> |
| separating rendered <code>LogEvent</code>s (defaults to <code>false</code> set by |
| <code>log4j.layout.jsonTemplate.nullEventDelimiterEnabled</code> property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>maxStringLength</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>int</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">truncate string values longer than the specified limit (defaults to 16384 set |
| by <code>log4j.layout.jsonTemplate.maxStringLength</code> property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>truncatedStringSuffix</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>String</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">suffix to append to strings truncated due to exceeding <code>maxStringLength</code> |
| (defaults to <code>…</code> set by <code>log4j.layout.jsonTemplate.truncatedStringSuffix</code> |
| property)</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>recyclerFactory</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>RecyclerFactory</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">recycling strategy that can either be <code>dummy</code>, <code>threadLocal</code>, or <code>queue</code> |
| (set by <code>log4j.layout.jsonTemplate.recyclerFactory</code> property)</p></td> |
| </tr> |
| </tbody> |
| </table> |
| <div class="sect2"> |
| <h3 id="additional-event-template-fields">Additional event template fields</h3> |
| <div class="paragraph"> |
| <p>Additional event template fields are a convenient short-cut to add custom fields |
| to a template or override the existing ones. Following configuration overrides |
| the <code>host</code> field of the <code>GelfLayout.json</code> template and adds two new custom |
| fields:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">XML configuration with additional fields</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-xml" data-lang="xml"><JsonTemplateLayout eventTemplateUri="classpath:GelfLayout.json"> |
| <EventTemplateAdditionalField key="host" value="www.apache.org"/> |
| <EventTemplateAdditionalField key="_serviceName" value="auth-service"/> |
| <EventTemplateAdditionalField key="_containerId" value="6ede3f0ca7d9"/> |
| </JsonTemplateLayout></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The default <code>format</code> for the added new fields are <code>String</code>. |
| One can also provide JSON-formatted additional fields:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">XML-formatted configuration with JSON-formatted additional fields</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-xml" data-lang="xml"><JsonTemplateLayout eventTemplateUri="classpath:GelfLayout.json"> |
| <EventTemplateAdditionalField |
| key="marker" |
| format="JSON" |
| value='{"$resolver": "marker", "field": "name"}'/> |
| <EventTemplateAdditionalField |
| key="aNumber" |
| format="JSON" |
| value="1"/> |
| <EventTemplateAdditionalField |
| key="aList" |
| format="JSON" |
| value='[1, 2, "three"]'/> |
| </JsonTemplateLayout></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Additional event template fields can very well be introduced using properties-, |
| YAML-, and JSON-formatted configurations:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">Properties-formatted configuration with JSON-formatted additional fields</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-properties" data-lang="properties">appender.console.layout.type = JsonTemplateLayout |
| appender.console.layout.eventTemplateUri = classpath:GelfLayout.json |
| appender.console.layout.eventTemplateAdditionalField[0].type = EventTemplateAdditionalField |
| appender.console.layout.eventTemplateAdditionalField[0].key = marker |
| appender.console.layout.eventTemplateAdditionalField[0].value = {"$resolver": "marker", "field": "name"} |
| appender.console.layout.eventTemplateAdditionalField[0].format = JSON |
| appender.console.layout.eventTemplateAdditionalField[1].type = EventTemplateAdditionalField |
| appender.console.layout.eventTemplateAdditionalField[1].key = aNumber |
| appender.console.layout.eventTemplateAdditionalField[1].value = 1 |
| appender.console.layout.eventTemplateAdditionalField[1].format = JSON |
| appender.console.layout.eventTemplateAdditionalField[2].type = EventTemplateAdditionalField |
| appender.console.layout.eventTemplateAdditionalField[2].key = aList |
| appender.console.layout.eventTemplateAdditionalField[2].value = [1, 2, "three"] |
| appender.console.layout.eventTemplateAdditionalField[2].format = JSON</code></pre> |
| </div> |
| </div> |
| <div class="listingblock"> |
| <div class="title">YAML-formatted configuration with JSON-formatted additional fields</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-yaml" data-lang="yaml">JsonTemplateLayout: |
| eventTemplateAdditionalField: |
| - key: "marker" |
| value: '{"$resolver": "marker", "field": "name"}' |
| format: "JSON" |
| - key: "aNumber" |
| value: "1" |
| format: "JSON" |
| - key: "aList" |
| value: '[1, 2, "three"]' |
| format: "JSON"</code></pre> |
| </div> |
| </div> |
| <div class="listingblock"> |
| <div class="title">JSON-formatted configuration with JSON-formatted additional fields</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "JsonTemplateLayout": { |
| "eventTemplateAdditionalField": [ |
| { |
| "key": "marker", |
| "value": "{\"$resolver\": \"marker\", \"field\": \"name\"}", |
| "format": "JSON" |
| }, |
| { |
| "key": "aNumber", |
| "value": "1", |
| "format": "JSON" |
| }, |
| { |
| "key": "aList", |
| "value": "[1, 2, \"three\"]", |
| "format": "JSON" |
| } |
| ] |
| } |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="recycling-strategy">Recycling strategy</h3> |
| <div class="paragraph"> |
| <p><code>RecyclerFactory</code> plays a crucial role for determining the memory footprint of |
| the layout. Template resolvers employ it to create recyclers for objects that |
| they can reuse. The behavior of each <code>RecyclerFactory</code> and when one should |
| prefer one over another is explained below:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>dummy</code> performs no recycling, hence each recycling attempt will result in a |
| new instance. This will obviously create a load on the garbage-collector. It |
| is a good choice for applications with low and medium log rate.</p> |
| </li> |
| <li> |
| <p><code>threadLocal</code> performs the best, since every instance is stored in |
| <code>ThreadLocal</code>s and accessed without any synchronization cost. Though this |
| might not be a desirable option for applications running with hundreds of |
| threads or more, e.g., a web servlet.</p> |
| </li> |
| <li> |
| <p><code>queue</code> is the best of both worlds. It allows recycling of objects up to a |
| certain number (<code>capacity</code>). When this limit is exceeded due to excessive |
| concurrent load (e.g., <code>capacity</code> is 50 but there are 51 threads concurrently |
| trying to log), it starts allocating. <code>queue</code> is a good strategy where |
| <code>threadLocal</code> is not desirable.</p> |
| <div class="paragraph"> |
| <p><code>queue</code> also accepts optional <code>supplier</code> (of type <code>java.util.Queue</code>, defaults to |
| <code>org.jctools.queues.MpmcArrayQueue.new</code> if JCTools is in the classpath; |
| otherwise <code>java.util.concurrent.ArrayBlockingQueue.new</code>) and <code>capacity</code> (of |
| type <code>int</code>, defaults to <code>max(8,2*cpuCount+1)</code>) parameters:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">Example configurations of <code>queue</code> recycling strategy</div> |
| <div class="content"> |
| <pre class="highlight"><code>queue:supplier=org.jctools.queues.MpmcArrayQueue.new |
| queue:capacity=10 |
| queue:supplier=java.util.concurrent.ArrayBlockingQueue.new,capacity=50</code></pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>The default <code>RecyclerFactory</code> is <code>threadLocal</code>, if |
| <code>log4j2.enable.threadlocals=true</code>; otherwise, <code>queue</code>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>See <a href="#extending-recycler">Extending Recycler Factories</a> for details on how to introduce custom |
| <code>RecyclerFactory</code> implementations.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="template-config">Template Configuration</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Templates are configured by means of the following <code>JsonTemplateLayout</code> |
| parameters:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>eventTemplate[Uri]</code> (for serializing <code>LogEvent</code>s)</p> |
| </li> |
| <li> |
| <p><code>stackTraceElementTemplate[Uri]</code> (for serializing <code>StackStraceElement</code>s)</p> |
| </li> |
| <li> |
| <p><code>eventTemplateAdditionalField</code> (for extending the used event template)</p> |
| </li> |
| </ul> |
| </div> |
| <div class="sect2"> |
| <h3 id="event-templates">Event Templates</h3> |
| <div class="paragraph"> |
| <p><code>eventTemplate[Uri]</code> describes the JSON structure <code>JsonTemplateLayout</code> uses to |
| serialize <code>LogEvent</code>s. The default configuration (accessible by |
| <code>log4j.layout.jsonTemplate.eventTemplate[Uri]</code> property) is set to |
| <code>classpath:EcsLayout.json</code> provided by the <code>log4j-layout-template-json</code> |
| artifact, which contains the following predefined event templates:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><a href="https://github.com/apache/logging-log4j2/tree/main/log4j-layout-template-json/src/main/resources/EcsLayout.json"><code>EcsLayout.json</code></a> |
| described by <a href="https://www.elastic.co/guide/en/ecs/current/ecs-reference.html">the Elastic Common Schema (ECS) specification</a></p> |
| </li> |
| <li> |
| <p><a href="https://github.com/apache/logging-log4j2/tree/main/log4j-layout-template-json/src/main/resources/LogstashJsonEventLayoutV1.json"><code>LogstashJsonEventLayoutV1.json</code></a> |
| described in <a href="https://github.com/logstash/log4j-jsonevent-layout">Logstash |
| <code>json_event</code> pattern for log4j</a></p> |
| </li> |
| <li> |
| <p><a href="https://github.com/apache/logging-log4j2/tree/main/log4j-layout-template-json/src/main/resources/GelfLayout.json"><code>GelfLayout.json</code></a> |
| described by <a href="https://docs.graylog.org/en/3.1/pages/gelf.html#gelf-payload-specification">the |
| Graylog Extended Log Format (GELF) payload specification</a> with additional |
| <code>_thread</code> and <code>_logger</code> fields. (Here it is advised to override the obligatory |
| <code>host</code> field with a user provided constant via |
| <a href="#additional-event-template-fields">additional event template fields</a> |
| to avoid <code>hostName</code> property lookup at runtime, which incurs an extra cost.)</p> |
| </li> |
| <li> |
| <p><a href="https://github.com/apache/logging-log4j2/tree/main/log4j-layout-template-json/src/main/resources/GcpLayout.json"><code>GcpLayout.json</code></a> |
| described by <a href="https://cloud.google.com/logging/docs/structured-logging">Google |
| Cloud Platform structured logging</a> with additional |
| <code>_thread</code>, <code>_logger</code> and <code>_exception</code> fields. The exception trace, if any, |
| is written to the <code>_exception</code> field as well as the <code>message</code> field – |
| the former is useful for explicitly searching/analyzing structured exception |
| information, while the latter is Google’s expected place for the exception, |
| and integrates with <a href="https://cloud.google.com/error-reporting">Google Error Reporting</a>.</p> |
| </li> |
| <li> |
| <p><a href="https://github.com/apache/logging-log4j2/tree/main/log4j-layout-template-json/src/main/resources/JsonLayout.json"><code>JsonLayout.json</code></a> |
| providing the exact JSON structure generated by <a href="layouts.html#JSONLayout"><code>JsonLayout</code></a> |
| with the exception of <code>thrown</code> field. (<code>JsonLayout</code> serializes the <code>Throwable</code> |
| as is via Jackson <code>ObjectMapper</code>, whereas <code>JsonLayout.json</code> template of |
| <code>JsonTemplateLayout</code> employs the <code>StackTraceElementLayout.json</code> template |
| for stack traces to generate a document-store-friendly flat structure.)</p> |
| </li> |
| </ul> |
| </div> |
| <div class="sect3"> |
| <h4 id="event-template-resolvers">Event Template Resolvers</h4> |
| <div class="paragraph"> |
| <p>Event template resolvers consume a <code>LogEvent</code> and render a certain property of |
| it at the point of the JSON where they are declared. For instance, <code>marker</code> |
| resolver renders the marker of the event, <code>level</code> resolver renders the level, |
| and so on. An event template resolver is denoted with a special object |
| containing a`$resolver` key:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">Example event template demonstrating the usage of <code>level</code> resolver</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "version": "1.0", |
| "level": { |
| "$resolver": "level", |
| "field": "name" |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Here <code>version</code> field will be rendered as is, while <code>level</code> field will be |
| populated by the <code>level</code> resolver. That is, this template will generate JSON |
| similar to the following:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">Example JSON generated from the demonstrated event template</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "version": "1.0", |
| "level": "INFO" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The complete list of available event template resolvers are provided below in |
| detail.</p> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-counter"><code>counter</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = [ start ] , [ overflowing ] , [ stringified ] |
| start = "start" -> number |
| overflowing = "overflowing" -> boolean |
| stringified = "stringified" -> boolean</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves a number from an internal counter.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Unless provided, <code>start</code> and <code>overflowing</code> are respectively set to zero and |
| <code>true</code> by default.</p> |
| </div> |
| <div class="paragraph"> |
| <p>When <code>stringified</code> is enabled, which is set to `false by default, the resolved |
| number will be converted to a string.</p> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <div class="title">Warning</div> |
| </td> |
| <td class="content"> |
| <div class="paragraph"> |
| <p>When <code>overflowing</code> is set to <code>true</code>, the internal counter is created using a |
| <code>long</code>, which is subject to overflow while incrementing, though garbage-free. |
| Otherwise, a <code>BigInteger</code> is used, which does not overflow, but incurs |
| allocation costs.</p> |
| </div> |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolves a sequence of numbers starting from 0. Once <code>Long.MAX_VALUE</code> is |
| reached, counter overflows to <code>Long.MIN_VALUE</code>.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "counter" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves a sequence of numbers starting from 1000. Once <code>Long.MAX_VALUE</code> is |
| reached, counter overflows to <code>Long.MIN_VALUE</code>.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "counter", |
| "start": 1000 |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves a sequence of numbers starting from 0 and keeps on doing as long as |
| JVM heap allows.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "counter", |
| "overflowing": false |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-caseConverter"><code>caseConverter</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = case , input , [ locale ] , [ errorHandlingStrategy ] |
| input = JSON |
| case = "case" -> ( "upper" | "lower" ) |
| locale = "locale" -> ( |
| language | |
| ( language , "_" , country ) | |
| ( language , "_" , country , "_" , variant ) |
| ) |
| errorHandlingStrategy = "errorHandlingStrategy" -> ( |
| "fail" | |
| "pass" | |
| "replace" |
| ) |
| replacement = "replacement" -> JSON</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Converts the case of string values.</p> |
| </div> |
| <div class="paragraph"> |
| <p><code>input</code> can be any available template value; e.g., a JSON literal, a lookup |
| string, an object pointing to another resolver.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Unless provided, <code>locale</code> points to the one returned by |
| <code>JsonTemplateLayoutDefaults.getLocale()</code>, which is configured by |
| <code>log4j.layout.jsonTemplate.locale</code> system property and by default set to the |
| default system locale.</p> |
| </div> |
| <div class="paragraph"> |
| <p><code>errorHandlingStrategy</code> determines the behavior when either the input doesn’t |
| resolve to a string value or case conversion throws an exception:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>fail</code> propagates the failure</p> |
| </li> |
| <li> |
| <p><code>pass</code> causes the resolved value to be passed as is</p> |
| </li> |
| <li> |
| <p><code>replace</code> suppresses the failure and replaces it with the <code>replacement</code>, |
| which is set to <code>null</code> by default</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p><code>errorHandlingStrategy</code> is set to <code>replace</code> by default.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Most of the time JSON logs are persisted to a storage solution (e.g., |
| Elasticsearch) that keeps a statically-typed index on fields. Hence, if a field |
| is always expected to be of type string, using non-string <code>replacement</code>s or |
| <code>pass</code> in <code>errorHandlingStrategy</code> might result in type incompatibility issues at |
| the storage level.</p> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <div class="title">Warning</div> |
| </td> |
| <td class="content"> |
| <div class="paragraph"> |
| <p>Unless the input value is <code>pass</code>ed intact or <code>replace</code>d, case conversion is |
| not garbage-free.</p> |
| </div> |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_2">Examples</h6> |
| <div class="paragraph"> |
| <p>Convert the resolved log level strings to upper-case:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "caseConverter", |
| "case": "upper", |
| "input": { |
| "$resolver": "level", |
| "field": "name" |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Convert the resolved <code>USER</code> environment variable to lower-case using <code>nl_NL</code> |
| locale:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "caseConverter", |
| "case": "lower", |
| "locale": "nl_NL", |
| "input": "${env:USER}" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Convert the resolved <code>sessionId</code> thread context data (MDC) to lower-case:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "caseConverter", |
| "case": "lower", |
| "input": { |
| "$resolver": "mdc", |
| "key": "sessionId" |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Above, if <code>sessionId</code> MDC resolves to a, say, number, case conversion will fail. |
| Since <code>errorHandlingStrategy</code> is set to <code>replace</code> and replacement is set to |
| <code>null</code> by default, the resolved value will be <code>null</code>. One can suppress this |
| behavior and let the resolved <code>sessionId</code> number be left as is:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "caseConverter", |
| "case": "lower", |
| "input": { |
| "$resolver": "mdc", |
| "key": "sessionId" |
| }, |
| "errorHandlingStrategy": "pass" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>or replace it with a custom string:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "caseConverter", |
| "case": "lower", |
| "input": { |
| "$resolver": "mdc", |
| "key": "sessionId" |
| }, |
| "errorHandlingStrategy": "replace", |
| "replacement": "unknown" |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-endOfBatch"><code>endOfBatch</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "endOfBatch" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves <code>logEvent.isEndOfBatch()</code> boolean flag.</p> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-exception"><code>exception</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = field , [ stringified ] , [ stackTrace ] |
| field = "field" -> ( "className" | "message" | "stackTrace" ) |
| |
| stackTrace = "stackTrace" -> ( |
| [ stringified ] |
| , [ elementTemplate ] |
| ) |
| |
| stringified = "stringified" -> ( boolean | truncation ) |
| truncation = "truncation" -> ( |
| [ suffix ] |
| , [ pointMatcherStrings ] |
| , [ pointMatcherRegexes ] |
| ) |
| suffix = "suffix" -> string |
| pointMatcherStrings = "pointMatcherStrings" -> string[] |
| pointMatcherRegexes = "pointMatcherRegexes" -> string[] |
| |
| elementTemplate = "elementTemplate" -> object</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves fields of the <code>Throwable</code> returned by <code>logEvent.getThrown()</code>.</p> |
| </div> |
| <div class="paragraph"> |
| <p><code>stringified</code> is set to <code>false</code> by default. <code>stringified</code> at the root level is |
| <strong>deprecated</strong> in favor of <code>stackTrace.stringified</code>, which has precedence if both |
| are provided.</p> |
| </div> |
| <div class="paragraph"> |
| <p><code>pointMatcherStrings</code> and <code>pointMatcherRegexes</code> enable the truncation of |
| stringified stack traces after the given matching point. If both parameters are |
| provided, <code>pointMatcherStrings</code> will be checked first.</p> |
| </div> |
| <div class="paragraph"> |
| <p>If a stringified stack trace truncation takes place, it will be indicated with a |
| <code>suffix</code>, which by default is set to the configured <code>truncatedStringSuffix</code> in |
| the layout, unless explicitly provided. Every truncation suffix is prefixed with |
| a newline.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Stringified stack trace truncation operates in <code>Caused by:</code> and <code>Suppressed:</code> |
| label blocks. That is, matchers are executed against each label in isolation.</p> |
| </div> |
| <div class="paragraph"> |
| <p><code>elementTemplate</code> is an object describing the template to be used while |
| resolving the <code>StackTraceElement</code> array. If <code>stringified</code> is set to <code>true</code>, |
| <code>elementTemplate</code> will be discarded. By default, <code>elementTemplate</code> is set to |
| <code>null</code> and rather populated from the layout configuration. That is, the stack |
| trace element template can also be provided using |
| <code>stackTraceElementTemplate[Uri]</code> layout configuration parameters. The template |
| to be employed is determined in the following order:</p> |
| </div> |
| <div class="olist arabic"> |
| <ol class="arabic"> |
| <li> |
| <p><code>elementTemplate</code> provided in the resolver configuration</p> |
| </li> |
| <li> |
| <p><code>stackTraceElementTemplate</code> parameter from layout configuration |
| (the default is populated from <code>log4j.layout.jsonTemplate.stackTraceElementTemplate</code> |
| system property)</p> |
| </li> |
| <li> |
| <p><code>stackTraceElementTemplateUri</code> parameter from layout configuration |
| (the default is populated from <code>log4j.layout.jsonTemplate.stackTraceElementTemplateUri</code> |
| system property)</p> |
| </li> |
| </ol> |
| </div> |
| <div class="paragraph"> |
| <p>See <a href="#stack-trace-element-templates">Stack Trace Element Templates</a> |
| for the list of available resolvers in a stack trace element template.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Note that this resolver is toggled by |
| <code>log4j.layout.jsonTemplate.stackTraceEnabled</code> property.</p> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <div class="title">Warning</div> |
| </td> |
| <td class="content"> |
| <div class="paragraph"> |
| <p>Since <code>Throwable#getStackTrace()</code> clones the original <code>StackTraceElement[]</code>, |
| access to (and hence rendering of) stack traces are not garbage-free.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Each <code>pointMatcherRegexes</code> item triggers a <code>Pattern#matcher()</code> call, which is |
| not garbage-free either.</p> |
| </div> |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_3">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve <code>logEvent.getThrown().getClass().getCanonicalName()</code>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "exception", |
| "field": "className" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the stack trace into a list of <code>StackTraceElement</code> objects:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "exception", |
| "field": "stackTrace" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the stack trace into a string field:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "exception", |
| "field": "stackTrace", |
| "stackTrace": { |
| "stringified": true |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the stack trace into a string field such that the content will be |
| truncated after the given point matcher:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "exception", |
| "field": "stackTrace", |
| "stackTrace": { |
| "stringified": { |
| "truncation": { |
| "suffix": "... [truncated]", |
| "pointMatcherStrings": ["at javax.servlet.http.HttpServlet.service"] |
| } |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the stack trace into an object described by the provided stack trace |
| element template:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "exception", |
| "field": "stackTrace", |
| "stackTrace": { |
| "elementTemplate": { |
| "class": { |
| "$resolver": "stackTraceElement", |
| "field": "className" |
| }, |
| "method": { |
| "$resolver": "stackTraceElement", |
| "field": "methodName" |
| }, |
| "file": { |
| "$resolver": "stackTraceElement", |
| "field": "fileName" |
| }, |
| "line": { |
| "$resolver": "stackTraceElement", |
| "field": "lineNumber" |
| } |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>See <a href="#stack-trace-element-templates">Stack Trace Element Templates</a> for further details on resolvers available |
| for <code>StackTraceElement</code> templates.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-exceptionRootCause"><code>exceptionRootCause</code></h5> |
| <div class="paragraph"> |
| <p>Resolves the fields of the innermost <code>Throwable</code> returned by |
| <code>logEvent.getThrown()</code>. Its syntax and garbage-footprint are identical to the |
| <a href="#event-template-resolver-exception"><code>exception</code></a> resolver.</p> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-level"><code>level</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = field , [ severity ] |
| field = "field" -> ( "name" | "severity" ) |
| severity = severity-field |
| severity-field = "field" -> ( "keyword" | "code" )</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves the fields of the <code>logEvent.getLevel()</code>.</p> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_4">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve the level name:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "level", |
| "field": "name" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the <a href="https://en.wikipedia.org/wiki/Syslog#Severity_levels">Syslog severity</a> |
| keyword:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "level", |
| "field": "severity", |
| "severity": { |
| "field": "keyword" |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the <a href="https://en.wikipedia.org/wiki/Syslog#Severity_levels">Syslog severity</a> |
| code:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "level", |
| "field": "severity", |
| "severity": { |
| "field": "code" |
| } |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-logger"><code>logger</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = "field" -> ( "name" | "fqcn" )</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves <code>logEvent.getLoggerFqcn()</code> and <code>logEvent.getLoggerName()</code>.</p> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_5">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve the logger name:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "logger", |
| "field": "name" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the logger’s fully qualified class name:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "logger", |
| "field": "fqcn" |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-main"><code>main</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = ( index | key ) |
| index = "index" -> number |
| key = "key" -> string</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Performs <a href="lookups.html#AppMainArgsLookup">Main Argument Lookup</a> for the |
| given <code>index</code> or <code>key</code>.</p> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_6">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve the 1st <code>main()</code> method argument:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "main", |
| "index": 0 |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the argument coming right after <code>--userId</code>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "main", |
| "key": "--userId" |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-map"><code>map</code></h5> |
| <div class="paragraph"> |
| <p>Resolves <code>MapMessage</code>s. See <a href="#map-resolver-template">Map Resolver Template</a> |
| for details.</p> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-marker"><code>marker</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = "field" -> ( "name" | "parents" )</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves <code>logEvent.getMarker()</code>.</p> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_7">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve the marker name:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "marker", |
| "field": "name" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the names of the marker’s parents:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "marker", |
| "field": "parents" |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-mdc"><code>mdc</code></h5> |
| <div class="paragraph"> |
| <p>Resolves Mapped Diagnostic Context (MDC), aka. Thread Context Data. See |
| <a href="#map-resolver-template">Map Resolver Template</a> for details.</p> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <div class="title">Warning</div> |
| </td> |
| <td class="content"> |
| <div class="paragraph"> |
| <p><code>log4j2.garbagefreeThreadContextMap</code> flag needs to be turned on to iterate |
| the map without allocations.</p> |
| </div> |
| </td> |
| </tr> |
| </table> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-message"><code>message</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = [ stringified ] , [ fallbackKey ] |
| stringified = "stringified" -> boolean |
| fallbackKey = "fallbackKey" -> string</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves <code>logEvent.getMessage()</code>.</p> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <div class="title">Warning</div> |
| </td> |
| <td class="content"> |
| <div class="paragraph"> |
| <p>For simple string messages, the resolution is performed without allocations. |
| For <code>ObjectMessage</code>s and <code>MultiformatMessage</code>s, it depends.</p> |
| </div> |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_8">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve the message into a string:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "message", |
| "stringified": true |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the message such that if it is an <code>ObjectMessage</code> or a |
| <code>MultiformatMessage</code> with JSON support, its type (string, list, object, etc.) |
| will be retained:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "message" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Given the above configuration, a <code>SimpleMessage</code> will generate a <code>"sample log |
| message"</code>, whereas a <code>MapMessage</code> will generate a <code>{"action": "login", |
| "sessionId": "87asd97a"}</code>. Certain indexed log storage systems (e.g., |
| <a href="https://www.elastic.co/elasticsearch/">Elasticsearch</a>) will not allow both values |
| to coexist due to type mismatch: one is a <code>string</code> while the other is an <code>object</code>. |
| Here one can use a <code>fallbackKey</code> to work around the problem:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "message", |
| "fallbackKey": "formattedMessage" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Using this configuration, a <code>SimpleMessage</code> will generate a |
| <code>{"formattedMessage": "sample log message"}</code> and a <code>MapMessage</code> will generate a |
| <code>{"action": "login", "sessionId": "87asd97a"}</code>. Note that both emitted JSONs are |
| of type <code>object</code> and have no type-conflicting fields.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-messageParameter"><code>messageParameter</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = [ stringified ] , [ index ] |
| stringified = "stringified" -> boolean |
| index = "index" -> number</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves <code>logEvent.getMessage().getParameters()</code>.</p> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <div class="title">Warning</div> |
| </td> |
| <td class="content"> |
| <div class="paragraph"> |
| <p>Regarding garbage footprint, <code>stringified</code> flag translates to |
| <code>String.valueOf(value)</code>, hence mind not-<code>String</code>-typed values. Further, |
| <code>logEvent.getMessage()</code> is expected to implement <code>ParameterVisitable</code> interface, |
| which is the case if <code>log4j2.enableThreadLocals</code> property set to true.</p> |
| </div> |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_9">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve the message parameters into an array:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "messageParameter" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the string representation of all message parameters into an array:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "messageParameter", |
| "stringified": true |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the first message parameter:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "messageParameter", |
| "index": 0 |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the string representation of the first message parameter:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "messageParameter", |
| "index": 0, |
| "stringified": true |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-ndc"><code>ndc</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = [ pattern ] |
| pattern = "pattern" -> string</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves the Nested Diagnostic Context (NDC), aka. Thread Context Stack, |
| <code>String[]</code> returned by <code>logEvent.getContextStack()</code>.</p> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_10">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve all NDC values into a list:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "ndc" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve all NDC values matching with the <code>pattern</code> regex:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "ndc", |
| "pattern": "user(Role|Rank):\\w+" |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-pattern"><code>pattern</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = pattern , [ stackTraceEnabled ] |
| pattern = "pattern" -> string |
| stackTraceEnabled = "stackTraceEnabled" -> boolean</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolver delegating to <a href="layouts.html#PatternLayout"><code>PatternLayout</code></a>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The default value of <code>stackTraceEnabled</code> is inherited from the parent |
| <code>JsonTemplateLayout</code>.</p> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_11">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve the string produced by <code>%p %c{1.} [%t] %X{userId} %X %m%ex</code> pattern:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "pattern", |
| "pattern": "%p %c{1.} [%t] %X{userId} %X %m%ex" |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-source"><code>source</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = "field" -> ( |
| "className" | |
| "fileName" | |
| "methodName" | |
| "lineNumber" )</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves the fields of the <code>StackTraceElement</code> returned by |
| <code>logEvent.getSource()</code>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Note that this resolver is toggled by |
| <code>log4j.layout.jsonTemplate.locationInfoEnabled</code> property.</p> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_12">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve the line number:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "source", |
| "field": "lineNumber" |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-thread"><code>thread</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = "field" -> ( "name" | "id" | "priority" )</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves <code>logEvent.getThreadId()</code>, <code>logEvent.getThreadName()</code>, |
| <code>logEvent.getThreadPriority()</code>.</p> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_13">Examples</h6> |
| <div class="paragraph"> |
| <p>Resolve the thread name:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "thread", |
| "field": "name" |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect4"> |
| <h5 id="event-template-resolver-timestamp"><code>timestamp</code></h5> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = [ patternConfig | epochConfig ] |
| |
| patternConfig = "pattern" -> ( [ format ] , [ timeZone ] , [ locale ] ) |
| format = "format" -> string |
| timeZone = "timeZone" -> string |
| locale = "locale" -> ( |
| language | |
| ( language , "_" , country ) | |
| ( language , "_" , country , "_" , variant ) |
| ) |
| |
| epochConfig = "epoch" -> ( unit , [ rounded ] ) |
| unit = "unit" -> ( |
| "nanos" | |
| "millis" | |
| "secs" | |
| "millis.nanos" | |
| "secs.nanos" | |
| ) |
| rounded = "rounded" -> boolean</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolves <code>logEvent.getInstant()</code> in various forms.</p> |
| </div> |
| <div class="sect5"> |
| <h6 id="examples_14">Examples</h6> |
| <table class="tableblock frame-all grid-all stretch"> |
| <caption class="title">Table 2. <code>timestamp</code> template resolver examples</caption> |
| <colgroup> |
| <col style="width: 71.4285%;"/> |
| <col style="width: 28.5715%;"/> |
| </colgroup> |
| <tbody> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Configuration</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>Output</code></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "timestamp" |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>2020-02-07T13:38:47.098+02:00</code></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "timestamp", |
| "pattern": { |
| "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", |
| "timeZone": "UTC", |
| "locale": "en_US" |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>2020-02-07T13:38:47.098Z</code></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "timestamp", |
| "epoch": { |
| "unit": "secs" |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>1581082727.982123456</code></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "timestamp", |
| "epoch": { |
| "unit": "secs", |
| "rounded": true |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>1581082727</code></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "timestamp", |
| "epoch": { |
| "unit": "secs.nanos" |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>982123456</code></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "timestamp", |
| "epoch": { |
| "unit": "millis" |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>1581082727982.123456</code></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "timestamp", |
| "epoch": { |
| "unit": "millis", |
| "rounded": true |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>1581082727982</code></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "timestamp", |
| "epoch": { |
| "unit": "millis.nanos" |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>123456</code></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "timestamp", |
| "epoch": { |
| "unit": "nanos" |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>1581082727982123456</code></p></td> |
| </tr> |
| </tbody> |
| </table> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="map-resolver-template">Map Resolver Template</h4> |
| <div class="paragraph"> |
| <p><code>ReadOnlyStringMap</code> is Log4j’s <code>Map<String, Object></code> equivalent with |
| garbage-free accessors and heavily employed throughout the code base. It is the |
| data structure backing both Mapped Diagnostic Context (MDC), aka. Thread Context |
| Data and <code>MapMessage</code> implementations. Hence template resolvers for both of |
| these are provided by a single backend: <code>ReadOnlyStringMapResolver</code>. Put another |
| way, both <code>mdc</code> and <code>map</code> resolvers support identical configuration, behaviour, |
| and garbage footprint, which are detailed below.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = singleAccess | multiAccess |
| |
| singleAccess = key , [ stringified ] |
| key = "key" -> string |
| stringified = "stringified" -> boolean |
| |
| multiAccess = [ pattern ] , [ replacement ] , [ flatten ] , [ stringified ] |
| pattern = "pattern" -> string |
| replacement = "replacement" -> string |
| flatten = "flatten" -> ( boolean | flattenConfig ) |
| flattenConfig = [ flattenPrefix ] |
| flattenPrefix = "prefix" -> string</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p><code>singleAccess</code> resolves a single field, whilst <code>multiAccess</code> resolves a |
| multitude of fields. If <code>flatten</code> is provided, <code>multiAccess</code> merges the fields |
| with the parent, otherwise creates a new JSON object containing the values.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Enabling <code>stringified</code> flag converts each value to its string representation.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Regex provided in the <code>pattern</code> is used to match against the keys. If provided, |
| <code>replacement</code> will be used to replace the matched keys. These two are |
| effectively equivalent to <code>Pattern.compile(pattern).matcher(key).matches()</code> and |
| <code>Pattern.compile(pattern).matcher(key).replaceAll(replacement)</code> calls.</p> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <div class="title">Warning</div> |
| </td> |
| <td class="content"> |
| <div class="paragraph"> |
| <p>Regarding garbage footprint, <code>stringified</code> flag translates to |
| <code>String.valueOf(value)</code>, hence mind not-<code>String</code>-typed values.</p> |
| </div> |
| <div class="paragraph"> |
| <p><code>pattern</code> and <code>replacement</code> incur pattern matcher allocation costs.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Writing certain non-primitive values (e.g., <code>BigDecimal</code>, <code>Set</code>, etc.) to JSON |
| generates garbage, though most (e.g., <code>int</code>, <code>long</code>, <code>String</code>, <code>List</code>, |
| <code>boolean[]</code>, etc.) don’t.</p> |
| </div> |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p><code>"$resolver"</code> is left out in the following examples, since it is to be |
| defined by the actual resolver, e.g., <code>map</code>, <code>mdc</code>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the value of the field keyed with <code>user:role</code>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "…", |
| "key": "user:role" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve the string representation of the <code>user:rank</code> field value:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "…", |
| "key": "user:rank", |
| "stringified": true |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve all fields into an object:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "…" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve all fields into an object such that values are converted to string:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "…", |
| "stringified": true |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve all fields whose keys match with the <code>user:(role|rank)</code> regex into an |
| object:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "…", |
| "pattern": "user:(role|rank)" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Resolve all fields whose keys match with the <code>user:(role|rank)</code> regex into an |
| object after removing the <code>user:</code> prefix in the key:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "…", |
| "pattern": "user:(role|rank)", |
| "replacement": "$1" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Merge all fields whose keys are matching with the <code>user:(role|rank)</code> regex into |
| the parent:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "…", |
| "flatten": true, |
| "pattern": "user:(role|rank)" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>After converting the corresponding field values to string, merge all fields to |
| parent such that keys are prefixed with <code>_</code>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "$resolver": "…", |
| "stringified": true, |
| "flatten": { |
| "prefix": "_" |
| } |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="stack-trace-element-templates">Stack Trace Element Templates</h3> |
| <div class="paragraph"> |
| <p><a href="#event-template-resolver-exception"><code>exception</code></a> and |
| <a href="#event-template-resolver-exceptionRootCause"><code>exceptionRootCause</code></a> event template resolvers can |
| serialize an exception stack trace (i.e., <code>StackTraceElement[]</code> returned by |
| <code>Throwable#getStackTrace()</code>) into a JSON array. While doing so, JSON templating |
| infrastructure is used again.</p> |
| </div> |
| <div class="paragraph"> |
| <p><code>stackTraceElement[Uri]</code> describes the JSON structure <code>JsonTemplateLayout</code> uses |
| to format <code>StackTraceElement</code>s. The default configuration (accessible by |
| <code>log4j.layout.jsonTemplate.stackTraceElementTemplate[Uri]</code> property) is set to |
| <code>classpath:StackTraceElementLayout.json</code> provided by the |
| <code>log4j-layout-template-json</code> artifact:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-json" data-lang="json">{ |
| "class": { |
| "$resolver": "stackTraceElement", |
| "field": "className" |
| }, |
| "method": { |
| "$resolver": "stackTraceElement", |
| "field": "methodName" |
| }, |
| "file": { |
| "$resolver": "stackTraceElement", |
| "field": "fileName" |
| }, |
| "line": { |
| "$resolver": "stackTraceElement", |
| "field": "lineNumber" |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The allowed template configuration syntax is as follows:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code>config = "field" -> ( |
| "className" | |
| "fileName" | |
| "methodName" | |
| "lineNumber" )</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>All above accesses to <code>StackTraceElement</code> is garbage-free.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="extending">Extending</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p><code>JsonTemplateLayout</code> relies on Log4j <a href="plugins.html">plugin system</a> to build |
| up the features it provides. This enables feature customization a breeze for |
| users. As of this moment, following features are implemented by means of |
| plugins:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Event template resolvers (e.g., <code>exception</code>, <code>message</code>, <code>level</code> event template resolvers)</p> |
| </li> |
| <li> |
| <p>Event template interceptors (e.g., injection of <code>eventTemplateAdditionalField</code>)</p> |
| </li> |
| <li> |
| <p>Recycler factories</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>Following sections cover these in detail.</p> |
| </div> |
| <div class="sect2"> |
| <h3 id="extending-plugins">Plugin Preliminaries</h3> |
| <div class="paragraph"> |
| <p>Log4j plugin system is the de facto extension mechanism embraced by various |
| Log4j components, including <code>JsonTemplateLayout</code>. Plugins make it possible |
| for extensible components <em>receive</em> feature implementations without any explicit |
| links in between. It is analogous to a |
| <a href="https://en.wikipedia.org/wiki/Dependency_injection">dependency injection</a> |
| framework, but curated for Log4j-specific needs.</p> |
| </div> |
| <div class="paragraph"> |
| <p>In a nutshell, you annotate your classes with <code>@Plugin</code> and their (<code>static</code>) |
| creator methods with <code>@PluginFactory</code>. Last, you inform the Log4j plugin system |
| to discover these custom classes. This can be done either using <code>packages</code> |
| declared in your Log4j configuration or by various other ways described in |
| <a href="plugins.html">plugin system documentation</a>.</p> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="extending-event-resolvers">Extending Event Resolvers</h3> |
| <div class="paragraph"> |
| <p>All available <a href="#event-template-resolvers">event template resolvers</a> are simple |
| plugins employed by <code>JsonTemplateLayout</code>. To add new ones, one just needs to |
| create their own <code>EventResolver</code> and instruct its injection via a |
| <code>@Plugin</code>-annotated <code>EventResolverFactory</code> class.</p> |
| </div> |
| <div class="paragraph"> |
| <p>For demonstration purposes, below we will create a <code>randomNumber</code> event resolver. |
| Let’s start with the actual resolver:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">Custom random number event resolver</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-java" data-lang="java">package com.acme.logging.log4j.layout.template.json; |
| |
| import org.apache.logging.log4j.core.LogEvent; |
| import org.apache.logging.log4j.layout.template.json.resolver.EventResolver; |
| import org.apache.logging.log4j.layout.template.json.util.JsonWriter; |
| |
| /** |
| * Resolves a random floating point number. |
| * |
| * <h3>Configuration</h3> |
| * |
| * <pre> |
| * config = ( [ range ] ) |
| * range = number[] |
| * </pre> |
| * |
| * {@code range} is a number array with two elements, where the first number |
| * denotes the start (inclusive) and the second denotes the end (exclusive). |
| * {@code range} is optional and by default set to {@code [0, 1]}. |
| * |
| * <h3>Examples</h3> |
| * |
| * Resolve a random number between 0 and 1: |
| * |
| * <pre> |
| * { |
| * "$resolver": "randomNumber" |
| * } |
| * </pre> |
| * |
| * Resolve a random number between -0.123 and 0.123: |
| * |
| * <pre> |
| * { |
| * "$resolver": "randomNumber", |
| * "range": [-0.123, 0.123] |
| * } |
| * </pre> |
| */ |
| public final class RandomNumberResolver implements EventResolver { |
| |
| private final double loIncLimit; |
| |
| private final double hiExcLimit; |
| |
| RandomNumberResolver(final TemplateResolverConfig config) { |
| final List<Number> rangeArray = config.getList("range", Number.class); |
| if (rangeArray == null) { |
| this.loIncLimit = 0D; |
| this.hiExcLimit = 1D; |
| } else if (rangeArray.size() != 2) { |
| throw new IllegalArgumentException( |
| "range array must be of size two: " + config); |
| } else { |
| this.loIncLimit = rangeArray.get(0).doubleValue(); |
| this.hiExcLimit = rangeArray.get(1).doubleValue(); |
| if (loIncLimit > hiExcLimit) { |
| throw new IllegalArgumentException("invalid range: " + config); |
| } |
| } |
| } |
| |
| static String getName() { |
| return "randomNumber"; |
| } |
| |
| @Override |
| public void resolve( |
| final LogEvent value, |
| final JsonWriter jsonWriter) { |
| final double randomNumber = |
| loIncLimit + (hiExcLimit - loIncLimit) * Math.random(); |
| jsonWriter.writeNumber(randomNumber); |
| } |
| |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Next create a <code>EventResolverFactory</code> class to register <code>RandomNumberResolver</code> |
| into the Log4j plugin system.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">Resolver factory class to register <code>RandomNumberResolver</code> into the Log4j plugin system</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-java" data-lang="java">package com.acme.logging.log4j.layout.template.json; |
| |
| import org.apache.logging.log4j.core.config.plugins.Plugin; |
| import org.apache.logging.log4j.core.config.plugins.PluginFactory; |
| import org.apache.logging.log4j.layout.template.json.resolver.EventResolverContext; |
| import org.apache.logging.log4j.layout.template.json.resolver.EventResolverFactory; |
| import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolver; |
| import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverConfig; |
| import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverFactory; |
| |
| /** |
| * {@link RandomNumberResolver} factory. |
| */ |
| @Plugin(name = "RandomNumberResolverFactory", category = TemplateResolverFactory.CATEGORY) |
| public final class RandomNumberResolverFactory implements EventResolverFactory { |
| |
| private static final RandomNumberResolverFactory INSTANCE = |
| new RandomNumberResolverFactory(); |
| |
| private RandomNumberResolverFactory() {} |
| |
| @PluginFactory |
| public static RandomNumberResolverFactory getInstance() { |
| return INSTANCE; |
| } |
| |
| @Override |
| public String getName() { |
| return RandomNumberResolver.getName(); |
| } |
| |
| @Override |
| public RandomNumberResolver create( |
| final EventResolverContext context, |
| final TemplateResolverConfig config) { |
| return new RandomNumberResolver(config); |
| } |
| |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Almost complete. Last, we need to inform the Log4j plugin system to discover |
| these custom classes:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">Log4j configuration employing custom <code>randomNumber</code> resolver</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-xml" data-lang="xml"><?xml version="1.0" encoding="UTF-8"?> |
| <Configuration> |
| <!-- ... --> |
| <JsonTemplateLayout> |
| <EventTemplateAdditionalField |
| key="id" |
| format="JSON" |
| value='{"$resolver": "randomNumber", "range": [0, 1000000]}'/> |
| </JsonTemplateLayout> |
| <!-- ... --> |
| </Configuration></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>All available event template resolvers are located in |
| <code>org.apache.logging.log4j.layout.template.json.resolver</code> package. It is a fairly |
| rich resource for inspiration while implementing new resolvers.</p> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="extending-template-resolver">Intercepting the Template Resolver Compiler</h3> |
| <div class="paragraph"> |
| <p><code>JsonTemplateLayout</code> allows interception of the template resolver compilation, |
| which is the process converting a template into a Java function performing the |
| JSON serialization. This interception mechanism is internally used to implement |
| <code>eventTemplateRootObjectKey</code> and <code>eventTemplateAdditionalField</code> features. In a |
| nutshell, one needs to create a <code>@Plugin</code>-annotated class extending from |
| <code>EventResolverInterceptor</code> interface.</p> |
| </div> |
| <div class="paragraph"> |
| <p>To see the interception in action, check out the <code>EventRootObjectKeyInterceptor</code> |
| class which is responsible for implementing the <code>eventTemplateRootObjectKey</code> |
| feature:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">Event interceptor to add <code>eventTemplateRootObjectKey</code>, if present</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-java" data-lang="java">import org.apache.logging.log4j.layout.template.json.resolver.EventResolverContext; |
| import org.apache.logging.log4j.layout.template.json.resolver.EventResolverInterceptor; |
| import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverInterceptor; |
| |
| /** |
| * Interceptor to add a root object key to the event template. |
| */ |
| @Plugin(name = "EventRootObjectKeyInterceptor", category = TemplateResolverInterceptor.CATEGORY) |
| public class EventRootObjectKeyInterceptor implements EventResolverInterceptor { |
| |
| private static final EventRootObjectKeyInterceptor INSTANCE = |
| new EventRootObjectKeyInterceptor(); |
| |
| private EventRootObjectKeyInterceptor() {} |
| |
| @PluginFactory |
| public static EventRootObjectKeyInterceptor getInstance() { |
| return INSTANCE; |
| } |
| |
| @Override |
| public Object processTemplateBeforeResolverInjection( |
| final EventResolverContext context, |
| final Object node) { |
| String eventTemplateRootObjectKey = context.getEventTemplateRootObjectKey(); |
| return eventTemplateRootObjectKey != null |
| ? Collections.singletonMap(eventTemplateRootObjectKey, node) |
| : node; |
| } |
| |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Here, <code>processTemplateBeforeResolverInjection()</code> method checks if the user has |
| provided an <code>eventTemplateRootObjectKey</code>. If so, it wraps the root <code>node</code> with a |
| new object; otherwise, returns the <code>node</code> as is. Note that <code>node</code> refers to the |
| root Java object of the event template read by <code>JsonReader</code>.</p> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="extending-recycler">Extending Recycler Factories</h3> |
| <div class="paragraph"> |
| <p><code>recyclerFactory</code> input <code>String</code> read from the layout configuration is converted |
| to a <code>RecyclerFactory</code> using the default <code>RecyclerFactoryConverter</code> extending |
| from <code>TypeConverter<RecyclerFactory></code>. If one wants to change this behavior, |
| they simply need to add their own <code>TypeConverter<RecyclerFactory></code> implementing |
| <code>Comparable<TypeConverter<?>></code> to prioritize their custom converter.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="title">Custom <code>TypeConverter</code> for <code>RecyclerFactory</code></div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-java" data-lang="java">package com.acme.logging.log4j.layout.template.json; |
| |
| import org.apache.logging.log4j.core.config.plugins.Plugin; |
| import org.apache.logging.log4j.core.config.plugins.convert.TypeConverter; |
| import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters; |
| |
| @Plugin(name = "AcmeRecyclerFactoryConverter", category = TypeConverters.CATEGORY) |
| public final class AcmeRecyclerFactoryConverter |
| implements TypeConverter<RecyclerFactory>, Comparable<TypeConverter<?>> { |
| |
| @Override |
| public RecyclerFactory convert(final String recyclerFactorySpec) { |
| return AcmeRecyclerFactory.ofSpec(recyclerFactorySpec); |
| } |
| |
| @Override |
| public int compareTo(final TypeConverter<?> ignored) { |
| return -1; |
| } |
| |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Here note that <code>compareTo()</code> always returns -1 to rank it higher compared to |
| other matching converters.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="features">Features</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Below is a feature comparison matrix between <code>JsonTemplateLayout</code> and |
| alternatives.</p> |
| </div> |
| <table class="tableblock frame-all grid-all stretch"> |
| <caption class="title">Table 3. Feature comparison matrix</caption> |
| <colgroup> |
| <col style="width: 42.8571%;"/> |
| <col style="width: 14.2857%;"/> |
| <col style="width: 14.2857%;"/> |
| <col style="width: 14.2857%;"/> |
| <col style="width: 14.2858%;"/> |
| </colgroup> |
| <tbody> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Feature</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><code>JsonTemplateLayout</code></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="layouts.html#JSONLayout"><code>JsonLayout</code></a></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="layouts.html#GELFLayout"><code>GelfLayout</code></a></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/elastic/java-ecs-logging/tree/master/log4j2-ecs-layout"><code>EcsLayout</code></a></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Java version</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">8</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">8</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">8</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">6</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Dependencies</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">None</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Jackson</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">None</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">None</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Schema customization?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Timestamp customization?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">(Almost) garbage-free?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Custom typed <code>Message</code> serialization?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">?<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Custom typed <code>MDC</code> value serialization?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Rendering stack traces as array?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Stack trace truncation?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">JSON pretty print?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Additional string fields?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Additional JSON fields?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">Custom resolvers?</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✓</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock">✕</p></td> |
| </tr> |
| </tbody> |
| </table> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="faq">F.A.Q.</h2> |
| <div class="sectionbody"> |
| <div class="sect2"> |
| <h3 id="faq-lookups">Are lookups supported in templates?</h3> |
| <div class="paragraph"> |
| <p>Yes, <a href="lookups.html">lookups</a> (e.g., <code>${java:version}</code>, |
| <code>${env:USER}</code>, <code>${date:MM-dd-yyyy}</code>) are supported in string |
| literals of templates. Though note that they are not garbage-free.</p> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="are_recursive_collections_supported">Are recursive collections supported?</h3> |
| <div class="paragraph"> |
| <p>No. Consider a <code>Message</code> containing a recursive value as follows:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-java" data-lang="java">Object[] recursiveCollection = new Object[1]; |
| recursiveCollection[0] = recursiveCollection;</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>While the exact exception might vary, you will most like get a |
| <code>StackOverflowError</code> for trying to render <code>recursiveCollection</code> into a |
| <code>String</code>. Note that this is also the default behaviour for other Java standard |
| library methods, e.g., <code>Arrays.toString()</code>. Hence mind self references while |
| logging.</p> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="faq-garbage-free">Is <code>JsonTemplateLayout</code> garbage-free?</h3> |
| <div class="paragraph"> |
| <p>Yes, if the garbage-free layout behaviour toggling properties |
| <code>log4j2.enableDirectEncoders</code> and <code>log4j2.garbagefreeThreadContextMap</code> are |
| enabled. Take into account the following caveats:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>The configured <a href="#recycling-strategy">recycling strategy</a> might not be |
| garbage-free.</p> |
| </li> |
| <li> |
| <p>Since <code>Throwable#getStackTrace()</code> clones the original <code>StackTraceElement[]</code>, |
| access to (and hence rendering of) stack traces are not garbage-free.</p> |
| </li> |
| <li> |
| <p>Serialization of <code>MapMessage</code>s and <code>ObjectMessage</code>s are mostly |
| garbage-free except for certain types (e.g., <code>BigDecimal</code>, <code>BigInteger</code>, |
| <code>Collection</code>s, except <code>List</code>).</p> |
| </li> |
| <li> |
| <p><a href="lookups.html">Lookups</a> (that is, <code>${…​}</code> variables) are not garbage-free.</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>Don’t forget to check out <a href="#event-template-resolvers">the notes on garbage footprint of resolvers</a> |
| you employ in templates.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div id="footnotes"> |
| <hr/> |
| <div class="footnote" id="_footnotedef_1"> |
| <a href="#_footnoteref_1">1</a>. Only for <code>ObjectMessage</code>s and if Jackson is in the classpath. |
| </div> |
| </div> |
| </main> |
| </div> |
| </div> |
| <hr/> |
| <footer> |
| <div class="container-fluid"> |
| <div class="row-fluid"> |
| <p align="center">Copyright © 1999-2024 <a class="external" href="https://www.apache.org">The Apache Software Foundation</a>. All Rights Reserved.<br> |
| Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, and the Apache Logging project logo are trademarks of The Apache Software Foundation.</p> |
| </div> |
| </div> |
| </footer> |
| <script> |
| if(anchors) { |
| anchors.add(); |
| } |
| </script> |
| </body> |
| </html> |