English | δΈζ
elog is a comprehensive, feature-rich logging system for Emacs Lisp, inspired by popular logging frameworks like Log4j (Java), Winston (Node.js), and loguru (Python). It provides a structured, flexible approach to logging in your Emacs packages and configurations.
- Multiple Log Levels: TRACE, DEBUG, INFO, WARNING, ERROR, FATAL - with configurable minimum level filtering
- Named Loggers: Create multiple loggers with unique names for identifying log sources
- Multiple Output Handlers: Output to buffers, files, or the message area (echo area)
- Customizable Format Patterns: Define your own log message format with various placeholders
- Context Support (MDC-like): Attach contextual data to log messages, similar to MDC in Log4j
- Global & Scoped Context: Set context at global, logger, or block level
- Automatic Buffer Rotation: Keep log buffer size manageable with automatic line pruning
- Colored Output: Visual distinction between log levels with customizable faces
- Caller Information: Optional inclusion of source function information
- Conditional Logging: Performance-optimized macros that skip evaluation when level is disabled
- Exception Logging: Convenient functions for logging errors and exceptions
- File Logging: Persistent logging to files with automatic directory creation
- Download
elog.elto your Emacs load path:
git clone https://github.com/Kinneyzhang/elog.git ~/.emacs.d/site-lisp/elog- Add to your Emacs configuration:
(add-to-list 'load-path "~/.emacs.d/site-lisp/elog")
(require 'elog)(use-package elog
:straight (:host github :repo "Kinneyzhang/elog"))(quelpa '(elog :fetcher github :repo "Kinneyzhang/elog"));; Create a logger
(setq my-logger (elog-logger :name "myapp" :level 'info))
;; Log messages at different levels
(elog-info my-logger "Application started")
(elog-warning my-logger "Configuration file not found, using defaults")
(elog-error my-logger "Failed to connect: %s" error-message)
;; View the log buffer
(elog-log-view my-logger)(elog-info my-logger "User %s logged in from %s" username ip-address)
(elog-debug my-logger "Processing %d items (%.2f%%)" count percentage)(elog-with-context my-logger '(:user-id "12345" :session "abc")
(elog-info my-logger "Starting transaction")
(elog-info my-logger "Transaction completed"))Elog provides six log levels, ordered from lowest to highest severity:
| Level | Priority | Description | Color |
|---|---|---|---|
trace |
0 | Fine-grained debugging information | Gray |
debug |
1 | Debugging information | Cyan |
info |
2 | General informational messages | Green |
warning |
3 | Warning messages for potentially harmful situations | Orange |
error |
4 | Error messages for error events | Red |
fatal |
5 | Severe error messages for critical failures | Red (bold, underlined) |
Messages are only logged if their level is equal to or higher than the logger's configured level.
;; Logger with 'warning level will only output warning, error, and fatal messages
(setq logger (elog-logger :name "example" :level 'warning))
(elog-debug logger "This won't appear") ; Below threshold
(elog-info logger "This won't appear") ; Below threshold
(elog-warning logger "This WILL appear") ; At threshold
(elog-error logger "This WILL appear") ; Above thresholdCreate a new logger with the specified configuration.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
:name |
string | "elog" |
Logger name/tag for identification in log messages |
:level |
symbol | 'debug |
Minimum log level (trace, debug, info, warning, error, fatal) |
:buffer |
string | "*elog*" |
Buffer name for buffer handler output |
:file |
string | nil |
File path for file handler output |
:format |
string | "%t| [%l] <%n> %m" |
Custom format pattern (see Format Patterns) |
:handlers |
list | '(buffer) |
List of output handlers (buffer, file, message) |
:context |
alist | nil |
Initial context key-value pairs |
Example:
;; Simple logger
(setq my-logger (elog-logger :name "myapp" :level 'info))
;; Logger with file output
(setq file-logger (elog-logger :name "app"
:level 'debug
:file "~/logs/app.log"
:handlers '(buffer file)))
;; Logger with custom format
(setq custom-logger (elog-logger :name "api"
:level 'info
:format "%T [%l] %n: %m"))Log a TRACE level message.
Log a DEBUG level message.
Log an INFO level message.
Log a WARNING level message.
Log an ERROR level message.
Log a FATAL level message.
Example:
(elog-info logger "User logged in")
(elog-error logger "Connection failed: %s" error-msg)
(elog-debug logger "Request: method=%s path=%s" method path)Set the minimum log level for the logger.
Set the output buffer name.
Set the output file path.
Set the log format pattern.
Set the list of output handlers.
Add a handler to the existing list.
Example:
;; Change level dynamically
(elog-set-level my-logger 'debug)
;; Add file logging to existing logger
(elog-set-file my-logger "~/logs/app.log")
(elog-add-handler my-logger 'file);; Set context for a logger
(elog-set-context logger '((user . "john") (session . "abc123")))
;; Add a single context key-value
(elog-add-context logger 'request-id "REQ-001");; Set global context (available to all loggers)
(elog-set-global-context 'app-version "2.0.0")
(elog-set-global-context 'environment "production")
;; Remove a global context key
(elog-remove-global-context 'app-version)
;; Clear all global context
(elog-clear-global-context);; Temporary context for a code block
(elog-with-context logger '(:transaction-id "TXN-999" :amount 100.00)
(elog-info logger "Starting transaction")
(elog-info logger "Processing payment")
(elog-info logger "Transaction complete"))
;; Context is automatically removed after the blockLog an exception/error at ERROR level.
Execute body and automatically log any errors.
Example:
;; Manual exception logging
(condition-case err
(risky-operation)
(error
(elog-exception my-logger err "While processing request")))
;; Automatic exception catching and logging
(elog-catch my-logger
(/ 1 0)) ; Error will be logged automaticallyThese macros avoid expensive computations when the log level is disabled:
(elog-when-debug logger
(elog-debug logger "Expensive debug info: %s"
(compute-expensive-debug-data)))
(elog-when-trace logger
(elog-trace logger "Very detailed trace: %s"
(generate-trace-report)))Available macros: elog-when-trace, elog-when-debug, elog-when-info, elog-when-warning, elog-when-error, elog-when-fatal
Switch to the logger's log buffer.
Clear all logs in the logger's buffer.
Quit the elog buffer and restore window configuration.
Check if a level is enabled for the logger.
Get the current log level of the logger.
Get the name of the logger.
Customize log message format using these placeholders:
| Placeholder | Description | Example |
|---|---|---|
%t |
Short timestamp (HH:MM:SS.mmm) | 14:30:45.123 |
%T |
Full timestamp (YYYY-MM-DD HH:MM:SS.mmm) | 2024-01-15 14:30:45.123 |
%l |
Log level (uppercase) | INFO |
%n |
Logger name | myapp |
%m |
Log message | User logged in |
%c |
Context data | user=john session=abc |
%f |
Source file name | app.el |
%L |
Source line number | 42 |
%F |
Source function name | my-function |
Format Examples:
;; Default format
"%t| [%l] <%n> %m"
;; Output: 14:30:45.123| [INFO] <myapp> User logged in
;; Full timestamp
"%T [%l] %n - %m"
;; Output: 2024-01-15 14:30:45.123 [INFO] myapp - User logged in
;; Minimal format
"[%l] %m"
;; Output: [INFO] User logged in
;; With context
"%t [%l] %n: %m {%c}"
;; Output: 14:30:45.123 [INFO] myapp: User logged in {user=john session=abc}
;; JSON-like format
"{\"time\":\"%T\",\"level\":\"%l\",\"logger\":\"%n\",\"msg\":\"%m\"}"| Variable | Default | Description |
|---|---|---|
elog-default-buffer |
"*elog*" |
Default buffer for log output |
elog-default-format |
"%t| [%l] <%n> %m" |
Default format pattern |
elog-max-buffer-lines |
10000 |
Max lines in buffer (nil = unlimited) |
elog-colorize |
t |
Enable colored output in buffers |
elog-include-caller |
nil |
Include caller function information |
Customize log level colors by modifying faces:
;; Change INFO level to blue
(set-face-attribute 'elog-info-face nil :foreground "blue")
;; Make WARNING level bold
(set-face-attribute 'elog-warning-face nil :weight 'bold)
;; Custom FATAL appearance
(set-face-attribute 'elog-fatal-face nil
:foreground "white"
:background "red"
:weight 'bold)Available faces:
elog-trace-faceelog-debug-faceelog-info-faceelog-warning-faceelog-error-faceelog-fatal-face
(setq http-logger (elog-logger :name "http-server"
:level 'info
:format "%T [%l] %n: %m"))
(elog-info http-logger "Received GET /api/users")
(elog-with-context http-logger '(:method "GET"
:path "/api/users"
:client-ip "192.168.1.100")
(elog-info http-logger "Authenticating request")
(elog-info http-logger "User authenticated: admin")
(elog-info http-logger "Returning 200 OK with 42 users"))
(elog-info http-logger "Request completed in 150ms");; Different loggers for different components with different levels
(setq db-logger (elog-logger :name "database" :level 'debug))
(setq auth-logger (elog-logger :name "auth" :level 'info))
(setq api-logger (elog-logger :name "api" :level 'warning))
(elog-debug db-logger "Executing query: SELECT * FROM users")
(elog-info auth-logger "User 'admin' authenticated")
(elog-warning api-logger "Rate limit approaching for client")(setq prod-logger (elog-logger :name "production"
:level 'warning ; Only warnings and above
:file "/var/log/myapp/app.log"
:format "%T [%l] %n: %m"
:handlers '(file)))
;; Set global context for all log entries
(elog-set-global-context 'hostname (system-name))
(elog-set-global-context 'pid (emacs-pid))
(elog-error prod-logger "Critical database connection lost")(defun my-app-init-logging ()
"Initialize logging for my application."
;; Set global context
(elog-set-global-context 'app "MyApp")
(elog-set-global-context 'version "2.0.0")
;; Create main application logger
(setq my-app-logger
(elog-logger :name "myapp"
:level (if my-app-debug-mode 'debug 'info)
:file (expand-file-name "myapp.log" user-emacs-directory)
:handlers '(buffer file)))
(elog-info my-app-logger "Application initialized"))
(defun my-app-shutdown ()
"Shutdown the application."
(elog-info my-app-logger "Shutting down...")
(elog-clear-global-context))-
Check that the log level is high enough:
(elog-get-level my-logger) ; Returns current level (elog-level-p my-logger 'debug) ; Check if debug is enabled
-
Verify handlers are configured:
(plist-get my-logger :handlers)
- Ensure the directory exists (elog creates directories automatically)
- Check file permissions
- Verify the file path:
(plist-get my-logger :file)
If logs are disappearing, check elog-max-buffer-lines:
(setq elog-max-buffer-lines nil) ; Disable rotationContributions are welcome! Please feel free to submit issues and pull requests.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
Kinney Zhang
- GitHub: @Kinneyzhang
Made with β€οΈ for the Emacs community