{"id":15776084,"url":"https://github.com/avisonovate/logging","last_synced_at":"2025-07-14T17:08:36.376Z","repository":{"id":35750040,"uuid":"40029196","full_name":"AvisoNovate/logging","owner":"AvisoNovate","description":"Easy setup of clojure.tools.logging w/ SLF4j, plus request correlation","archived":false,"fork":false,"pushed_at":"2021-05-19T00:00:14.000Z","size":30,"stargazers_count":27,"open_issues_count":0,"forks_count":3,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-10-11T17:15:00.797Z","etag":null,"topics":["clojure","correlation","logging"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AvisoNovate.png","metadata":{"files":{"readme":"README.asciidoc","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-31T23:47:46.000Z","updated_at":"2023-10-23T18:13:51.000Z","dependencies_parsed_at":"2022-09-06T09:51:30.125Z","dependency_job_id":null,"html_url":"https://github.com/AvisoNovate/logging","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AvisoNovate%2Flogging","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AvisoNovate%2Flogging/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AvisoNovate%2Flogging/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AvisoNovate%2Flogging/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AvisoNovate","download_url":"https://codeload.github.com/AvisoNovate/logging/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250797796,"owners_count":21488974,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","correlation","logging"],"created_at":"2024-10-04T17:04:50.029Z","updated_at":"2025-04-25T10:28:04.781Z","avatar_url":"https://github.com/AvisoNovate.png","language":"Clojure","readme":"## Logging - Clojure logging with Logback and SLF4J plus request correlation across servers\n\nimage:https://img.shields.io/clojars/v/io.aviso/logging.svg[Clojars Project, link=\"http://clojars.org/io.aviso/logging\"]\n\nimage:https://circleci.com/gh/AvisoNovate/logging/tree/master.svg?style=svg[\"CircleCI\", link=\"https://circleci.com/gh/AvisoNovate/logging/tree/master\"]\n\nLogging is available under the terms of the Apache Software License 2.0.\n\nlink:http://avisonovate.github.io/docs/logging/[API Documentation]\n\n## Dependencies\n\nMostly, we set up the dependencies so that \nlink:https://github.com/clojure/tools.logging[clojure.tools.logging], \nuses link:http://www.slf4j.org/[SLF4J] and \nlink:http://logback.qos.ch/[Logback].\n\nWe also include the link:http://www.slf4j.org/legacy.html#jclOverSLF4J[shim library] \nthat converts \nlink:http://commons.apache.org/proper/commons-logging/[Apache Commons Logging] to use SLF4J as well.\n\n## Setup\n\nThe namespace io.aviso.logging.setup performs all the necessary setup; it installs\nlink:https://github.com/AvisoNovate/pretty[pretty exception reporting] for console output\nand commons logging output, as well as a default uncaught exception handler (that logs the \nuncaught exception).\n \nSimply have some part of your application require this namespace. \n\n## Request Correlation\n \nEver try to trace a request from a client through a cluster of services?\nEven if you can get all the logs in one place, how do you pick out the single thread of\nrequests and responses from all the other requests and responses?\n\nWith request correlation, each request gets assigned a correlation id on entry to the system.\nWhen one server sends a request to another server, it includes the correlation id as a header\non the request.\n\nThat, plus a little bit of logic in your Logback configuration, allows every request to be flagged.\n\n### Request Correlation Middleware\n\nThe function io.aviso.logging.correlation/wrap-with-request-correlation is a Ring middleware function;\nit is passed a Ring request handler and wraps it.\n\nOn each request, the request correlation id is extracted, or generated if not present.\nBy default, the correlation id comes from the Correlation-Id header, and is generated\nas a random UUID if not present.\n\nIn addition, the io.aviso.logging.correlation/\\*correlation-id* Var is bound to the correlation id.\nThis little bit of semi-global state can be used when making requests to other servers,\nto include the correlation id in the request.\n\n### Logging the Correlation Id\n\nThe final piece of the puzzle is to get the correlation id logged. \nthis is accomplished by making the correlation id a\nlink:http://logback.qos.ch/manual/mdc.html[mapped diagnostic context].\n\nIn your logback.xml (or logback-test.xml):\n\n\n[source,xml]\n----\n    \u003cappender name=\"SETUP-CORR-ID\" class=\"io.aviso.logging.CorrelationIdAppender\"/\u003e\n\n    \u003cappender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\"\u003e\n        \u003cencoder\u003e\n            \u003cpattern\u003e%date{HH:mm:ss.SSS} %mdc{correlation-id} %-5level [%thread] %logger{} - %msg%n\u003c/pattern\u003e\n        \u003c/encoder\u003e\n    \u003c/appender\u003e\n\n    \u003croot level=\"info\"\u003e\n        \u003cappender-ref ref=\"SETUP-CORR-ID\"/\u003e\n        \u003cappender-ref ref=\"STDOUT\"/\u003e\n    \u003c/root\u003e\n----\n\nThe CorrelationIdAppender is used to set up the correlation id into the MDC (the mapped\ndiagnostic context).\nAfter that, the `%mdc{correlation-id}` form can extract that value and include it in the\nlogged output.\n\n## Extra MDC\n\nVersion 0.2.0 adds a generalization of request correlation; an arbitrary map of keys and values\ncan be added to the MDC.\n\nThis takes the form of a new appender class, io.aviso.logging.ExtraMDCAppender:\n\n[source,xml]\n----\n    \u003cappender name=\"SETUP-MDC\" class=\"io.aviso.logging.ExtraMDCAppender\"/\u003e\n\n    \u003cappender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\"\u003e\n        \u003cencoder\u003e\n            \u003cpattern\u003e%date{HH:mm:ss.SSS} %mdc{authenticated} %-5level [%thread] %logger{} - %msg%n\u003c/pattern\u003e\n        \u003c/encoder\u003e\n    \u003c/appender\u003e\n\n    \u003croot level=\"info\"\u003e\n        \u003cappender-ref ref=\"SETUP-MDC\"/\u003e\n        \u003cappender-ref ref=\"STDOUT\"/\u003e\n    \u003c/root\u003e\n----\n\nIn the above example, authenticated is presumably a value that may be bound into the\n`*extra-mdc*` var.\n\nA macro, `with-mdc`, makes it easy to bind new keys and values into the extra MDC.\n\nLogback's message diagnostic map is mutable and designed to be inherited by\nsub-threads in a way that overlaps with Clojure's inheriting of dynamic vars.\nBecause of this, a value stored into the MDC is \"sticky\" and may leak into\nlater logging on the thread, even after exiting the `with-mdc` block.\n\nThe best solution for this is to *always* set a default for any key\nthat may be added inside a `with-mdc` block:\n\n[source,clojure]\n----\n(require '[io.aviso.logging.mdc :refer [set-mdc-default with-mdc]\n         '[clojure.tools.logging :as l])\n\n(set-mdc-default {:authenticated \"none\"})\n\n(with-mdc {:authenticated \"http\"}\n   ...\n   (l/info \"Handling request\")\n   ... )\n----\n\nAlthough the \"authenticated\" key will have value \"https\"\nin the Logback MDC even after exiting from the `with-mdc` block,\nany subsequent logging will reset it back to the default, \"none\".\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favisonovate%2Flogging","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Favisonovate%2Flogging","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favisonovate%2Flogging/lists"}