Skip to content

Commit f4189ce

Browse files
committed
Pass-through single string arg for simpler and faster output
1 parent fb4d5bb commit f4189ce

3 files changed

Lines changed: 56 additions & 12 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ log messages on stderr.
6262

6363
#### log.tostr
6464

65-
A custom function that takes a single value and returns its string
66-
representation, useful when you need richer or domain-specific output. `nil`
67-
by default, falling back to the plain `tostring`.
65+
A custom function for converting a value to its string representation, useful
66+
when you need richer or domain-specific output. Replaces the default behavior
67+
(number rounding + `tostring`). `nil` by default. A single string argument
68+
(the most common case) is always passed through as-is, bypassing `tostr`.
6869

6970
An example using [inspect.lua](https://github.com/kikito/inspect.lua):
7071

log.lua

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ end
4242

4343
local make_msg = function(tostr, ...)
4444
local t = {}
45-
for i = 1, select('#', ...) do
45+
local argc = select('#', ...)
46+
if argc == 1 and type(select(1, ...)) == "string" then
47+
return select(1, ...)
48+
end
49+
for i = 1, argc do
4650
local x = select(i, ...)
4751
if tostr then
4852
t[#t + 1] = tostr(x)
@@ -68,9 +72,9 @@ local noop = function() end
6872
local function attach_log_methods(instance, extra_mt)
6973
local current_level = instance.level
7074
local current_name = instance.name
75+
local impls = {}
7176

7277
-- Build real implementations upfront, closed over `instance`.
73-
local impls = {}
7478
for i, x in ipairs(modes) do
7579
local nameupper = x.name:upper()
7680
impls[i] = function(...)
@@ -114,12 +118,12 @@ local function attach_log_methods(instance, extra_mt)
114118

115119
-- Remove level and name from the raw table so __newindex always fires for them.
116120
rawset(instance, "level", nil)
117-
rawset(instance, "name", nil)
121+
rawset(instance, "name", nil)
118122

119123
local mt = extra_mt or {}
120124
mt.__index = function(_, k)
121125
if k == "level" then return current_level end
122-
if k == "name" then return current_name end
126+
if k == "name" then return current_name end
123127
end
124128
mt.__newindex = function(t, k, v)
125129
if k == "level" then

test.lua

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ local function strip_ansi(s)
5656
return s:gsub("\27%[%d+m", "")
5757
end
5858

59+
-- Check that a log line ends with the expected message
60+
local function ends_with_msg(s, msg)
61+
return strip_ansi(s):sub(- #msg - 1) == msg .. "\n"
62+
end
63+
5964

6065
-- ── Suite: level filtering ────────────────────────────────────────────────────
6166

@@ -486,7 +491,7 @@ do
486491
end
487492

488493
do
489-
-- custom tostr is called for each argument
494+
-- custom tostr is called for each argument in multi-arg calls
490495
reset_log()
491496
log.usecolor = false
492497
local calls = {}
@@ -502,6 +507,40 @@ do
502507
assert_true("custom tostr return value appears in output", captured[1]:find("x x"))
503508
end
504509

510+
do
511+
-- single non-string arg goes through tostr
512+
reset_log()
513+
log.usecolor = false
514+
local received
515+
log.tostr = function(v)
516+
received = v; return "<" .. tostring(v) .. ">"
517+
end
518+
519+
capture_start()
520+
log.info(42)
521+
capture_stop()
522+
523+
assert_eq("tostr receives the non-string value", received, 42)
524+
assert_true("single non-string arg formatted by tostr", ends_with_msg(captured[1], "<42>"))
525+
end
526+
527+
do
528+
-- single string arg bypasses tostr and equals the message exactly
529+
reset_log()
530+
log.usecolor = false
531+
local called = false
532+
log.tostr = function(v)
533+
called = true; return tostring(v)
534+
end
535+
536+
capture_start()
537+
log.info("plain string")
538+
capture_stop()
539+
540+
assert_false("tostr not called for single string arg", called)
541+
assert_true("single string arg equals message exactly", ends_with_msg(captured[1], "plain string"))
542+
end
543+
505544
do
506545
-- custom tostr bypasses number rounding
507546
log.tostr = function(v) return tostring(v) end
@@ -520,12 +559,12 @@ do
520559
local inst = log { tostr = function(v) return "T:" .. tostring(v) end, usecolor = false }
521560

522561
capture_start()
523-
inst.info("hello")
562+
inst.info("hello", "world")
524563
capture_stop()
525564
assert_true("instance tostr wraps value", captured[1]:find("T:hello"))
526565

527566
capture_start()
528-
log.info("hello")
567+
log.info("hello", "world")
529568
capture_stop()
530569
assert_false("global logger unaffected by instance tostr", captured[1]:find("T:hello"))
531570
end
@@ -538,7 +577,7 @@ do
538577
log.usecolor = false
539578

540579
capture_start()
541-
inst.info("hello")
580+
inst.info("hello", "world")
542581
capture_stop()
543582

544583
assert_true("instance inherits tostr from global logger", captured[1]:find("G:hello"))
@@ -551,7 +590,7 @@ do
551590
local inst = log { tostr = function(v) return "I:" .. tostring(v) end, usecolor = false }
552591

553592
capture_start()
554-
inst.info("hello")
593+
inst.info("hello", "world")
555594
capture_stop()
556595
assert_true("instance tostr overrides global tostr", captured[1]:find("I:hello"))
557596
assert_false("global tostr not used when instance tostr set", captured[1]:find("G:hello"))

0 commit comments

Comments
 (0)