内容简介:You may also likeLogging seems like a simple enough concept. It’s basically
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!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
游戏编程权威指南
Mike McShaffry 麦克沙福瑞、David “Rez” Graham 格雷海姆 / 师蓉、李静、李青翠 / 人民邮电 / 2016-3 / 99.00元
全书分为4个部分共24章。首部分是游戏编程基础,主要介绍了游戏编程的定义、游戏架构等基础知识。 第二部分是让游戏跑起来,主要介绍了初始化和关闭代码、主循环、游戏主题和用户界面等。 第三部分是核心游戏技术,主要介绍了一些*为复杂的代码 示例,如3D编程、游戏音频、物理和AI编程等。 第四部分是综合应用,主要介绍了网络编程、多道程序设计和用C#创建工具等,并利用前面所讲的 知识开发出......一起来看看 《游戏编程权威指南》 这本书的介绍吧!