Full functionality and documentation

parent a10a8331
......@@ -2,21 +2,8 @@
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
## [Unreleased]
### Changed
- Add a new arity to `make-widget-async` to provide a different widget shape.
## [0.1.1] - 2018-09-20
### Changed
- Documentation on how to make the widgets.
### Removed
- `make-widget-sync` - we're all async, all the time.
### Fixed
- Fixed widget maker to keep working when daylight savings switches over.
## 0.1.0 - 2018-09-20
## [0.0.1] - 2018-09-21
### Added
- Files from the new template.
- Widget maker public API - `make-widget-sync`.
- Wraps most common Timbre functions.
- Pre configured setup for development and NSD elk logging
# no.nsd.envelope
# Envelope
`Envelope` is a thin wrapper around
[`taoensso.timbre`](https://github.com/ptaoussanis/timbre) with built in
settings for logging to NSD's ELK stack and transparent interop with various
logging packages. It can be used from Clojure and ClojureScript.
## Value proposition
Choosing and configuring a logging framework on the JVM can be a headache.
Once a framework has been chosen, you need to consider the logging format. At
NSD we use an ELK stack to handle centralized logging, and we have a [common
schema](https://gitlab.nsd.no/nsd-commons/log-schema) defining how the log
entries should look.
This package has everything set up out of the box. No need to struggle with XML
files or letting your third party dependencies dictate what logging framework
to use. Just require `Envelope` and start logging.
A Clojure library designed to ... well, that part is up to you.
## Usage
First of all; If your project is a library, you probably don't want `Envelope`; Just
use [clojure/tools.logging](https://github.com/clojure/tools.logging) and let
your consumers worry about formatting and appenders and all that jazz.
If your project is an application, add `Envelope` to your dependency graph:
`no.nsd.envelope {:mvn/version 0.1.0}`
And use it like this:
(ns no.nsd.your-app
(:require [no.nsd.envelope :as log))
(defn -main
[& args]
(log/init! {:service-name "your-app" :service-version "1.0.0"}
(log/debug "Hello NSD ELK format")))
`Timbre`'s macros `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `report`,
`spy`, and `with-context` are exposed as top-level macros in `Envelope`.
### Setup
While Envelope will work without any configuration, you will probably want to
at least tell it about the name and version of your application. The `init!`
function takes an optional map with these values, in addition to other things
you might want to tweak.
Any field supported by `Timbre` can be set. Note that setting the `:output-fn`
and `timestamp-opts` fields will not have any effect when `:log-to-elk` is true
(which is the default). In addition to the `Timbre` fields, `Envelope` supports
the following fields:
{; Value for the serviceName field. Default: "Unknown"
:service-name "my-logging-app"
; Value for the serviceVersion field. Default "0.0.0"
:service-version "1.0.0"
; Whether you want to log to ELK. Default is true. When false you will get a simpler,
; easy to read text format with colorized stack traces.
:log-to-elk false
; Value for the serviceStack field. Default "development"
:service-stack "prod"
; Same as Tibres :log-level field, but with default value depending on :log-to-elk. If
; not set, it will be :info when :log-to-elk is true and :debug when :log-to-elk is false
:log-level :debug}
See the [Timbre
documentation](https://github.com/ptaoussanis/timbre#configuration) for
information about all the fields supported by `Timbre`.
You can change these settings at any time (runtime, no need to restart) by
passing a config map to `merge-config!`.:
user> (no.nsd.envelope/init! {:log-to-elk false :level :warn})
user> (no.nsd.envelope/info "Hi?")
user> (no.nsd.envelope/merge-config! {:level :debug})
{:log-to-elk false, :service-name "Unknown", :service-version "0.0.0", :console-time-pattern "HH:mm:ss.SSS", :level :debug}
user> (no.nsd.envelope/info "Hi?")
13:18:50.286 INFO Hi?
### Interop with other logging frameworks
If you depend on third party code that use `clojure/tools.logging`, `slf4j`, `jcl`,
`log4j` or `jul` for logging, those log entries will be consumed by `Envelope` and
injected into your log. You don't need to do anything for this to work.
*Note*: If any of your dependencies depends on an older
`slf4j-api` than "1.7.14", there might be problems.
### Context
`Timbre`'s `with-context` is exposed as a top-level symbol in `Envelope`. The
preset ELK format will output the `xRequestId` and `thread` fields when
`:x-request-id` or `:thread` are set in the context. The default non-elk
(console) format will print the x-request-id field when present. Consider the
(ns no.nsd.your-app
(:require [no.nsd.envelope :as log))
(defn get-number
(log/debug "Getting number")
(defn do-something
(log/debug "Doing something")
(+ 2 (get-number)))
(defn start
(log/debug "App started")
(log/with-context {:x-request-id "My request-id"
:thread (.getName (Thread/currentThread))}
(log/debug "App finished"))
(defn -main
[& args]
The above code will produce log entries like this:
10:45:13.855 DEBUG App started
10:45:13.856 [My request-id] DEBUG Doing something
10:45:13.857 [My request-id] DEBUG Getting number
10:45:13.857 DEBUG App finished
### More fancy usage
If you need to use any of the more esoteric functionality from `Timbre`, you can
just call its functions or macros:
(taoensso.timbre/logf :warn "Explicitly using the timbre logf macro")
There is no need to add a dependency to `Timbre`, since it is pulled in by
## License
Copyright © 2018 FIXME
Copyright © 2018 NSD
Distributed under the Eclipse Public License either version 1.0 or (at
your option) any later version.
{:paths ["resources" "src"]
:deps {org.clojure/clojure {:mvn/version "RELEASE"}}
{:test {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "RELEASE"}}}
{:extra-deps {com.cognitect/test-runner
{:git/url "https://github.com/cognitect-labs/test-runner"
:sha "76568540e7f40268ad2b646110f237a60295fa3c"}}
:main-opts ["-m" "cognitect.test-runner"
"-d" "test"]}}}
:deps {org.clojure/clojure {:mvn/version "RELEASE"}
org.clojure/data.json {:mvn/version "0.2.6"}
com.taoensso/timbre {:mvn/version "4.10.0"}
org.clojure/tools.logging {:mvn/version "0.4.1"}
com.fzakaria/slf4j-timbre {:mvn/version "0.3.12"}
org.slf4j/log4j-over-slf4j {:mvn/version "1.7.14"}
org.slf4j/jul-to-slf4j {:mvn/version "1.7.14"}
org.slf4j/jcl-over-slf4j {:mvn/version "1.7.14"}}
:aliases {:test {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "RELEASE"}}}
{:extra-deps {com.cognitect/test-runner
{:git/url "https://github.com/cognitect-labs/test-runner"
:sha "76568540e7f40268ad2b646110f237a60295fa3c"}}
:main-opts ["-m" "cognitect.test-runner"
"-d" "test"]}}}
(ns no.nsd.envelope)
(defn foo
"I don't do a whole lot."
(println x "Hello, World!"))
(ns no.nsd.envelope
(:require [clojure.string :as str]
[clojure.data.json :as json]
[taoensso.timbre :as timbre]
#?(:clj [taoensso.timbre.tools.logging :as cljlog])))
(defonce default-config {:log-to-elk true
:service-name "Unknown"
:service-version "0.0.0"
:console-time-pattern "HH:mm:ss.SSS"})
(defonce envelope-config (atom default-config))
(defn nsd-logstash-format-fn [config data]
"NSD format, compliant with https://gitlab.nsd.no/nsd-commons/log-schema"
(let [{:keys [level ?err msg_ ?ns-str ?file hostname_
timestamp_ ?line context]} data
output-data (cond->
{"@timestamp" (force timestamp_)
:serviceName (force (:service-name config))
:serviceVersion (force (:service-version config))
:schemaVersion (force "v3")
:levelName (str/upper-case (name level))
:message (force msg_)}
(some? (:service-stack config))
(assoc :serviceStack (force (:service-stack config)))
(some? (:x-request-id context))
(assoc :xRequestId (:x-request-id context))
(some? (:thread context))
(assoc :thread (:thread context))
(assoc :err (timbre/stacktrace ?err {:stacktrace-fonts {}})))]
(json/write-str output-data)))
(defn local-console-format-fn [data]
"A simpler log format, suitable for readable logs during development. colorized stacktraces"
(let [{:keys [level ?err msg_ ?ns-str ?file hostname_
timestamp_ ?line context]} data]
(str (force timestamp_)
(when (:x-request-id context)
(str " ["
(:x-request-id context)
" "
(str/upper-case (name level))
" "
(force msg_))
(str " "(timbre/stacktrace ?err)))))
(defn merge-config!
"Merge *conf* with the existing config map"
(let [current-conf (swap! envelope-config merge conf)
elk? (:log-to-elk current-conf)]
(timbre/merge-config! {:timestamp-opts (if elk?
{:pattern "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"}
{:pattern (:console-time-pattern current-conf)
:timezone :jvm-default})
:level (or (:level current-conf)
(if elk? :info :debug))
:output-fn (if elk?
(partial nsd-logstash-format-fn current-conf)
(defn init!
"Start the logging subsystem with default values merged with optional *conf*"
([] (init! {}))
#?(:clj (cljlog/use-timbre))
(merge-config! (merge default-config conf))
(timbre/debug "Logging envelope initialized")))
;; Expose Timbre macros
(def ^:private timbre-symbols
['trace 'debug 'info 'warn 'error 'fatal 'report 'spy 'with-context])
(defmacro timbre-alias ^:private [sym]
`(let [timbre-sym# (resolve (symbol (str "timbre/" ~sym)))]
(intern *ns* (with-meta ~sym (meta timbre-sym#)) (deref timbre-sym#))))
(doseq [sym timbre-symbols]
(timbre-alias sym))
(ns no.nsd.envelope-test
(:require [clojure.test :refer :all]
[no.nsd.envelope :refer :all]))
(deftest a-test
(testing "FIXME, I fail."
(is (= 0 1))))
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment