summaryrefslogtreecommitdiff
path: root/README.md
blob: d4a7bd8c980330d085e17085cf5f931f73378dff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# dmsg.el

Structured debug logging for Emacs.  Each `(dmsg ...)` call appends a
timestamped, levelled entry, with backtrace to a dedicated buffer and provides
a custom `dmsg-mode` to interact with it.



## Installation

```elisp
(add-to-list 'load-path "/path/to/dmsg/")
(require 'dmsg)
```



## Usage

```elisp
(dmsg "x is %S" x)               ; debug level (default)
(dmsg 'warn "unexpected: %=s" x)   ; explicit level
(dmsg 'error "failed: %s" msg)

;; %=X logs label=value automatically
(let ((state 'idle)
      (count 10))
  (dmsg "Status: %=S %=d" state count))
;; DBG [2026-01-01 12:00:00.123] [eval] States: state=idle count=42

;; Switch to buffer
(display-buffer dmsg-buffer-name)
```

Levels in increasing severity: `debug` `info` `warn` `error`.



## Buffer format

```
* LVL [YYYY-MM-DD HH:MM:SS.mmm] first line of message
 continuation line                    ;; one leading space per \n in message
(fn-name arg ...)                     ;; backtrace frame  (hidden by mode)
(fn-name ...)                         ;; unevaluated frame (hidden by mode)
```

A new entry begins at each `* LVL` line at column 0.



## Keys

| Key | Action |
|--|--|
| `<tab>` | Toggle compact `caller <- ... <- outer` chain for entry at point |
| `b` | Open detailed backtrace in a side window |
| `c` | Hide all current entries without erasing (toggle) |
| `e` | Erase buffer |
| `f` | Filter: show only entries matching a regexp |
| `s` | Snapshot visible entries to a timestamped `.log` file |
| `l1`-`l4` | Set minimum display level (1=debug 2=info 3=warn 4=error) |

The header line always shows `visible/total`, active filter, and level cutoff.

Function names in the compact chain, caller tag, and detailed backtrace are
all clickable (`mouse-1` / `RET`) and jump to the function's definition via
a single shared keymap.



## `%=` format specifier

`%=SPEC` expands to `label=value`.  The label is derived from the unevaluated
argument form (symbol name, or `prin1` for complex expressions). All other
format specifiers work as expected.

```elisp
(let ((buf "foo.el") (line 10))
  (dmsg "at %=s %=d" buf line))
;; at buf=foo.el line=10
```


## Visibility and filtering

All filtering hides entries via overlays.

- **`f`**  regexp filter on message text.
- **`c`**  hide all current entries. Press `c` again to restore.
- **`l 1` -- `l 4`**  set minimum shown level.
- **`dmsg-max-entries`** hides oldest entries beyond this count (`nil` = unlimited).



## Snapshot

`s` (`dmsg-snapshot`) prompts for a filename, defaulting to a timestamped
`dmsg-YYYYMMDD-HHMMSS.log` in the buffer's `default-directory`.  Navigating
to a different directory without specifying a name saves the file there under
the default name.  Only currently visible entries are written; the buffer is
not modified.


## Intercept `message`

```elisp
;; Route matching message() calls into the dmsg buffer at debug level
(dmsg-on-message "error\\|warning")
(dmsg-on-message nil)  ; disable
```

## Log errors from a function

```elisp
(dmsg-on-error 'my-function)          ; add (or toggle)
```

When active, errors from the function are logged at `error` level and
re-signalled normally, existing error handling is otherwise unaffected.