/README.md
Markdown | 181 lines | 137 code | 44 blank | 0 comment | 0 complexity | 61fcf4636a172cd6df0db90711a3a401 MD5 | raw file
1Logula 2====== 3 4*Bwah ah ah ah.* 5 6[Logula](http://github.com/codahale/logula) is a Scala library which provides a 7sane log output format and an easy-to-use mixin for adding logging to your code. 8 9It's a thin front-end for [log4j 1.2](http://logging.apache.org/log4j/1.2/) 10because `java.util.logging` was a pain in the neck to deal with. 11 12 13Requirements 14------------ 15 16* Java SE 6 17* Scala 2.8.1 or 2.9.0-1 18* log4j 1.2 19 20 21How To Use 22---------- 23 24**First**, specify Logula as a dependency: 25 26```xml 27<repositories> 28 <repository> 29 <id>repo.codahale.com</id> 30 <url>http://repo.codahale.com</url> 31 </repository> 32</repositories> 33 34<dependencies> 35 <dependency> 36 <groupId>com.codahale</groupId> 37 <artifactId>logula_${scala.version}</artifactId> 38 <version>2.1.3</version> 39 </dependency> 40</dependencies> 41``` 42 43**Second**, configure the logging system: 44 45```scala 46import com.codahale.logula.Logging 47import org.apache.log4j.Level 48 49Logging.configure { log => 50 log.registerWithJMX = true 51 52 log.level = Level.INFO 53 log.loggers("com.myproject.weebits") = Level.OFF 54 55 log.console.enabled = true 56 log.console.threshold = Level.WARN 57 58 log.file.enabled = true 59 log.file.filename = "/var/log/myapp/myapp.log" 60 log.file.maxSize = 10 * 1024 // KB 61 log.file.retainedFiles = 5 // keep five old logs around 62 63 // syslog integration is always via a network socket 64 log.syslog.enabled = true 65 log.syslog.host = "syslog-001.internal.example.com" 66 log.syslog.facility = "local3" 67} 68``` 69 70**Third**, add some logging to your classes: 71 72```scala 73class MyThing extends Logging { 74 def complicatedManoeuvre() { 75 try { 76 log.warn("This is about to get complicated...") 77 log.info("Trying to do %d backflips", backflipsToAttempt) 78 // complicated bit elided 79 } catch { 80 case e: Exception => log.error(e, "Horrible things have happened.") 81 } 82 } 83} 84``` 85 86Notice that the logging statements use Scala's formatting syntax, and that 87logged exceptions are passed as the first argument. 88 89 90Statement Arguments 91------------------- 92 93Unlike a lot of Scala logging libraries, Logula doesn't use pass-by-name 94semantics (e.g., `f: => A`) for its logging statements, which means two things: 95 961. The Scala compiler doesn't have to create one-off closure objects for each 97 logging statement. This should reduce the amount of garbage collection 98 pressure. 992. If your logging arguments are complex to create, that price will be paid 100 regardless of whether or not the statement is logged. 101 102For example: 103 104```scala 105log.debug("A huge collection: %s", things.mkString(", ")) 106``` 107 108The `mkString` call will happen every time. To prevent this, either keep your 109arguments simple: 110 111```scala 112log.debug("A huge collection: %s", things) 113``` 114 115or only conditionally log them: 116 117```scala 118if (log.isDebugEnabled) { 119 log.debug("A huge collection: %s", things.mkString(", ")) 120} 121``` 122 123In most cases, it's simple enough to just log basic values. 124 125 126The Log Format 127-------------- 128 129Logula's log format has a few specific goals. 130 131* Be roughly human readable. You shouldn't need another program to make sense of 132 your program. 133* Be machine parsable. You shouldn't need another human to make sense of your 134 program. (Shush.) 135* Make it easy for sleepy ops folks to figure out why things are pear-shaped at 136 o'dark-thirty using standard UNIXy tools like `tail`, `grep`, and `fortune`. 137 138An example of logging output looks like this: 139 140 TRACE [2010-04-06 06:42:35,271] com.codahale.logula.examples.ThingDoer: Contemplating doing a thing. 141 DEBUG [2010-04-06 06:42:35,274] com.codahale.logula.examples.ThingDoer: About to do a thing. 142 INFO [2010-04-06 06:42:35,274] com.codahale.logula.examples.ThingDoer: Doing a thing 143 WARN [2010-04-06 06:42:35,275] com.codahale.logula.examples.ThingDoer: Doing a thing 144 ERROR [2010-04-06 06:42:35,275] com.codahale.logula.examples.ThingDoer: This may get ugly. 145 FATAL [2010-04-06 06:42:35,275] com.codahale.logula.examples.ThingDoer: The thing has gone horribly wrong. 146 ! java.lang.RuntimeException: oh noes! 147 ! at scala.Predef$.error(Predef.scala:74) 148 ! at com.codahale.logula.examples.ThingDoer.run(ExampleLoggingRun.scala:16) 149 ! at com.codahale.logula.examples.ExampleLoggingRun$.main(ExampleLoggingRun.scala:40) 150 ! at com.codahale.logula.examples.ExampleLoggingRun.main(ExampleLoggingRun.scala) 151 ! at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 152 ! at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 153 ! at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 154 ! at java.lang.reflect.Method.invoke(Method.java:597) 155 ! at sbt.Run.run0(Run.scala:60) 156 ! at sbt.Run.execute$1(Run.scala:47) 157 ! at sbt.Run$$anonfun$run$2.apply(Run.scala:50) 158 ! at sbt.Run$$anonfun$run$2.apply(Run.scala:50) 159 ! at sbt.TrapExit$.executeMain$1(TrapExit.scala:33) 160 ! at sbt.TrapExit$$anon$1.run(TrapExit.scala:42) 161 ! 162 163A few items of note: 164 165 * All timestamps are in UTC and ISO 8601 format. This really should be OK with 166 you. 167 * You can grep for messages of a specific level really easily: 168 `tail -f logula.log | grep '^WARN'` 169 * You can grep for messages from a specific class or package really easily: 170 `tail -f logula.log | grep 'ThingDoer'` 171 * You can even pull out full exception stack traces, plus the accompanying 172 log message: `tail -f logula.log | grep -B 1 '^\!'` 173 * If you squint, you can still make out the actual log messages. 174 175 176License 177------- 178 179Copyright (c) 2010-2011 Coda Hale 180 181Published under The MIT License, see LICENSE