Giter Site home page Giter Site logo

timbre's Introduction

Taoensso open-source

CHANGELOG | API | current Break Version:

[com.taoensso/timbre "4.10.0"] ; Please check CHANGELOG for details

Please consider helping to support my continued open-source Clojure/Script work?

Even small contributions can add up + make a big difference to help sustain my time writing, maintaining, and supporting Timbre and other Clojure/Script libraries. Thank you!

- Peter Taoussanis

Timbre

A pure Clojure/Script logging library

Java logging is a Kafkaesque mess of complexity that buys you nothing. It can be comically hard to get even the simplest logging working, and it just gets worse at scale.

Timbre offers an all Clojure/Script alternative that's fast, deeply flexible, easy to configure, and that works out the box. No XML.

Supports optional interop with tools.logging and log4j/logback/slf4j.

Happy hacking!

Features

  • Full Clojure + ClojureScript support (v4+).
  • No XML or properties files. A single, simple config map, and you're set.
  • Simple, flexible fn appender model with middleware.
  • Great performance at any scale.
  • Filter logging by levels, namespace whitelist/blacklist patterns, and more.
  • Zero overhead with complete Clj+Cljs elision for compile-time level/ns filters.
  • Useful built-in appenders for out-the-box Clj+Cljs logging.
  • Powerful, easy-to-configure rate limits and async logging.
  • Logs as Clojure values (v3+).
  • Small, simple, cross-platform codebase.

3rd-party tools, appenders, etc.

Link Description
@fzakaria/slf4j-timbre Route log4j/logback/sfl4j log output to Timbre
@palletops/log-config Library to help manage Timbre logging config
Your link here? PR's welcome!

Getting started

Add the necessary dependency to your project:

[com.taoensso/timbre "4.10.0"]

And setup your namespace imports:

(ns my-clj-ns ; Clojure namespace
  (:require
    [taoensso.timbre :as timbre
      :refer [log  trace  debug  info  warn  error  fatal  report
              logf tracef debugf infof warnf errorf fatalf reportf
              spy get-env]]))

(ns my-cljs-ns ; ; ClojureScript namespace
  (:require
    [taoensso.timbre :as timbre
      :refer-macros [log  trace  debug  info  warn  error  fatal  report
                     logf tracef debugf infof warnf errorf fatalf reportf
                     spy get-env]]))

You can also call (timbre/refer-timbre) to configure Clj ns referrals automatically.

Logging

By default, Timbre gives you basic println and js/console (v4+) output at a :debug log level:

(info "This will print") => nil
%> 15-Jun-13 19:18:33 localhost INFO [my-app.core] - This will print

(spy :info (* 5 4 3 2 1)) => 120
%> 15-Jun-13 19:19:13 localhost INFO [my-app.core] - (* 5 4 3 2 1) => 120

(defn my-mult [x y] (info "Lexical env:" (get-env)) (* x y)) => #'my-mult
(my-mult 4 7) => 28
%> 15-Jun-13 19:21:53 localhost INFO [my-app.core] - Lexical env: {x 4, y 7}

(trace "This won't print due to insufficient log level") => nil

First-argument exceptions generate a nicely cleaned-up stack trace using io.aviso.exception (Clj only):

(info (Exception. "Oh noes") "arg1" "arg2")
%> 15-Jun-13 19:22:55 localhost INFO [my-app.core] - arg1 arg2
java.lang.Exception: On noes
<Stacktrace>

Other utils include: log-errors, log-and-rethrow-errors, logged-future, and handle-uncaught-jvm-exceptions! (please see the API for details).

Disabling stacktrace colors

ANSI colors are enabled by default for stacktraces. To turn these off (e.g. for log files or emails), you can add the following entry to your top-level config or individual appender map/s:

:output-fn (partial timbre/default-output-fn {:stacktrace-fonts {}})

And/or you can set the TIMBRE_DEFAULT_STACKTRACE_FONTS environment variable (supports edn).

Data flow

Timbre's inherently a very simple design, no magic. It's just Clojure data and functions:

  1. Unfiltered logging calls generate a data map: {:level _ :?ns-str _ ...}
  2. The resulting data map passes through any middleware fns, (fn [data]) -> ?data
  3. The resulting data map is sent to all appender fns, (fn [data]) -> ?effects

Configuration

This is the biggest win over Java logging IMO.

All of Timbre's behaviour is controlled through a single, simple Clojure map.

See timbre/example-config for Timbre's default config map

(def example-config
  "An example Timbre v4 config map.

  APPENDERS
    An appender is a map with keys:
      :min-level       ; Level keyword, or nil (=> no minimum level)
      :enabled?        ;
      :async?          ; Dispatch using agent? Useful for slow appenders (clj only)
      :rate-limit      ; [[ncalls-limit window-ms] <...>], or nil
      :output-fn       ; Optional override for inherited (fn [data]) -> string
      :timestamp-opts  ; Optional override for inherited {:pattern _ :locale _ :timezone _} (clj only)
      :ns-whitelist    ; Optional, stacks with active config's whitelist
      :ns-blacklist    ; Optional, stacks with active config's blacklist
      :fn              ; (fn [data]) -> side effects, with keys described below

    An appender's fn takes a single data map with keys:
      :config          ; Entire config map (this map, etc.)
      :appender-id     ; Id of appender currently dispatching
      :appender        ; Entire map of appender currently dispatching
      :instant         ; Platform date (java.util.Date or js/Date)
      :level           ; Keyword
      :error-level?    ; Is level e/o #{:error :fatal}?
      :?ns-str         ; String,  or nil
      :?file           ; String,  or nil
      :?line           ; Integer, or nil ; Waiting on CLJ-865
      :?err            ; First-arg platform error, or nil
      :vargs           ; Vector of raw args
      :output_         ; Forceable - final formatted output string created
                       ; by calling (output-fn <this-data-map>)
      :msg_            ; Forceable - args as a string
      :timestamp_      ; Forceable - string (clj only)
      :hostname_       ; Forceable - string (clj only)
      :output-fn       ; (fn [data]) -> formatted output string
                       ; (see `default-output-fn` for details)
      :context         ; *context* value at log time (see `with-context`)

      **NB** - any keys not specifically documented here should be
      considered private / subject to change without notice.

  MIDDLEWARE
    Middleware are simple (fn [data]) -> ?data fns (applied left->right) that
    transform the data map dispatched to appender fns. If any middleware
    returns nil, NO dispatch will occur (i.e. the event will be filtered).

  The `example-config` source code contains further settings and details.
  See also `set-config!`, `merge-config!`, `set-level!`."

  {:level :debug  ; e/o #{:trace :debug :info :warn :error :fatal :report}

   ;; Control log filtering by namespaces/patterns. Useful for turning off
   ;; logging in noisy libraries, etc.:
   :ns-whitelist  [] #_["my-app.foo-ns"]
   :ns-blacklist  [] #_["taoensso.*"]

   :middleware [] ; (fns [data]) -> ?data, applied left->right

   ;; Clj only:
   :timestamp-opts default-timestamp-opts ; {:pattern _ :locale _ :timezone _}

   :output-fn default-output-fn ; (fn [data]) -> string

   :appenders
   {;; The standard println appender:
    ;; :println (println-appender {:stream :auto})

    :an-example-custom-println-appender
    ;; Inline appender definition (just a map):
    {:enabled?   true
     :async?     false
     :min-level  nil
     :rate-limit [[1 250] [10 5000]] ; 1/250ms, 10/5s
     :output-fn  :inherit
     :fn ; Appender's (fn [data]) -> side effects
     (fn [data]
       (let [{:keys [output_]} data
             formatted-output-str (force output_)]
         (println formatted-output-str)))}}})

A few things to note:

  • Appenders are trivial to write & configure - they're just fns. It's Timbre's job to dispatch useful args to appenders when appropriate, it's their job to do something interesting with them.
  • Being 'just fns', appenders have basically limitless potential: write to your database, send a message over the network, check some other state (e.g. environment config) before making a choice, etc.

Log levels and ns filters

The log level may be set:

  • At compile-time: (TIMBRE_LEVEL environment variable).
  • Statically using: timbre/set-level!/timbre/merge-level!.
  • Dynamically using: timbre/with-level.

