{"id":18607,"date":"2018-05-15T09:45:43","date_gmt":"2018-05-15T09:45:43","guid":{"rendered":"https:\/\/stackify.com\/?p=18607"},"modified":"2024-04-11T06:34:00","modified_gmt":"2024-04-11T06:34:00","slug":"logging-java","status":"publish","type":"post","link":"https:\/\/stackify.com\/logging-java\/","title":{"rendered":"The State of Logging in Java"},"content":{"rendered":"<p><a id=\"post-18607-_d36kwlq1j5g6\"><\/a> When developing an application, chances are that it won\u2019t <a href=\"https:\/\/stackify.com\/deployment-tracking\/\">perform as expected<\/a> on the first run. In order to check what went wrong, developers in general use debuggers. But experienced developers know that if it happens in <a href=\"https:\/\/stackify.com\/retrace-error-monitoring\/\">production<\/a>, most debuggers won\u2019t be available. Hence, they pepper the source code with logging statements to help their future self debug the next potential bug.<\/p>\n<p>The subject of this post is to describe the range of possible options for Java applications.<\/p>\n<h2><a id=\"post-18607-_50sncv51hi4k\"><\/a><strong>The console: the legacy way<\/strong><\/h2>\n<p>In <a href=\"https:\/\/stackify.com\/content\/java\/\">Java<\/a>, a long time ago, there was no way to log but to use the standard output and standard error respectively through:<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>System.out<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>System.err<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>Exception.printStackTrace()<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Such kind of logging does the job, but logging is an &#8220;always on&#8221; feature. It lacks flexibility across different dimensions.<\/p>\n<h3><a id=\"post-18607-_gnojd6o18l9t\"><\/a>Environment<\/h3>\n<p>In many cases, whether a log should be written or not depends on the environment (development, QA, production, etc.). Let\u2019s imagine a banking application. In production environments &#8211; or at least in environments with production data, it\u2019s not desirable to log sensitive information <em>e.g.<\/em> passwords, account numbers, amount transferred, etc. However, in non-production environments, it might be a precious way to help solve a bug.<\/p>\n<h3><a id=\"post-18607-_72oo6ye3bprq\"><\/a>Time<\/h3>\n<p>It\u2019s very tempting to write everything into the log &#8220;just in case&#8221;. However, having too much information is similar to having none, because there\u2019s no way to extract useful data. It would be useful to write only important log statements, but be able to enable relevant log statements when a bug happens in a specific area.<\/p>\n<h3><a id=\"post-18607-_eidon3fpuern\"><\/a>Targets<\/h3>\n<p>By definition, logs are written to the standard output and\/or the standard console. In the end, they just print to the console. However, there are a lot of backend systems that might be good targets for logs: messaging systems, event buses, databases, etc. In the absence of dedicated logging capabilities, there must be an adapter between the console and the target system that scrapes the former to feed the later.<\/p>\n<table>\n<tbody>\n<tr>\n<td><\/td>\n<td>Scraping may be a good strategy in order to move the responsibility of feeding to the adapter from the application. However, in the absence of capability, it\u2019s the only choice available. Options are always good.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2><a id=\"post-18607-_d86lp21hn5bb\"><\/a><strong>Log4J: the role model<\/strong><\/h2>\n<p><a href=\"http:\/\/logging.apache.org\/log4j\/1.2\/\" target=\"_blank\" rel=\"noopener noreferrer\">Apache Log4J<\/a> started as an attempt to remedy the console situation. Log4J introduced many concepts that are reused across subsequent libraries.<\/p>\n<h3><a id=\"post-18607-_yz21636akcsz\"><\/a>Log levels<\/h3>\n<p>To handle the &#8220;always-on&#8221; nature of the legacy log statements, Log4J was designed around <strong>log levels<\/strong>. There are several log level available (<em>e.g.<\/em> ERROR, INFO, DEBUG), and each log statement must use one of them. At runtime, a single log level is set: log statements with the same or a higher level are executed, the others are canceled.<\/p>\n<p>Different environments can then be configured with different log levels. For example, production-like environments configuration will allow INFO logs and above only, while development environments will allow everything.<\/p>\n<h3><a id=\"post-18607-_9rjk22ijjn0d\"><\/a>Loggers<\/h3>\n<p>A <strong>logger<\/strong> is the entry-point into the Log4J library.<\/p>\n<blockquote><p><em>The Logger itself performs no direct actions. It simply has a name [\u2026\u200b]<\/em><\/p><\/blockquote>\n<p>Loggers are organized into parent-child relationships, via their name. Hence, the ch is the parent logger of the ch.frankel logger, which itself is a parent of ch.frankel.Foo logger.<\/p>\n<h3><a id=\"post-18607-_xpwlyy8l786j\"><\/a>Appenders<\/h3>\n<p>An <strong>appender<\/strong> is responsible to output a log statement to a single destination type.<\/p>\n<blockquote><p><em>The ability to selectively enable or disable logging requests based on their logger is only part of the picture. Log4j allows logging requests to print to multiple destinations. In log4j speak, an output destination is called an Appender.<\/em><\/p><\/blockquote>\n<p>Destinations includes:<\/p>\n<ul>\n<li>Files<\/li>\n<li>JMS queues<\/li>\n<li>Databases<\/li>\n<li>etc.<\/li>\n<\/ul>\n<p>If no out-of-the-box appender exists for one\u2019s specific need, it\u2019s not an issue: the Appender interface allows you to create your own implementation for specific needs.<\/p>\n<table>\n<tbody>\n<tr>\n<td><\/td>\n<td>Some appenders also offer specific features. For example, regarding the file appender, one of the most important ones is asynchronous writing. Because writing in a file is a blocking operation, log writing can become the bottleneck of an application. While logging is an important feature, it\u2019s not a core business one. Asynchronous writing makes it possible to buffer log statements in memory, and have a dedicated thread to write them in batches.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2><a id=\"post-18607-_hud2mq71enmj\"><\/a><strong>Java Util Logging: the standard<\/strong><\/h2>\n<p>Log4J started to get traction and became nearly ubiquitous. Pressure started to mount to embed similar logging capabilities inside the Java API itself. Thus, JDK 1.4 included the java.util.logging package.<\/p>\n<p>This was not the end of it all, though.<\/p>\n<p>A problem regarding JUL was that some log levels didn\u2019t have specific semantics <em>e.g.<\/em>FINER, unlike Log4J. Also, the number of log levels was different from Log4J, thus there was no easy one-to-one mapping.<\/p>\n<table>\n<tbody>\n<tr>\n<td>Log4J<\/td>\n<td>JUL<\/td>\n<\/tr>\n<tr>\n<td>\n<ul>\n<li>TRACE<\/li>\n<li>DEBUG<\/li>\n<li>INFO<\/li>\n<li>WARN<\/li>\n<li>ERROR<\/li>\n<li>FATAL<\/li>\n<\/ul>\n<\/td>\n<td>\n<ul>\n<li>FINEST<\/li>\n<li>FINER<\/li>\n<li>FINE<\/li>\n<li>CONFIG<\/li>\n<li>INFO<\/li>\n<li>WARNING<\/li>\n<li>SEVERE<\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Finally, adapters were severely limited: only console and file are provided out-of-the-box.<\/p>\n<p>Given the limitations and since Log4J was already firmly entrenched by now, JUL never really caught on.<\/p>\n<h2><a id=\"post-18607-_hd07shsxbd8m\"><\/a><strong>Apache Commons Logging: the abstraction<\/strong><\/h2>\n<p>Yet, a few libraries did migrate to the new API. As an application developer, that meant that if you were unlucky enough to use libraries that used both frameworks &#8211; Log4J and JUL, you had to configure both.<\/p>\n<p>To reduce that configuration effort, <a href=\"https:\/\/commons.apache.org\/proper\/commons-logging\/\" target=\"_blank\" rel=\"noopener noreferrer\">Apache Commons Logging<\/a> was born:<\/p>\n<blockquote><p><em>The Logging package is an ultra-thin bridge between different logging implementations. A library that uses the commons-logging API can be used with any logging implementation at runtime. Commons-logging comes with support for a number of popular logging implementations, and writing adapters for others is a reasonably simple task.\u00a0<\/em><em>\u2014 Apache Commons Logging<\/em><\/p><\/blockquote>\n<p>In the end, however, that just complicated the whole situation, as some libraries used Commons Logging, some JUL, and then most Log4J.<\/p>\n<p><a href=\"https:\/\/xkcd.com\/927\/\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-18608\" src=\"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/state-of-logging-java-standards-18608.png\" alt=\"image-proliferated\" width=\"500\" height=\"283\" \/><\/a><\/p>\n<h2><a id=\"post-18607-_o6n6jgk905zs\"><\/a><strong>SLF4J: the de-facto standard<\/strong><\/h2>\n<p>Meanwhile, Log4J had become feature complete: development had stopped.<\/p>\n<p>Ceki G\u00fclc\u00fc, Log4J\u2019s main contributor, started to work on an un-official &#8220;Log4J v2&#8221; outside of the Apache Foundation. The main goal was to fix Log4J\u2019s main problem: coupling between the API and the implementation. Thus was born <a href=\"https:\/\/www.slf4j.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">Simple Logging Facade For Java<\/a> &#8211; SLF4J.<\/p>\n<h3><a id=\"post-18607-_dsv6zix0r5nq\"><\/a>Architecture<\/h3>\n<p>The architecture of SLF4J takes advantage of the Java <a href=\"https:\/\/docs.oracle.com\/javase\/7\/docs\/api\/java\/util\/ServiceLoader.html\" target=\"_blank\" rel=\"noopener noreferrer\">Service Loader<\/a> mechanism: it allows it to work with abstractions, and to use the implementation provided at runtime on the classpath.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-18609\" src=\"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/state-of-logging-java-architecture-18609.png\" alt=\"image-log\" width=\"569\" height=\"246\" \/><\/p>\n<p>In essence, at compile-time, use SLF4J API, and any desired library at runtime. Out-of-the-box libraries include:<\/p>\n<table>\n<tbody>\n<tr>\n<td><strong>JAR<\/strong><\/td>\n<td><strong>DESCRIPTION<\/strong><\/td>\n<\/tr>\n<tr>\n<td>slf4j-log4j<\/td>\n<td>Redirects calls from SLF4J to Log4J<\/td>\n<\/tr>\n<tr>\n<td>slf4j-jdk14<\/td>\n<td>Redirects calls from SLF4J to JUL<\/td>\n<\/tr>\n<tr>\n<td>slf4j-jcl<\/td>\n<td>Redirects calls from SLF4J to Java Commons Logging<\/td>\n<\/tr>\n<tr>\n<td>slf4j-simple<\/td>\n<td>Write logs to the console<\/td>\n<\/tr>\n<tr>\n<td>slf4j-logback<\/td>\n<td>Uses the <a href=\"https:\/\/logback.qos.ch\/\" target=\"_blank\" rel=\"noopener noreferrer\">Logback<\/a> library<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4><\/h4>\n<h3><a id=\"post-18607-_hjmt3q5a1cp\"><\/a>Bridges<\/h3>\n<p>To allow an easy migration path from any of the previous logging frameworks (Log4J, JUL, or Commons Logging), SLF4J offers bridges to redirect calls from one of them to SLF4J:<\/p>\n<table>\n<tbody>\n<tr>\n<td><strong>JAR<\/strong><\/td>\n<td><strong>DESCRIPTION<\/strong><\/td>\n<\/tr>\n<tr>\n<td>jcl-over-slf4j<\/td>\n<td>Redirects calls from Commons Logging to SLF4J<\/td>\n<\/tr>\n<tr>\n<td>log4j-over-slf4j<\/td>\n<td>Redirects calls from Log4J to SLF4J<\/td>\n<\/tr>\n<tr>\n<td>jul-over-slf4j<\/td>\n<td>Redirects calls from JUL to SLF4J<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Probably because of those bridges, SLF4J became very popular, even more so than Log4J\u2026 in some cases, SLF4J used as an API, while Log4J used as an implementation.<\/p>\n<h2><a id=\"post-18607-_czrheox7e1v5\"><\/a><strong>Log4J 2: the &#8220;new&#8221; kid on the block<\/strong><\/h2>\n<p>Log4J 2 was released in 2014. It offers the same features as other logging frameworks:<\/p>\n<ul>\n<li>API separation<\/li>\n<li>Abstraction layer of multiple implementations<\/li>\n<li>Dynamic configuration reloading<\/li>\n<li>etc.<\/li>\n<\/ul>\n<p>The main advantage of Log4J 2 is lazy evaluation of log statements, by taking advantage of Java 8\u2019s lambda.<\/p>\n<p>Imagine the following log statement:<\/p>\n<pre class=\"prettyprint\">LOGGER.debug(\"This is an computationally expensive log statement\" + slowMethod());\n<\/pre>\n<p>Regardless of the log level, the slowMethod() call will take place, and decrease performance.<\/p>\n<p>Hence, for ages, it was advised to guard the log between an evaluation:<\/p>\n<pre class=\"prettyprint\">if (LOGGER.isDebug()) {\n LOGGER.debug(\"This is an computationally expensive log statement\" + slowMethod());\n}\n<\/pre>\n<p>Now, the method is called only if the log level reaches the DEBUG level. However, this introduces some issues:<\/p>\n<ul>\n<li>One needs to exercise good judgement whether this guard is necessary or not<\/li>\n<li>It makes the code less readable<\/li>\n<li>There is a risk of using different log levels in the evaluation and the log itself<\/li>\n<\/ul>\n<p>Log4J 2 solves this issues by changing the method argument from String to Provider&lt;String&gt;. It\u2019s now possible to write the following:<\/p>\n<pre class=\"prettyprint\">LOGGER.debug(() -&gt; \"This is an computationally expensive log statement\" + slowMethod());\n<\/pre>\n<p>At this point, the method is only called if the log level is DEBUG.<\/p>\n<p>And yet, I never saw Log4J 2 used, whether in apps or in third-party libraries.<\/p>\n<h2><a id=\"post-18607-_6o26b9cl94t6\"><\/a><strong>(Bonus) SLF4K: an experiment<\/strong><\/h2>\n<table>\n<tbody>\n<tr>\n<td><\/td>\n<td><em>Disclaimer<\/em>The author of this post is also the author of this library.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><a href=\"https:\/\/github.com\/nfrankel\/slf4k\" target=\"_blank\" rel=\"noopener noreferrer\">SLF4K<\/a> is a thin Kotlin wrapper around the SLF4J API to lazily evaluate messages and arguments passed to logger methods. It allows the following code:<\/p>\n<pre class=\"prettyprint\">LOGGER.debug(\"This is an computationally expensive log statement\") {slowMethod()}\n<\/pre>\n<h2><a id=\"post-18607-_e42zbpxli380\"><\/a><strong>Conclusion<\/strong><\/h2>\n<p>The state of logging in Java is a big mess: it\u2019s very fragmented between a small number of frameworks. While some frameworks try to play nicely with some others, it doesn\u2019t solve the issue that using multiple libraries might require using different configuration files.<\/p>\n<p>Retrace can help by correlating logs, errors and APM data to get more intelligence.\u00a0 <a href=\"https:\/\/s1.stackify.com\/account\/createclient?topnav=1&amp;_ga=2.187426664.1587512718.1526305723-217864825.1524494444\">Sign up<\/a> for a free 14-day trial today.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When developing an application, chances are that it won\u2019t perform as expected on the first run. In order to check what went wrong, developers in general use debuggers. But experienced developers know that if it happens in production, most debuggers won\u2019t be available. Hence, they pepper the source code with logging statements to help their [&hellip;]<\/p>\n","protected":false},"author":31,"featured_media":37607,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7],"tags":[26],"class_list":["post-18607","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","tag-logging"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v25.6) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>The State of Logging in Java - Stackify<\/title>\n<meta name=\"description\" content=\"When developing an application, chances are that it won\u2019t perform as expected on the first run. Developers know that if it happens in production, debuggers won\u2019t be available.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/stackify.com\/logging-java\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"The State of Logging in Java - Stackify\" \/>\n<meta property=\"og:description\" content=\"When developing an application, chances are that it won\u2019t perform as expected on the first run. Developers know that if it happens in production, debuggers won\u2019t be available.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/stackify.com\/logging-java\/\" \/>\n<meta property=\"og:site_name\" content=\"Stackify\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Stackify\/\" \/>\n<meta property=\"article:published_time\" content=\"2018-05-15T09:45:43+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-04-11T06:34:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"881\" \/>\n\t<meta property=\"og:image:height\" content=\"441\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Nicolas Frankel\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@stackify\" \/>\n<meta name=\"twitter:site\" content=\"@stackify\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Nicolas Frankel\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/stackify.com\/logging-java\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/stackify.com\/logging-java\/\"},\"author\":{\"name\":\"Nicolas Frankel\",\"@id\":\"https:\/\/stackify.com\/#\/schema\/person\/0d4d6a350ac15a4acbbdf9b9ce16c58f\"},\"headline\":\"The State of Logging in Java\",\"datePublished\":\"2018-05-15T09:45:43+00:00\",\"dateModified\":\"2024-04-11T06:34:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/stackify.com\/logging-java\/\"},\"wordCount\":1533,\"publisher\":{\"@id\":\"https:\/\/stackify.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/stackify.com\/logging-java\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png\",\"keywords\":[\"logging\"],\"articleSection\":[\"Developer Tips, Tricks &amp; Resources\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/stackify.com\/logging-java\/\",\"url\":\"https:\/\/stackify.com\/logging-java\/\",\"name\":\"The State of Logging in Java - Stackify\",\"isPartOf\":{\"@id\":\"https:\/\/stackify.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/stackify.com\/logging-java\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/stackify.com\/logging-java\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png\",\"datePublished\":\"2018-05-15T09:45:43+00:00\",\"dateModified\":\"2024-04-11T06:34:00+00:00\",\"description\":\"When developing an application, chances are that it won\u2019t perform as expected on the first run. Developers know that if it happens in production, debuggers won\u2019t be available.\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/stackify.com\/logging-java\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/stackify.com\/logging-java\/#primaryimage\",\"url\":\"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png\",\"contentUrl\":\"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png\",\"width\":881,\"height\":441},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/stackify.com\/#website\",\"url\":\"https:\/\/stackify.com\/\",\"name\":\"Stackify\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/stackify.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/stackify.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/stackify.com\/#organization\",\"name\":\"Stackify\",\"url\":\"https:\/\/stackify.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/stackify.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/stackify.com\/wp-content\/uploads\/2024\/05\/logo-1.png\",\"contentUrl\":\"https:\/\/stackify.com\/wp-content\/uploads\/2024\/05\/logo-1.png\",\"width\":1377,\"height\":430,\"caption\":\"Stackify\"},\"image\":{\"@id\":\"https:\/\/stackify.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Stackify\/\",\"https:\/\/x.com\/stackify\",\"https:\/\/www.instagram.com\/stackify\/\",\"https:\/\/www.linkedin.com\/company\/2596184\",\"https:\/\/www.youtube.com\/stackify\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/stackify.com\/#\/schema\/person\/0d4d6a350ac15a4acbbdf9b9ce16c58f\",\"name\":\"Nicolas Frankel\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/stackify.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/9c99d60babc5a2d53bb0ab2978e9b336?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/9c99d60babc5a2d53bb0ab2978e9b336?s=96&d=mm&r=g\",\"caption\":\"Nicolas Frankel\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"The State of Logging in Java - Stackify","description":"When developing an application, chances are that it won\u2019t perform as expected on the first run. Developers know that if it happens in production, debuggers won\u2019t be available.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/stackify.com\/logging-java\/","og_locale":"en_US","og_type":"article","og_title":"The State of Logging in Java - Stackify","og_description":"When developing an application, chances are that it won\u2019t perform as expected on the first run. Developers know that if it happens in production, debuggers won\u2019t be available.","og_url":"https:\/\/stackify.com\/logging-java\/","og_site_name":"Stackify","article_publisher":"https:\/\/www.facebook.com\/Stackify\/","article_published_time":"2018-05-15T09:45:43+00:00","article_modified_time":"2024-04-11T06:34:00+00:00","og_image":[{"width":881,"height":441,"url":"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png","type":"image\/png"}],"author":"Nicolas Frankel","twitter_card":"summary_large_image","twitter_creator":"@stackify","twitter_site":"@stackify","twitter_misc":{"Written by":"Nicolas Frankel","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/stackify.com\/logging-java\/#article","isPartOf":{"@id":"https:\/\/stackify.com\/logging-java\/"},"author":{"name":"Nicolas Frankel","@id":"https:\/\/stackify.com\/#\/schema\/person\/0d4d6a350ac15a4acbbdf9b9ce16c58f"},"headline":"The State of Logging in Java","datePublished":"2018-05-15T09:45:43+00:00","dateModified":"2024-04-11T06:34:00+00:00","mainEntityOfPage":{"@id":"https:\/\/stackify.com\/logging-java\/"},"wordCount":1533,"publisher":{"@id":"https:\/\/stackify.com\/#organization"},"image":{"@id":"https:\/\/stackify.com\/logging-java\/#primaryimage"},"thumbnailUrl":"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png","keywords":["logging"],"articleSection":["Developer Tips, Tricks &amp; Resources"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/stackify.com\/logging-java\/","url":"https:\/\/stackify.com\/logging-java\/","name":"The State of Logging in Java - Stackify","isPartOf":{"@id":"https:\/\/stackify.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/stackify.com\/logging-java\/#primaryimage"},"image":{"@id":"https:\/\/stackify.com\/logging-java\/#primaryimage"},"thumbnailUrl":"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png","datePublished":"2018-05-15T09:45:43+00:00","dateModified":"2024-04-11T06:34:00+00:00","description":"When developing an application, chances are that it won\u2019t perform as expected on the first run. Developers know that if it happens in production, debuggers won\u2019t be available.","inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/stackify.com\/logging-java\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/stackify.com\/logging-java\/#primaryimage","url":"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png","contentUrl":"https:\/\/stackify.com\/wp-content\/uploads\/2018\/05\/The-state-of-logging-in-Java-881x441-1.png","width":881,"height":441},{"@type":"WebSite","@id":"https:\/\/stackify.com\/#website","url":"https:\/\/stackify.com\/","name":"Stackify","description":"","publisher":{"@id":"https:\/\/stackify.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/stackify.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/stackify.com\/#organization","name":"Stackify","url":"https:\/\/stackify.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/stackify.com\/#\/schema\/logo\/image\/","url":"https:\/\/stackify.com\/wp-content\/uploads\/2024\/05\/logo-1.png","contentUrl":"https:\/\/stackify.com\/wp-content\/uploads\/2024\/05\/logo-1.png","width":1377,"height":430,"caption":"Stackify"},"image":{"@id":"https:\/\/stackify.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Stackify\/","https:\/\/x.com\/stackify","https:\/\/www.instagram.com\/stackify\/","https:\/\/www.linkedin.com\/company\/2596184","https:\/\/www.youtube.com\/stackify"]},{"@type":"Person","@id":"https:\/\/stackify.com\/#\/schema\/person\/0d4d6a350ac15a4acbbdf9b9ce16c58f","name":"Nicolas Frankel","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/stackify.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/9c99d60babc5a2d53bb0ab2978e9b336?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9c99d60babc5a2d53bb0ab2978e9b336?s=96&d=mm&r=g","caption":"Nicolas Frankel"}}]}},"_links":{"self":[{"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/posts\/18607"}],"collection":[{"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/comments?post=18607"}],"version-history":[{"count":3,"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/posts\/18607\/revisions"}],"predecessor-version":[{"id":43887,"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/posts\/18607\/revisions\/43887"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/media\/37607"}],"wp:attachment":[{"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/media?parent=18607"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/categories?post=18607"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/stackify.com\/wp-json\/wp\/v2\/tags?post=18607"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}