Logging in Clojure: Making Sense of the Mess

栏目: IT技术 · 发布时间: 4年前

内容简介:You may also likeLogging seems like a simple enough concept. It’s basically

Logging in Clojure: Making Sense of the Mess

You may also like Dates in Clojure: Making Sense of the Mess

Logging seems like a simple enough concept. It’s basically println with a couple of extra smarts. And yet it can be oh so confounding. Even when you literally do not care a sigle bit about logging you may still be sent down a rabbit hole because of warnings of things that are or aren’t on the classpath. What even is a classpath?

The Logging Wars

Most of the mess that is logging we inherit from Java. During the first half of the nillies logging libraries and frameworks were fighthing for dominance in what I will dramatically dub “The Logging Wars”. Around 2006 things started stabilizing, but the scars from this period are still visible in our dependency graphs today.

Here’s a typical example, snippets like this you find in nearly every production Clojure project. To make sense of it we need to dive into a little bit of history

{:deps
 {,,,
  org.slf4j/slf4j-api {:mvn/version "1.7.30"}
  org.slf4j/jul-to-slf4j {:mvn/version "1.7.30"}
  org.slf4j/jcl-over-slf4j {:mvn/version "1.7.30"}
  org.slf4j/log4j-over-slf4j {:mvn/version "1.7.30"}
  org.slf4j/osgi-over-slf4j {:mvn/version "1.7.30"}
  ch.qos.logback/logback-classic {:mvn/version "1.2.3"}
  }}

The first logging library to gain traction in the Java world was Log4j. Log4j 1.0 was released in January 2000, but its history goes back to 1996, when it was developed as part of an EU research project called SEMPER .

Log4j is credited with being the first to introduce the hierarchical loggers, a concept that would be adopted by nearly every logging library that came after it, not just in Java.

Meanwhile Sun Microsystems was working on including logging functionality in the JDK, JSR-47 was initiated in 1999, and the java.util.logging (JUL) package was included in Java 1.4 in February 2002. The API has a striking similarity with Log4j.

Facade/off

Because it had taken Java so long to include an official logging API a bunch of different libraries had sprung up to fill the void. This was kind of annoying, because third party libraries would all depend on different logging implementations, making it hard to get a single unified log of what was going on in your application. Application developers also became more prudent about coupling their code to a specific logging implementation, given how things were shifting.

And thus the idea of a “Logging Facade” was born. A Logging Facade provides an intermediate interface. Application or library code can use whatever logging library code they prefer, the logging calls simply get forwarded to the facade.

The facade in turn will forward these to a specific logging implementation, so you could swap out Log4j for java.util.logging for Avalon Logkit without having to change a line of code.

The first library to popularize this idea was Jakarta Commons Logging (JCL), which hit 1.0 in 2002, right around the time that Java 1.4 was released. JCL would later be renamed to Apache Commons Logging.

Apparently JCL had some shortcomings, and so in 2005 SLF4J was conceived, the Simple Logging Facade for Java. SLF4J, like JCL, is able to output to various pre-existing libraries, including Log4j and java.util.logging, but the SLF4J author also wrote a logging backend that is “native” to SLF4J, Logback.

SLF4J and Logback are both written by Ceki Gulcu who was already involved way back with the original Log4j, so supposedly he’s had some time to mull over the problem space, and you could say that SLF4J is the de facto standard now.

Given that SLF4J has become somewhat of a standard in the Java world, and that you can pipe virtually every logging library out there into SLF4J, I would recommend to center the logging of your application around SLF4J. Add the necessary adapters to forward all other logging libraries to SLF4J. For the backend I recommend Logback. That’s how you end up with the snippet at the top.

Logback Mountain

SLF4J will automatically figure out which backend is available, and use that one. This means that to make this work reliably you need to make sure there is one and only one logging backend in your dependencies. A lot of libraries will indirectly pull in the “nop” backend which is simply a placeholder that doesn’t log anything. Use lein deps :tree or clj -Stree and add exclusions where necessary. For example:

org.clojure/tools.deps.alpha {:mvn/version "0.6.480", :exclusions [org.slf4j/slf4j-nop]}

Some of the libraries that you want to pipe into SLF4J will do their own auto-detection of where their logs will go, leading to more stuff to exclude. You probably want to avoid having any of these in your list of dependencies, directly or indirectly

  • commons-logging
  • log4j
  • org.apache.logging.log4j/log4j
  • org.slf4j/simple
  • org.slf4j/slf4j-jcl
  • org.slf4j/slf4j-nop
  • org.slf4j/slf4j-log4j12
  • org.slf4j/slf4j-log4j13

You can try this snippet to see if you need to add any extra exclusions.

clj -Stree | egrep '^\s*(commons-logging|log4j|org\.apache\.logging\.log4j/log4j|org\.slf4j/simple|org\.slf4j/slf4j-jcl|org\.slf4j/slf4j-nop|org\.slf4j/slf4j-log4j12|org\.slf4j/slf4j-log4j13) '

or for Leiningen:

lein deps :tree | egrep '\[(commons-logging|log4j|org\.apache\.logging\.log4j/log4j|org\.slf4j/simple|org\.slf4j/slf4j-jcl|org\.slf4j/slf4j-nop|org\.slf4j/slf4j-log4j12|org\.slf4j/slf4j-log4j13) '

If you are using Leiningen then it’s possible to declare exclusions at the top level and have them apply to all dependencies, which is quite useful. Have a look at Stuart Sierra’s log.dev example project for what that looks like.

To configure Logback all you need is a logback.xml file, which you typically put under resources/ . Do make sure "resources" is part of your :paths (deps.edn). Leiningen adds it to its :resource-paths automatically.

This snippet is a good starting point:

<configuration scan="true" scanPeriod="5 seconds">
  <appender name="STDOUT">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
  <root level="INFO">
    <appender-ref ref="STDOUT" />
  </root>
  <logger name="org.apache.kafka" level="WARN" />
</configuration>
logback.xml

There’s Something About Clojure

Note that so far I haven’t mentioned anything yet about Clojure libraries. But given the above all you need is a pretty lightweight library that outputs to SLF4J. Ideally said library would be able to log Clojure data structures directly, so you can reap the benefits of structured logging (logs as data). Which brings me to my favorite Clojure logging library io.pedestal.log .

There are various other Clojure libraries, some of them have appealing propositions, and you may or may not decide to adopt them in your projects. I hope that with the background this article provides you can now more easily assess where they fit in the ecosystem, and what they bring to the table.

You may also have come across clojure.tools.logging . This is an attempt to provide a logging facade directly in Clojure. It has support for various backends, including SLF4J. It provided an idiomatic API for logging from Clojure early on (2011), without dictating a specific backend. Nowadays I think there are better alternatives.

Unilog provides a more Clojure-esque configuration interface to Logback, allowing you to use EDN files instead of logback.xml .

Some library authors decided they had enough of this mess, and started over from scratch, implementing an entire logging stack in Clojure. These include Timbre and μ/log . Timbre is particularly impressive in that it can still act as a backend for SLF4J.

μ/log pushes the idea of structured logging in a distributed context, where logs are just another data stream akin to a Kafka topic, and indeed it can log to Kafka directly, as well as ElasticSearch, Kinesis, or plain files or stdout.

Revenge of the Log4j

After a small decade of stability the Java folks decided to stir things up again by coming up with Log4j 2. This is really a grand rewrite, and so it’s unfortunate they didn’t just give it a new name.

Log4j 2 tries to learn from all that came before it. It separates logging from implementation, is said to have better performance in multi-threaded applications, has first class support for data-based log messages (rather than strings), the list goes on.

I think Log4j 2 holds some interesting potential for Clojure, since it’s well connected to the rest of the ecosystem (adapters to and from other logging libraries and facades), and has support for java.util.Map based messages (an interface implemented by Clojure’s maps). I’d be curious to see Clojure libraries explore what they can do with this.

Conclusion

SLF4J + Logback has been my default for some time, and one that I’ve seen used with success in real world projects, in particular in combination with pedestal.log. It’s an approach that really leans in to Clojure’s philosophy of making good use of its host instead of reinventing the wheel, and Logback’s reload functionality make it a great fit for a REPL driven workflow.

Timbre however does form a strong contender, particularly for people who prefer a pure Clojure approach, and with its SLF4J bridge it can still play the same role as Logback, thus unifying application and library logging.

This post has been all about Clojure, luckily on ClojureScript things are a little less messy. The Google Closure library contains goog.log , which offers an API inspired by Log4j, and Glögi offers an idiomatic ClojureScript interface inspired by pedestal.log.

Happy logging!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

游戏引擎架构

游戏引擎架构

[美] Jason Gregory (杰森.格雷戈瑞) / 叶劲峰 / 电子工业出版社 / 2014-1 / 128.00元

《游戏引擎架构》同时涵盖游戏引擎软件开发的理论及实践,并对多方面的题目进行探讨。本书讨论到的概念及技巧实际应用于现实中的游戏工作室,如艺电及顽皮狗。虽然书中采用的例子通常依据一些专门的技术,但是讨论范围远超于某个引擎或API。文中的参考及引用也非常有用,可让读者继续深入游戏开发过程的任何特定方向。 《游戏引擎架构》为一个大学程度的游戏编程课程而编写,但也适合软件工程师、业余爱好者、自学游戏程......一起来看看 《游戏引擎架构》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具