The ns filters may be set:

  • At compile-time: (TIMBRE_NS_WHITELIST, TIMBRE_NS_BLACKLIST env vars).
  • Statically using: timbre/set-config!/timbre/merge-config!.
  • Dynamically using: timbre/with-config.

There are also variants of the core logging macros that take an explicit config arg:

(timbre/log*  <config-map> <level> <& args>) ; or
(timbre/logf* <config-map> <level> <& args>)

Logging calls excluded by a compile-time option (e.g. during Cljs compilation) will be entirely elided from your codebase, e.g.:

#!/bin/bash

# edn values welcome:
export TIMBRE_LEVEL=':warn'               # Elide all lower logging calls
export TIMBRE_NS_WHITELIST='["my-app.*"]' # Elide all other ns logging calls
export TIMBRE_NS_BLACKLIST='["my-app.foo" "my-app.bar.*"]'

lein cljsbuild once # Compile js with appropriate logging calls excluded
lein uberjar        # Compile jar ''

Built-in appenders

Basic file appender

;; (:require [taoensso.timbre.appenders.core :as appenders]) ; Add to ns

(timbre/merge-config!
  {:appenders {:spit (appenders/spit-appender {:fname "/path/my-file.log"})}})

;; (timbre/merge-config! {:appenders {:spit {:enabled? false}}} ; To disable
;; (timbre/merge-config! {:appenders {:spit nil}}               ; To remove entirely

Redis (Carmine) appender (v3+)

;; [com.taoensso/carmine <latest-version>] ; Add to project.clj deps
;; (:require [taoensso.timbre.appenders (carmine :as car-appender)]) ; Add to ns

(timbre/merge-config! {:appenders {:carmine (car-appender/carmine-appender)}})

This gives us a high-performance Redis appender:

  • All raw logging args are preserved in serialized form (even errors).
  • Configurable number of entries to keep per log level.
  • Only the most recent instance of each unique entry is kept.
  • Resulting log is just a Clojure value: a vector of log entries (maps).

Clojure has a rich selection of built-in and 3rd party tools for querying values like this.

See also car-appender/query-entries.

Email (Postal) appender

;; [com.draines/postal <latest-version>] ; Add to project.clj deps
;; (:require [taoensso.timbre.appenders (postal :as postal-appender)]) ; Add to ns

(timbre/merge-config!
  {:appenders
   {:postal
    (postal-appender/postal-appender
      ^{:host "mail.isp.net" :user "jsmith" :pass "sekrat!!1"}
      {:from "[email protected]" :to "[email protected]"})}})

Other included appenders

A number of 3rd-party appenders are included out-the-box here. Please see the relevant docstring for details. Thanks goes to the respective authors!

Just give me a shout if you've got an appender you'd like to have added.

Profiling

As of v4.6.0, Timbre's profiling features have been enhanced and exported to a dedicated profiling library called Tufte.

Timbre's old profiling features will be kept for backwards compatibility throughout v4.x, but future development will be focused exclusively on Tufte.

Tufte has out-the-box support for integration with Timbre, and migration is usually simple.

Sorry for the inconvenience!

This project supports the ClojureWerkz-logo goals

ClojureWerkz is a growing collection of open-source, batteries-included Clojure libraries that emphasise modern targets, great documentation, and thorough testing.

Contacting me / contributions

Please use the project's GitHub issues page for all questions, ideas, etc. Pull requests welcome. See the project's GitHub contributors page for a list of contributors.

Otherwise, you can reach me at Taoensso.com. Happy hacking!

- Peter Taoussanis

License

Distributed under the EPL v1.0 (same as Clojure).
Copyright © 2015-2016 Peter Taoussanis.

timbre's People

Contributors

ptaoussanis avatar emlyn avatar dsapala avatar noelweichbrodt avatar megayu avatar josh-tilles avatar maurolopes avatar michaelcameron avatar mikekap avatar mikesperber avatar nberger avatar rickmoynihan avatar rinx avatar ducky427 avatar rsslldnphy avatar ryfow avatar samuelotter avatar sbelak avatar devth avatar tvanhens avatar aperiodic avatar mopemope avatar rossajmcd avatar whodidthis avatar yuliu-mdsol avatar aeronotix avatar adamclements avatar alexander-yakushev avatar polymeris avatar chiaolun avatar

Watchers

Reut Sharabani avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.