Skip to content

Commit 9da9863

Browse files
committed
Add logging.lua, documentation, examples etc.
1 parent c76e66b commit 9da9863

21 files changed

+861
-0
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
test:
2+
$(MAKE) --directory=examples test

README.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Pandoc lua logging
2+
3+
This library provides pandoc-aware functions for dumping and logging lua objects. It can be used standalone but is primarily intended for using within pandoc lua filters.
4+
5+
# Getting started
6+
7+
Put `logging.lua` somewhere where pandoc can access it, e.g., in the same directory as your lua filters. Then try this `simple.lua` filter:
8+
9+
```lua
10+
local logging = require 'logging'
11+
12+
function Pandoc(pandoc)
13+
logging.temp('pandoc', pandoc)
14+
end
15+
```
16+
17+
...with this `simple.md` input:
18+
19+
```markdown
20+
text
21+
```
22+
23+
...to get this output on `stderr`:
24+
25+
```text
26+
(#) pandoc Pandoc {
27+
blocks: Blocks {
28+
[1] Para {
29+
content: Inlines {
30+
[1] Str text: "text"
31+
}
32+
}
33+
}
34+
meta: Meta {}
35+
}
36+
```
37+
38+
The `logging.temp()` function is intended for temporary debug output (hence its name) and always generates output. You can use `logging.error()`, `logging.warning()`, `logging.info()` etc. to generate output that's conditional on the log level.
39+
40+
The initial log level is derived from the pandoc `--quiet`, `--verbose` and `--trace` command-line options. By default (i.e., if none of these options are specified) the log level is `0` and only errors and warnings will be output.
41+
42+
With this `para.lua` filter:
43+
44+
```lua
45+
local logging = require 'logging'
46+
47+
function Para(para)
48+
logging.info('para', para)
49+
end
50+
```
51+
52+
...by default there's no output:
53+
54+
```text
55+
% pandoc simple.md -L para.lua >/dev/null
56+
```
57+
58+
...but `--verbose` sets the log level to `1` (`info`):
59+
60+
```text
61+
% pandoc simple.md -L para.lua >/dev/null --verbose
62+
[INFO] Running filter para.lua
63+
(I) para Para {, content: Inlines {[1] Str text: "text"}}
64+
[INFO] Completed filter para.lua in 8 ms
65+
```
66+
67+
All lua objects can be passed to `logging.info()` etc., and they will be output in a form that should be useful for lua filter development and debugging. The output is intended to be a faithful representation of the [pandoc lua types](https://pandoc.org/lua-filters.html#module-pandoc) and should make it easy to "dig down". For example, you can see that:
68+
69+
* `para` is a [Para](https://pandoc.org/lua-filters.html#type-para) instance
70+
* `para.content` is an [Inlines](https://pandoc.org/lua-filters.html#type-inlines) instance
71+
* `para.content[1]` is a [Str](https://pandoc.org/lua-filters.html#type-str) instance
72+
* `para.content[1].text` is a string
73+
74+
...and you could reference all of these directly in the filter. For example, with this `para2.lua` filter:
75+
76+
```lua
77+
local logging = require 'logging'
78+
79+
function Para(para)
80+
logging.info('para', para)
81+
logging.info('para.content', para.content)
82+
logging.info('para.content[1]', para.content[1])
83+
logging.info('para.content[1].text', para.content[1].text)
84+
end
85+
```
86+
87+
...you get this:
88+
89+
```text
90+
% pandoc simple.md -L para2.lua >/dev/null --verbose
91+
[INFO] Running filter para2.lua
92+
(I) para Para {, content: Inlines {[1] Str text: "text"}}
93+
(I) para.content Inlines {[1] Str text: "text"}
94+
(I) para.content[1] Str text: "text"
95+
(I) para.content[1].text text
96+
[INFO] Completed filter para2.lua in 8 ms
97+
```
98+
99+
# Module contents
100+
101+
## logging.type(value)
102+
103+
Returns whatever [`pandoc.utils.type()`](https://pandoc.org/lua-filters.html#pandoc.utils.type) returns, modified as follows:
104+
105+
* Spaces are replaced with periods, e.g., `pandoc Row` becomes `pandoc.Row`
106+
* `Inline` and `Block` are replaced with the corresponding `tag` value, e.g. `Emph` or `Table`
107+
108+
## logging.dump(value [, maxlen])
109+
110+
Returns a pandoc-aware string representation of `value`, which can be an arbitrary lua object.
111+
112+
The returned string is a single line if not longer than `maxlen` (default `70`), and is otherwise multiple lines (with two character indentation). The string is not terminated with a newline.
113+
114+
Map keys are sorted alphabetically in order to ensure that output is repeatable.
115+
116+
117+
118+
See the *Getting started* section for examples, and note that
119+
120+
## logging.output(...)
121+
122+
Pass each argument to `logging.dump()` and output the results to `stderr`, separated by single spaces and terminated (if necessary) with a newline.
123+
124+
Note: Actually (as a slight optimization) only `table` and `userdata` arguments are passed to `logging.dump()`. Other arguments are passed to the built-in `tostring()` function.
125+
126+
## logging.loglevel
127+
128+
Integer log level, which controls which of `logging.error()`, `logging.warning()`, `logging.info()` will generate output when called.
129+
130+
* `-2` : (or less) suppress all logging (apart from `logging.temp()`)
131+
* `-1` : output only error messages
132+
* `0` : output error and warning messages
133+
* `1` : output error, warning and info messages
134+
* `2` : output error, warning, info and debug messages
135+
* `3` : (or more) output error, warning, info, debug and trace messages
136+
137+
The initial log level is `0`, unless the following pandoc command-line options are specified:
138+
139+
* `--trace` : `3` if `--verbose` is also specified; otherwise `2`
140+
* `--verbose` : `1`
141+
* `--quiet` : `-1`
142+
143+
## logging.setloglevel(loglevel)
144+
145+
Sets the log level and returns the previous level.
146+
147+
Calling this function is preferable to setting `logging.loglevel` directly.
148+
149+
## logging.error(...)
150+
151+
If the log level is >= `-1`, calls `logging.output()` with `(E)` and the supplied arguments.
152+
153+
## logging.warning(...)
154+
155+
If the log level is >= `0`, calls `logging.output()` with `(W)` and the supplied arguments.
156+
157+
## logging.info(...)
158+
159+
If the log level is >= `1`, calls `logging.output()` with `(I)` and the supplied arguments.
160+
161+
## logging.debug(...)
162+
163+
If the log level is >= `2`, calls `logging.output()` with `(D)` and the supplied arguments.
164+
165+
## logging.trace(...)
166+
167+
If the log level is >= `3`, calls `logging.output()` with `(T)` and the supplied arguments.
168+
169+
## logging.temp(...)
170+
171+
Unconditionally calls `logging.output()` with `(#)` and the supplied arguments.

examples/Makefile

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
DIFF ?= diff --strip-trailing-cr --unified
2+
LUA ?= lua
3+
PANDOC ?= pandoc
4+
5+
STANDALONE = dumpex.lua levels.lua
6+
FILTER = para.lua para2.lua simple.lua table.lua
7+
8+
# make COMPARE= to run tools without redirection or comparison
9+
# make SNAPSHOT=true to create .expected files
10+
ifneq "$(SNAPSHOT)" ""
11+
COMPARE = 2>$*.expected >/dev/null
12+
else
13+
COMPARE = 2>&1 >/dev/null | $(DIFF) $*.expected -
14+
endif
15+
16+
test: test-standalone test-filter
17+
18+
test-standalone: $(STANDALONE:%=test-standalone-%)
19+
20+
test-filter: $(FILTER:%=test-filter-%)
21+
22+
test-standalone-%: %
23+
$(LUA) $* $(COMPARE)
24+
25+
test-filter-%: %
26+
$(PANDOC) $(wildcard *.md) -L $* $(COMPARE)

examples/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Pandoc lua logging examples
2+
3+
This directory contains example lua files (standalone and pandoc filters) and some sample markdown files.
4+
5+
These files were used when generating the examples in the top-level README file.
6+
7+
There's also a `Makefile` that runs lua and pandoc (as appropriate) and compares the output with the expected output.

examples/basic.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
foo: '*emph*'
3+
goo: '[underline]{.underline}'
4+
---
5+
6+
# Header {#header .new-page a=b}
7+
8+
Paragraph with *emphasized* and **bold** text.
9+
10+
```
11+
Code
12+
```
13+
14+
* and a list.

examples/dumpex.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
local logging = require 'logging'
2+
3+
local function output(value)
4+
io.stderr:write(value .. '\n')
5+
end
6+
7+
output(logging.dump(nil))
8+
output(logging.dump(1))
9+
output(logging.dump(false))
10+
output(logging.dump('string'))
11+
output(logging.dump({}))
12+
output(logging.dump({1, 2, 3}))
13+
output(logging.dump({a=1, b=2, c=3}))
14+
output(logging.dump({a=1, b=2, c=3, 4, 5}))
15+
output(logging.dump({a=1, b=2, c=3, 4, 5, 6, 7, 8, 9, 10}))
16+
output(logging.dump({a=1, b=2, c=3, 4, 5, 6, 7, 8, 9, 10, 11}))
17+
output(logging.dump({1, 2, {3, 4, {a=1, b=2, c=3, d=4, e=5, f=6}}}))

examples/dumpex.lua.expected

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
nil
2+
1
3+
false
4+
"string"
5+
{}
6+
{[1] 1, [2] 2, [3] 3}
7+
{a: 1, b: 2, c: 3}
8+
{[1] 4, [2] 5, a: 1, b: 2, c: 3}
9+
{[1] 4, [2] 5, [3] 6, [4] 7, [5] 8, [6] 9, [7] 10, a: 1, b: 2, c: 3}
10+
{
11+
[1] 4
12+
[2] 5
13+
[3] 6
14+
[4] 7
15+
[5] 8
16+
[6] 9
17+
[7] 10
18+
[8] 11
19+
a: 1
20+
b: 2
21+
c: 3
22+
}
23+
{
24+
[1] 1
25+
[2] 2
26+
[3] {
27+
[1] 3
28+
[2] 4
29+
[3] {
30+
a: 1
31+
b: 2
32+
c: 3
33+
d: 4
34+
e: 5
35+
f: 6
36+
}
37+
}
38+
}

examples/levels.lua

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
local logging = require 'logging'
2+
3+
if #arg > 0 then
4+
logging.setloglevel(tonumber(arg[1]) or 0)
5+
end
6+
7+
logging.temp('loglevel is', logging.loglevel, '(this is always output)')
8+
logging.error('this is output if loglevel >= -1')
9+
logging.warning('this is output if loglevel >= 0')
10+
logging.info('this is output if loglevel >= 1')
11+
logging.debug('this is output if loglevel >= 2')
12+
logging.debug2('this is output if loglevel >= 3')
13+
14+
logging.temp('args', 'are', 'automatically', 'separated', 'with',
15+
'spaces and a newline is added if necessary')
16+
logging.temp('args can be of any type, e.g.', {maps='like', this='one'},
17+
'or', {'lists', 'like', 'this', 'one'})
18+
logging.temp('if an arg is too long it will be output on multiple lines',
19+
{'aardvark', 'bison', 'camel', 'dingo', 'echnidna'}, 'like this')

examples/levels.lua.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(#) loglevel is 0 (this is always output)
2+
(E) this is output if loglevel >= -1
3+
(W) this is output if loglevel >= 0
4+
(#) args are automatically separated with spaces and a newline is added if necessary
5+
(#) args can be of any type, e.g. {maps: "like", this: "one"} or {[1] "lists", [2] "like", [3] "this", [4] "one"}
6+
(#) if an arg is too long it will be output on multiple lines {
7+
[1] "aardvark"
8+
[2] "bison"
9+
[3] "camel"
10+
[4] "dingo"
11+
[5] "echnidna"
12+
} like this

examples/logging.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../logging.lua

0 commit comments

Comments
 (0)