{"id":17760280,"url":"https://github.com/fuco1/emacs-lgr","last_synced_at":"2025-08-02T20:33:16.595Z","repository":{"id":142585031,"uuid":"612294370","full_name":"Fuco1/emacs-lgr","owner":"Fuco1","description":"A fully featured logging framework for Emacs","archived":false,"fork":false,"pushed_at":"2024-05-16T10:33:30.000Z","size":114,"stargazers_count":21,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-01T13:15:21.500Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Fuco1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-10T16:09:31.000Z","updated_at":"2025-02-06T20:26:46.000Z","dependencies_parsed_at":"2024-12-14T06:25:44.903Z","dependency_job_id":"aa80c3dc-8b13-4221-9078-a7fae787b497","html_url":"https://github.com/Fuco1/emacs-lgr","commit_stats":{"total_commits":29,"total_committers":1,"mean_commits":29.0,"dds":0.0,"last_synced_commit":"20f3a3db9a142e86a15d14597213de23d01b772c"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Fuco1/emacs-lgr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fuco1%2Femacs-lgr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fuco1%2Femacs-lgr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fuco1%2Femacs-lgr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fuco1%2Femacs-lgr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Fuco1","download_url":"https://codeload.github.com/Fuco1/emacs-lgr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fuco1%2Femacs-lgr/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268448362,"owners_count":24252019,"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","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-10-26T19:01:08.114Z","updated_at":"2025-08-02T20:33:16.562Z","avatar_url":"https://github.com/Fuco1.png","language":"Emacs Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lgr\n\nlgr is a logging package for Emacs built on the back of\n[EIEIO](https://www.gnu.org/software/emacs/manual/html_node/eieio/)\nclasses. It is designed to be flexible, performant, and\nextensible.\n\n# Features\n\n- *Hierarchical loggers* like in log4j and python logging. This is\n  useful if you want to be able to configure logging on a per-package\n  basis.\n- An *arbitrary number of appenders* for each logger. A single logger\n  can write to the console, a logfile, a database, etc… .\n- Support for structured logging. As opposed to many other logging\n  packages for Emacs a log event is not just a message with a\n  timestamp, but an object that can contain arbitrary data\n  fields. This is useful for producing machine readable logs.\n- *Lazy evaluated* arguments for log messages.  If the log event level\n  is above the threshold, arguments won't be evaluated to save time.\n- Appenders that write logs to a wide range of destinations:\n  - minibuffer via `message`,\n  - standard output with `princ`,\n  - plaintext files (with a powerful formatting syntax),\n  - JSON files with arbitrary data fields,\n  - ... or your own custom appender.\n\n# Usage\n\n## How do I log!\n\nTo log an *event* with lgr, we call `(lgr-LEVEL lgr \u003cmessage\u003e)`.  Rest\nof the arguments to the logging function are interpreted by `format`\nuntil all the format control sequences are replaced, then the rest is\nstored as arbitrary event metadata.\n\nTo get a `lgr` logger object, call `(lgr-get-logger \"logger-name\")`.\nLogger name is an arbitrary string, but should somehow correspond to\nyour package's name.\n\n```emacs-lisp\n(lgr-fatal lgr \"A critical error\")\n;=\u003e [2023-03-11T01:24:49+0000] (fatal) A critical error\n(lgr-error lgr \"A less severe error\")\n;=\u003e [2023-03-11T01:24:49+0000] (error) A less severe error\n(lgr-warn lgr \"A potentially bad situation\")\n;=\u003e [2023-03-11T01:24:49+0000] (warn) A potentially bad situation\n(lgr-info lgr \"iris has %s rows\" (nrow iris))\n;=\u003e [2023-03-11T01:24:49+0000] (info) iris has 150 rows\n\n; the following log levels are hidden by default\n(lgr-debug lgr \"A debug message\")\n(lgr-trace lgr \"A finer grained debug message\")\n```\n\nLoggers **should never** be created manually but only be retrieved\nusing `lgr-get-logger`.  If a logger with the same name already\nexists, it will be returned from a cache.  The common idiom is to\nlet-bind a logger at the beginning of a function and then use it\nthroughout the function.\n\n``` emacs-lisp\n(defun start-worker (worker-id)\n  (let ((lgr (lgr-get-logger \"package.worker\")))\n    (lgr-info lgr \"Starting worker %d\" worker-id)\n    ...))\n\n(defun main ()\n  (let ((lgr (lgr-get-logger \"package\")))\n    (lgr-info lgr \"Starting the package main event loop\")\n    (start-worker 1)\n    (start-worker 2)))\n```\n\nYou can of course use multiple loggers in a single function by\nlet-binding multiple calls to `lgr-get-logger` (or even use them\ninline).\n\n## Give me 3 minute rundown of configuration\n\nLogging an event by itself wont store it anywhere, for that, the\nlogger must be configured with an appended.  A Logger can have several\nappenders to write to multiple destinations.\n\nFor example, we can add a file appender to format events as JSONs and\nsave them to file.  To do this, we need to configure two settings:\n\n- add the JSON layout to the appender so it knows how to format the\n  events before writing them to the file.\n- attach this appender to the logger object `lgr`\n\nConfiguration is very convenient with the usage of the `-\u003e` macro from\nthe\n[dash.el](https://github.com/magnars/dash.el#--x-optional-form-rest-more)\npackage, but can be equally done without.\n\n``` emacs-lisp\n(-\u003e lgr\n    (lgr-add-appender\n     (-\u003e (lgr-appender-file :file \"json-logs.log\")\n         (lgr-set-layout (lgr-layout-json)))))\n\n;; same code macro-expanded\n(lgr-add-appender\n lgr\n (lgr-set-layout\n  (lgr-appender-file :file \"json-logs.log\")\n  (lgr-layout-json)))\n```\n\nThe `-\u003e` style resembles the method \"dot chaining\" from traditional\nOOP languages like Java or C++.  To make this possible, we make sure\nthat all the configuration methods always take the instance as the\nfirst argument and return itself so they can be chained:\n\n``` emacs-lisp\n(-\u003e lgr\n    (lgr-add-appender (lgr-appender-princ))\n    (lgr-set-threshold lgr-level-trace)\n    (lgr-set-propagate nil))\n```\n\n## Logger hierarchies\n\nLoggers are organized in hierarchies.  The loggers are automatically\nnested by separating the segments of the name with a dot:\n\n``` emacs-lisp\n(lgr-get-logger \"lgr\")\n(lgr-get-logger \"lgr.appender\")\n(lgr-get-logger \"lgr.layout\")\n\n;; lgr\n;; ├─ appender\n;; └─ layout\n```\n\nLoggers *propagate* events up the hierarchy unless configured not to\nwith `lgr-set-propagate`.\n\nThe most common situation is to configure appenders only on top-level\nlogger and let events bubble up and be processed there.  But if an\nappender is added to some logger lower in the hierarchy, an event can\nbe dispatched twice or more times.\n\nUse `M-x lgr-loggers-format-to-tree` to visualize the logger\nhierarchy.  The results are displayed in a `*lgr loggers*` buffer:\n\n```\nlgr logger hierarchy\n====================\n\n🔇 Loggers without appenders\n\n🔇 lgr--root [info]\n├─ elsa [info] \u003e Princ\n│  └─ lsp\n├─ 🔇 lgr\n│  ├─ 🔇 appender\n│  └─ 🔇 layout\n├─ local \u003e Warnings\n│  ├─ error [error]\n│  └─ test\n│     ├─ one\n│     └─ two\n└─ 🔇 test [error]\n```\n\n## Configuring thresholds\n\nLoggers and appenders can both be configured independently with\nthresholds.\n\nCurrently, six levels are built-in in `lgr`:\n\n- `fatal` =\u003e 100 or constant `lgr-level-fatal`\n- `error` =\u003e 200 or constant `lgr-level-error`\n- `warn` =\u003e 300 or constant `lgr-level-warn`\n- `info` =\u003e 400 or constant `lgr-level-info` **[default]**\n- `debug` =\u003e 500 or constant `lgr-level-debug`\n- `trace` =\u003e 600 or constant `lgr-level-trace`\n\nA logger won't emit an event whose level is higher than the logger\nthreshold.\n\nAn appender won't append an event whose level is higher than the\nappender threshold.\n\nThis way, we can create interesting setups such as:\n\nConfigure one logger with two appenders, one for file logging and one\nsending emails.  We configure the file appender to debug threshold and\nthe email appender to error threshold.\n\nIf the logger itself has an info threshold, only events info and above\nwill be emited.  All those will be saved in the file, because the file\nappender has debug threshold.  But only fatal and error events will be\nsent as emails to an SRE operator.\n\nIf a logger has no configured threshold, it will look up the logger\nhierarchy to inherit the threshold of first configured logger.  This\nway, you can selectively increase or decrease the log granularity of\nparts of the logger hierarchy when debugging specific parts of code.\n\nThresholds are configured with `lgr-set-threshold` method:\n\n``` emacs-lisp\n(-\u003e (lgr-get-logger \"lgr\")\n    (lgr-set-threshold lgr-level-debug))\n```\n\n## Event metadata\n\nBy passing additional key-value pairs in form of a plist, you can add\narbitrary metadata to your events.\n\n``` emacs-lisp\n(lgr-info lgr \"This is a message number %d\" 5 :worker-id \"west-eu-7\" :datacenter \"dc1\")\n```\n\nVarious layouts handle the formatting of metadata differently, you can\nread in their documentation.  For example, JSON layout will serialize\nit as JSON subobject under a `meta` key.\n\n# Available Loggers, Appenders and Layouts\n\nlgr comes with many appenders and layouts out of the box.  You can\nread the built-in documentation with `C-h f \u003cclass-name\u003e`.\n\nCurrently implemented loggers:\n\n- `lgr-logger` - log message as-is\n- `lgr-logger-format` - interpret message as format string for\n  `format`, using remaining arguments as replacement.\n\nThe `lgr-logger-format` is the default format returned by\n`lgr-get-logger`.\n\nCurrently implemented appenders:\n\n- `lgr-appender` - print events using `message`\n- `lgr-appender-princ` - print events using `princ` (standard output in `-batch`)\n- `lgr-appender-file` - write events to a file\n- `lgr-appender-buffer` - write events to a buffer\n- `lgr-appender-warnings` - use `display-warning` to log events\n- `lgr-appender-journald` - write logs to systemd journal\n\nAvailable layouts:\n\n- `lgr-layout-format` - use custom format string template to format events\n- `lgr-layout-json` - format as JSON string\n\n# But isn't it going to slow down my code?\n\nNo.  `lgr` uses macros to implement lazy evaluation of the arguments.\nIf the logger threshold doesn't exceed the event level, no arguments\nto the `lgr-LEVEL` call are actually evaluated (except the logger\nitself which needs to be checked).\n\nThis is why it is not advisable to use `lgr-log` directly but instead\nalways use the `lgr-LEVEL` macros.\n\n# Extending lgr\n\nThe main idea of `lgr` is to make it easily extensible by adding your\nown layouts and appenders.\n\nHere is an example appender used in `lgr`'s own test suite.  It simply\npushes the events to an internal list.\n\n``` emacs-lisp\n(defclass lgr-test-appender\n  ;; extend `lgr-appender' class\n  (lgr-appender)\n  ;; add a new slot to store the events\n  ((events :type list :initform nil)))\n\n;; implement the `log-append' method\n(cl-defmethod lgr-append ((appender lgr-test-appender) (event lgr-event))\n  (push event (oref appender events)))\n```\n\n\nHere is a more interesting example of an appender using\n[emacs-async](https://github.com/jwiegley/emacs-async/pull/167) to\nsend messages from worker processes to the main process:\n\n``` emacs-lisp\n(defclass elsa-worker-appender (lgr-appender) ()\n  \"Appender sending messages back to parent process.\")\n\n(cl-defmethod lgr-append ((this elsa-worker-appender) event)\n  (when async-in-child-emacs\n    (async-send\n     :op \"echo\"\n     :message (lgr-format-event (oref this layout) event)))\n  this)\n\n;; configure the logging in a worker process\n(-\u003e (lgr-get-logger \"elsa\")\n    (lgr-reset-appenders)\n    (lgr-add-appender\n     (-\u003e (elsa-worker-appender)\n         (lgr-set-layout (elsa-plain-layout))))\n    (lgr-set-threshold lgr-level-info))\n```\n\n(as seen in [Elsa](https://github.com/emacs-elsa/Elsa))\n\nThis example shows the power of `lgr`.  We can keep the same\n`lgr-info` and `lgr-debug` calls everywhere and based on the\nconfiguration in either the main process or the worker process\ndifferent appender will be used to dispatch the messages where they\nneed to go.  Therefore, the logging logic, destinations and formatting\nare separate from the logging *calls*.\n\n# Using lgr in my own package.\n\nBecause all the packages loaded in Emacs share the common namespace,\nthere are some basic guidelines for using lgr in your own private or\npublished packages:\n\n- The main logger name should correspond to your package name.\n- All the loggers you use in the package should be nested under your main logger.\n- If your package is used inside Emacs, you should provide some\n  reasonable default configuration, for example in the major-mode\n  function or as a separate function `PACKAGE-setup-lgr` that users\n  can call in their init file.\n\nThat's it!.  This way, *consumers* of your package can independently\nof you as the author increase or decrease or even completely disable\nlogging in your package.\n\n# Acknowledgement\n\nThis library's architecture was inspired in great deal by\n[s-fleck/lgr](https://github.com/s-fleck/lgr) package for R language,\nwhich in turn is modelled after [python\nlogging](https://docs.python.org/3/library/logging.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuco1%2Femacs-lgr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffuco1%2Femacs-lgr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuco1%2Femacs-lgr/lists"}