{"id":2444,"date":"2016-04-08T11:33:06","date_gmt":"2016-04-08T15:33:06","guid":{"rendered":"http:\/\/springframework.guru\/?p=2444"},"modified":"2024-10-21T08:52:39","modified_gmt":"2024-10-21T12:52:39","slug":"logback-enterprise-logging-framework-2","status":"publish","type":"post","link":"https:\/\/springframework.guru\/logback-enterprise-logging-framework-2\/","title":{"rendered":"Logback Introduction: An Enterprise Logging Framework"},"content":{"rendered":"<p>Monitoring, diagnosing, and troubleshooting are key activities in any enterprise application lifecycle, and logging is the core part of these activities. Through logging you get to see what the application code is actually\u00a0doing during these activities at runtime. Using System.out to print messages to the console is simply not sufficient for enterprise applications. Enterprise applications have logging requirements with varied degree of complexities. You will need generating logs with different levels of importance, such as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">ERROR<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">WARN<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">INFO<\/code>, and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">DEBUG<\/code>. You&#8217;ll also have requirements to send logs to different destinations, such as console, file, database, SMTP server, or\u00a0JMS queue destination. These requirements are not possible with simple System.out statements. Logging frameworks such as Logback are designed to meet the needs of logging in the enterprise.<\/p>\n<h2>Logback Architecture<\/h2>\n<p>Ceki G\u00fclc\u00fc the founder of the Log4J along with S\u00e9bastien Pennec, another Log4J contributor, designed logback. With decades of experience with logging frameworks, they designed Logback to be fast and generic enough to work under different environments. Logback is comprised of three modules:<\/p>\n<ul>\n<li><strong>logback-core<\/strong>: Forms the foundation of logback-classic and logback-access. To perform logging, you need the more specialized logback-classic or logback-access.<\/li>\n<li><strong>logback-classic<\/strong>: Relies on logback-core for logging services.<\/li>\n<li><strong>logback-access<\/strong>: Provides HTTP-access log functionalities to servlets containers, such as Tomcat and Jetty.<\/li>\n<\/ul>\n<p>In this post we will explore<code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\"> log-back-classic<\/code>\u00a0, which in\u00a0going forward\u00a0I&#8217;ll refer to as logback. Logback natively implements the <a title=\"Simple Logging Facade for Java (SLF4J)\" href=\"https:\/\/www.slf4j.org\/manual.html\" target=\"_blank\" rel=\"noopener noreferrer\">Simple Logging Facade for Java (SLF4J)<\/a> API. In a nutshell, SLF4J is a fa\u00e7ade for various logging frameworks. As a developer, you&#8217;ll write logging code against the SLF4J API. At deployment time, you have the flexibility to plug-in a\u00a0desired logging framework, made possible through an intermediate SLF4J bridge layer. As logback natively implements SLF4J, the additional SLF4J API layer doesn\u2019t incur any performance overhead, a slight\u00a0advantage that Logback has over other frameworks.<\/p>\n<p>This figure illustrates the interaction of an application with Logback.<\/p>\n<p><a href=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Architecture.png\" rel=\"attachment wp-att-2441\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2441 size-full\" src=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Architecture.png\" alt=\"Interaction of a Java application with the Logback logging framework\" width=\"672\" height=\"220\" srcset=\"https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Architecture.png 672w, https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Architecture-300x98.png 300w\" sizes=\"(max-width: 672px) 100vw, 672px\" \/><\/a><\/p>\n<p>The key Logback components are loggers, appenders and encoders\/layouts. The components work together to provide developer full control on how messages are logged, formatted, and where they are reported.<\/p>\n<h3>Logger<\/h3>\n<p>Loggers are the components that do the heavy work\u00a0in logging. They capture the logging data and output it to a destination using\u00a0appenders. The loggers used in an application are typically organized into a hierarchy and a root logger resides at the top of the hierarchy. It is the <a href=\"https:\/\/logback.qos.ch\/apidocs\/ch.qos.logback.classic\/ch\/qos\/logback\/classic\/LoggerContext.html\" target=\"_blank\" rel=\"noopener noreferrer\">LoggerContext<\/a> that is responsible for creating loggers and arranging them in a hierarchy.<\/p>\n<p>Loggers maintains a hierarchical naming rule. As an example, a logger named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">guru<\/code> is the parent of the logger, named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">guru.springframework<\/code> and the ancestor of the logger, named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">guru.springframework.blog<\/code>.<br \/>\n<a href=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logger_Hierarchy.png\" rel=\"attachment wp-att-2442\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2442\" src=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logger_Hierarchy.png\" alt=\"Logger Hierarchy\" width=\"549\" height=\"212\" srcset=\"https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logger_Hierarchy.png 549w, https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logger_Hierarchy-300x116.png 300w\" sizes=\"(max-width: 549px) 100vw, 549px\" \/><\/a><\/p>\n<p>Apart from logger inheritance, an important logback concept is level inheritance, also referred as effective level. You can assign levels to loggers. Logback supports the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">TRACE<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">DEBUG<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">INFO<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">WARN<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">ERROR<\/code> levels, as shown in this figure.<\/p>\n<p><a href=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Log_Levels.png\" rel=\"attachment wp-att-2443\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2443\" src=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Log_Levels.png\" alt=\"Log Levels\" width=\"379\" height=\"399\" srcset=\"https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Log_Levels.png 379w, https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Log_Levels-285x300.png 285w\" sizes=\"(max-width: 379px) 100vw, 379px\" \/><\/a><\/p>\n<p>As you can see in the figure above, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">TRACE<\/code> is the lowest level and the level moves up through, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">DEBUG<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">INFO<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">WARN<\/code>, till <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">ERROR<\/code>, the highest level. This means that if you set the logger level to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">WARN<\/code>, then only the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">WARN<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">ERROR<\/code> level log messages will be displayed and the rest will be ignored.<\/p>\n<p>In addition to the above levels, there are two special levels:<\/p>\n<ul>\n<li><strong>ALL<\/strong>: Turns on all levels.<\/li>\n<li><strong>OFF<\/strong>: Turns off all levels.<\/li>\n<\/ul>\n<p>If a logger is not assigned a level, then level inheritance comes into play. The logger will inherit the level from its nearest ancestor with an assigned level. If none of the application loggers in the hierarchy have assigned level, the level of the root logger will be inherited. The default level of the root logger is <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">DEBUG<\/code>.<\/p>\n<p><strong>Note<\/strong>: While developing in your local machine, it is common to set the log level to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">DEBUG<\/code>. This will give you detailed log messages for your development use. When deployed to a\u00a0production environment, it&#8217;s typical to set the log level to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">ERROR<\/code>. This is to avoid filling your logs with excessive debug information. Also,\u00a0while logging is very efficient, there is still a cost to system resources.<\/p>\n<h3>Appenders<\/h3>\n<p>Once you capture logging information through a logger, you need to send it to an output destination. The output destination is called an appender, and it&#8217;s attached to the logger. Log4J 2 provides appenders for console, files, remote socket servers, SMTP servers, many\u00a0popular databases (such as MySQL, PostgreSQL, and Oracle), JMS, remote UNIX Syslog daemons, and more.<\/p>\n<h3>Layouts\/Encoders<\/h3>\n<p>An appender uses a layout to format a log event. A layout, which is an implementation of the <a title=\"Layout Interface\" href=\"https:\/\/logback.qos.ch\/manual\/layouts.html\" target=\"_blank\" rel=\"noopener noreferrer\">Layout<\/a> interface of log4j-core, transforms a log event to a string. A layout can\u2019t control when log events get written out, and therefore can\u2019t group events into batches. To address the limitations of layouts, logback introduced encoders in version 0.9.19. Encoders, which are implementation of the <a title=\"Encoder Interface\" href=\"https:\/\/logback.qos.ch\/manual\/encoders.html\" target=\"_blank\" rel=\"noopener noreferrer\">Encoder<\/a> interface, transforms an incoming log event into a byte array and writes out the resulting array onto the appropriate output stream. Encoders have total control over the format of the bytes written out. In addition, encoders can control whether (and when) those bytes get written out. I&#8217;ll discuss more about layouts and encoders in upcoming posts on logback configuration.<\/p>\n<h2>Using Logback<\/h2>\n<p>We will start with a simple application that uses Logback for logging. To start with, we need the logback dependencies in our project. Out of the box, both Spring Boot core and web projects include the logback classic dependencies. This figure shows the logback dependencies included in Spring Boot.<\/p>\n<p><a href=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Dependencies.png\" rel=\"attachment wp-att-2445\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2445 size-full\" src=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Dependencies.png\" alt=\"Logback Dependencies via Sprint Boot\" width=\"534\" height=\"470\" srcset=\"https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Dependencies.png 534w, https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Dependencies-300x264.png 300w\" sizes=\"(max-width: 534px) 100vw, 534px\" \/><\/a><br \/>\nAs shown in the figure above, the latest <strong>SpringBoot 1.3.3REALESE<\/strong> version as of writing this post uses <strong>Logback classic 1.1.5<\/strong>.<\/p>\n<p>If you want to use different Logback and SLF4J versions or if you are not using SpringBoot, define their dependencies in your Maven POM, like this.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\">. . .\n&lt;dependency&gt;\n  &lt;groupId&gt;org.slf4j&lt;\/groupId&gt;\n  &lt;artifactId&gt;slf4j-api&lt;\/artifactId&gt;\n  &lt;version&gt;1.7.21&lt;\/version&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n  &lt;groupId&gt;ch.qos.logback&lt;\/groupId&gt;\n  &lt;artifactId&gt;logback-classic&lt;\/artifactId&gt;\n  &lt;version&gt;1.1.7&lt;\/version&gt;\n&lt;\/dependency&gt;\n. . .\n<\/pre>\n<p>In an application, you can retrieve a logger by calling the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">getLogger()<\/code> method of the SLF4J <a title=\"LoggerFactory Class\" href=\"https:\/\/www.slf4j.org\/api\/org\/slf4j\/LoggerFactory.html\" target=\"_blank\" rel=\"noopener noreferrer\">LoggerFactory<\/a> class. There are two overloaded <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">getLogger()<\/code> methods. One returns a <a title=\"Logger Class\" href=\"https:\/\/www.slf4j.org\/apidocs\/org\/slf4j\/Logger.html\" target=\"_blank\" rel=\"noopener noreferrer\">Logger<\/a> instance named according to the string value passed as parameter. The other returns a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Logger<\/code> instance named corresponding to the class passed as parameter. The recommended strategy is to use the latter one. This is because in a large application with thousands of log statements, you will find it easy to identify the origin of a log message as the log output bears the name of the generating logger. Once you retrieve a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Logger<\/code>, you can call the log methods on it, like this.<\/p>\n<h4>LogbackDemo.java<\/h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\">package guru.springframework.blog.logbackoverview;\n\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class LogbackDemo {\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n    public void performTask(){\n        logger.info(\"This is an {} message.\",\"info\");\n        logger.warn(\"This is a warn message.\");\n        logger.error(\"This is an error message.\");\n        logger.debug(\"This is a debug message.\");\n    }\n}\n<\/pre>\n<p>In Line 8 of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">LogbackDemo<\/code> class above, we retrieved a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Logger<\/code> object with a call to the static <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Loggerfactory.getLogger()<\/code> method. Passing <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">LogbackDemo.class<\/code> to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">getLogger()<\/code> instead of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">this.getClass()<\/code> will produce the same result. But I suggest passing <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">this.getClass()<\/code> to decouple the statement from a particular class, thereby making it reusable across other logging classes. From Line 10- Line 13 we called the log methods on the retrieved logger. Notice Line 10 that uses parameterized message in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">info()<\/code> method. You can use such parameterized log messages in the other logging methods too.<\/p>\n<p>To test the preceding logger, we will use <a href=\"http:\/\/springframework.guru\/unit-testing-junit-part-1\/\" target=\"_blank\" rel=\"noopener noreferrer\">JUnit<\/a>. The test class is this.<\/p>\n<h4>LogbackDemoTest.java<\/h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\">package guru.springframework.blog.logbackoverview;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\npublic class LogbackDemoTest {\n\n    @Test\n    public void testPerformTask() throws Exception {\n        LogbackDemo logBackDemo = new LogbackDemo();\n        logBackDemo.performTask();\n    }\n}\n<\/pre>\n<p>When you run the test class, the log messages of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">LogbackDemo<\/code> are sent to the console.<br \/>\n<a href=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Output.png\" rel=\"attachment wp-att-2446\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2446\" src=\"http:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Output.png\" alt=\"Log Output in Console\" width=\"982\" height=\"177\" srcset=\"https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Output.png 982w, https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Output-300x54.png 300w, https:\/\/springframework.guru\/wp-content\/uploads\/2016\/04\/Logback_Output-768x138.png 768w\" sizes=\"(max-width: 982px) 100vw, 982px\" \/><\/a><\/p>\n<h2>Summary<\/h2>\n<p>In the example of this post, you may have noticed that I did not specify any appender\/encoder or layout for the logger. Rather, I relied upon defaults inherited from the logback root logger. By default, the root logger is associated with the console appender and have the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">DEBUG<\/code> level, and our logger inherited both. Therefore, debug and higher log messages were sent to the IntelliJ console. However, in enterprise applications its likely you&#8217;ll work with external configuration files to use more advanced features of Logback.\u00a0These configuration files can be XML or Groovy to specify Logback configuration options. In upcoming posts, I&#8217;ll discuss using both XML and Groovy external configuration files to help you explore what a powerful logging tool Logback is.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Monitoring, diagnosing, and troubleshooting are key activities in any enterprise application lifecycle, and logging is the core part of these activities. Through logging you get to see what the application code is actually\u00a0doing during these activities at runtime. Using System.out to print messages to the console is simply not sufficient for enterprise applications. Enterprise applications [&hellip;]<a href=\"https:\/\/springframework.guru\/logback-enterprise-logging-framework-2\/\" class=\"df-link-excerpt\">Continue reading<\/a><\/p>\n","protected":false},"author":1,"featured_media":4588,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"Logback Introduction: An Enterprise Logging Framework #java","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[162],"tags":[163,29],"class_list":["post-2444","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-logback","tag-logback","tag-spring-boot"],"jetpack_publicize_connections":[],"aioseo_notices":[],"modified_by":"jt","jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/springframework.guru\/wp-content\/uploads\/2015\/03\/Banner560x292_05aweb.jpg","jetpack_shortlink":"https:\/\/wp.me\/p5BZrZ-Dq","_links":{"self":[{"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/posts\/2444"}],"collection":[{"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/comments?post=2444"}],"version-history":[{"count":8,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/posts\/2444\/revisions"}],"predecessor-version":[{"id":8261,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/posts\/2444\/revisions\/8261"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/media\/4588"}],"wp:attachment":[{"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/media?parent=2444"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/categories?post=2444"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/tags?post=2444"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}