slog: Loggers hold contexts
This CL provides a way to pass contexts to Handlers.
It is often asked that Handler.Handle be given access to the context
of the log line. The main reason is to allow logs to contain a trace
ID, so they can be correlated with tracing. Observability systems like
Open Telemetry store trace information in the context.
The usual suggestion is to add a context argument to log methods:
slog.Info(ctx, "message")
But that imposes a burden on all users, even those who don't need
the context.
Another solution is to create a new Logger each time a new trace
element ("span" in Open Telemetry terminology) is created, with an
attribute containing the span (or the context). This means that
everyone who creates a span must remember to create a Logger as well,
or it requires wrapping this package in another, context-aware one.
This CL implements the following alternative:
- A Logger now holds a context as well as a Handler.
- Logger.Context returns this context.
- Logger.WithContext returns a new Logger with a given context.
- To avoid allocation in WithContext, Logger is changed from a pointer
type to a value type. Since it is immutable, its semantics are
unchanged. Its size is quadrupled (from a one-word pointer to a
four-word struct); our benchmarks show small slowdowns for the
fastest operations (that make unrealistically optimistic
assumptions), and none for slower (and more practical) ones.
- Record has a new ctx field and a Context method to retrieve it.
- NewRecord gains an additional argument for the context.
- When a Logger l creates a Record r to pass to a Handler, it
sets r.ctx to l.ctx.
- The top-level Ctx function both retrieves a Logger from a given
context, and adds that context to the logger: it is equivalent to
FromContext(ctx).WithContext(ctx)
Change-Id: I552b722c118d11406a05412e999c0b629b50228c
Reviewed-on: https://go-review.googlesource.com/c/exp/+/439059
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/slog/benchmarks/handlers_test.go b/slog/benchmarks/handlers_test.go
index 223bfca..623a588 100644
--- a/slog/benchmarks/handlers_test.go
+++ b/slog/benchmarks/handlers_test.go
@@ -9,7 +9,7 @@
)
func TestHandlers(t *testing.T) {
- r := slog.NewRecord(TestTime, slog.InfoLevel, TestMessage, 0)
+ r := slog.NewRecord(TestTime, slog.InfoLevel, TestMessage, 0, nil)
r.AddAttrs(TestAttrs...)
t.Run("text", func(t *testing.T) {
var b bytes.Buffer
diff --git a/slog/benchmarks/slog-pc.bench b/slog/benchmarks/slog-pc.bench
index 899db0c..c4372f7 100644
--- a/slog/benchmarks/slog-pc.bench
+++ b/slog/benchmarks/slog-pc.bench
@@ -2,80 +2,80 @@
goarch: amd64
pkg: golang.org/x/exp/slog/benchmarks
cpu: Intel(R) Xeon(R) CPU @ 2.20GHz
-BenchmarkAttrs/disabled/5_args-8 100000000 10.02 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/5_args-8 120896504 9.981 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/5_args-8 100000000 10.23 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/5_args-8 100000000 10.06 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/5_args-8 120556380 9.942 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 62952199 17.94 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 63987754 18.04 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 62148806 18.09 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 60456765 18.01 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 63532894 18.07 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18832660 63.05 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18503997 63.05 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18793000 63.33 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18851764 62.90 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18620378 63.33 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 3081579 386.9 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 2594556 389.4 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 3092631 387.6 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 3095012 396.3 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 3090172 389.4 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 1816550 682.2 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 1721044 692.5 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 1749418 674.9 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 1728466 712.4 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 1780058 676.4 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 510950 2316 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 489726 2332 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 464305 2298 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 499432 2455 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 462938 2300 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 2519834 481.5 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 2521468 481.4 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 2465902 486.9 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 2511697 474.6 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 2455657 497.1 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 1217221 922.8 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 1303630 920.9 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 1331846 915.3 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 1300803 905.6 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 1342477 908.3 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 370690 3211 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 382737 3269 ns/op 1413 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 397886 3160 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 389143 3125 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 395041 3141 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1205694 985.4 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1000000 1013 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1214325 977.5 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1202088 985.5 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1000000 1017 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 771573 1641 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 805484 1651 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 756583 1652 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 811674 1642 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 812188 1630 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 223544 5286 ns/op 1477 B/op 17 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 243157 5573 ns/op 1477 B/op 17 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 226200 5568 ns/op 1477 B/op 17 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 231757 5341 ns/op 1477 B/op 17 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 230116 5308 ns/op 1477 B/op 17 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1347787 893.8 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1340078 880.7 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1333688 899.6 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1440332 825.5 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1390076 851.6 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 865652 1498 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 857676 1501 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 895814 1483 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 943058 1497 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 888254 1452 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 232080 5004 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 262764 4974 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 249297 4970 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 230474 4990 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 248367 4970 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/disabled/5_args-8 100000000 10.97 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/5_args-8 100000000 11.03 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/5_args-8 100000000 10.83 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/5_args-8 97441850 10.84 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/5_args-8 95607450 10.82 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 62498384 18.88 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 61121353 18.86 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 62246416 18.88 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 60799956 18.89 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 59061819 18.88 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18597520 64.12 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18031036 64.39 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18696117 63.94 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18752000 63.81 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18493252 63.88 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 3089053 402.2 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 3071889 389.2 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 3114163 386.9 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 3103900 386.4 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 3092712 385.4 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 1747160 695.9 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 1705131 696.5 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 1718131 689.8 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 1705776 706.5 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 1695583 704.3 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 462394 2350 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 495303 2381 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 535171 2326 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 457449 2331 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 540910 2323 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 2504246 487.5 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 2470434 492.9 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 2494670 484.9 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 2475620 487.3 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 2399865 498.9 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 1257261 946.4 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 1279915 929.7 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 1272433 935.2 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 1000000 1002 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 1243527 975.8 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 365814 3315 ns/op 1413 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 336776 3290 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 354438 3241 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 377692 3275 ns/op 1413 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 326116 3290 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 988131 1076 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 983840 1042 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 1000000 1028 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 990825 1064 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 1000000 1028 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 741379 1727 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 768871 1736 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 712422 1726 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 761432 1715 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 768960 1743 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 224613 5627 ns/op 1477 B/op 17 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 226648 5848 ns/op 1477 B/op 17 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 212464 5621 ns/op 1477 B/op 17 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 219996 5690 ns/op 1477 B/op 17 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 198876 5662 ns/op 1477 B/op 17 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 1322396 876.7 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 1359752 940.1 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 1297011 918.5 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 1387628 882.2 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 1382520 892.2 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 826593 1558 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 815361 1554 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 829800 1551 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 854877 1539 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 835476 1563 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 257505 5145 ns/op 1411 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 231441 5103 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 236878 5033 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 230604 4987 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 231382 5082 ns/op 1412 B/op 1 allocs/op
PASS
-ok golang.org/x/exp/slog/benchmarks 113.516s
+ok golang.org/x/exp/slog/benchmarks 107.152s
diff --git a/slog/benchmarks/slog.bench b/slog/benchmarks/slog.bench
index 5a520a3..0b58079 100644
--- a/slog/benchmarks/slog.bench
+++ b/slog/benchmarks/slog.bench
@@ -2,80 +2,80 @@
goarch: amd64
pkg: golang.org/x/exp/slog/benchmarks
cpu: Intel(R) Xeon(R) CPU @ 2.20GHz
-BenchmarkAttrs/disabled/5_args-8 120793970 10.09 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/5_args-8 120658738 9.899 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/5_args-8 100000000 10.01 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/5_args-8 100000000 10.01 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/5_args-8 120536524 9.989 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 64657927 18.04 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 63846556 18.06 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 63629414 18.02 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 64100286 19.21 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/10_args-8 65766166 18.12 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18972454 63.28 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18783354 64.29 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18715994 63.11 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18988142 63.22 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/disabled/40_args-8 18678668 63.03 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 15315618 78.12 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 15137607 78.32 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 15149436 78.73 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 15181647 81.45 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/5_args-8 15160042 77.58 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 4704374 274.1 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 4397641 268.1 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 4408592 276.0 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 4068620 289.2 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/10_args-8 4362400 268.4 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 1282314 949.3 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 1282678 953.8 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 1288890 938.6 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 1325292 976.2 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/async_discard/40_args-8 1298730 921.9 ns/op 1408 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 7389552 157.3 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 7859308 172.5 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 5420155 233.2 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 6782334 173.1 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/5_args-8 5866044 210.0 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 2312252 518.3 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 2258119 532.0 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 2281345 512.9 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 2327382 512.6 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/10_args-8 2330041 518.1 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 633972 1977 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 609144 1960 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 607858 2006 ns/op 1412 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 637404 1948 ns/op 1411 B/op 1 allocs/op
-BenchmarkAttrs/fastText_discard/40_args-8 645720 1982 ns/op 1411 B/op 1 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1742284 687.7 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1761308 679.4 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1678760 706.1 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1644138 726.8 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/5_args-8 1725314 719.6 ns/op 8 B/op 2 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 1000000 1305 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 1000000 1249 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 1000000 1246 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 1000000 1247 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/10_args-8 1000000 1261 ns/op 224 B/op 5 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 256171 4429 ns/op 1476 B/op 17 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 301150 4270 ns/op 1476 B/op 17 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 252357 4238 ns/op 1476 B/op 17 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 328380 4245 ns/op 1476 B/op 17 allocs/op
-BenchmarkAttrs/Text_discard/40_args-8 301352 4192 ns/op 1477 B/op 17 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1827435 635.1 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1804364 652.4 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1928208 627.1 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1893918 574.8 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/5_args-8 1791198 664.7 ns/op 0 B/op 0 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 1000000 1168 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 1000000 1153 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 1000000 1130 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 1000000 1118 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/10_args-8 1000000 1163 ns/op 208 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 319970 3941 ns/op 1411 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 304051 4078 ns/op 1411 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 310402 3868 ns/op 1411 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 332454 4042 ns/op 1411 B/op 1 allocs/op
-BenchmarkAttrs/JSON_discard/40_args-8 326292 3939 ns/op 1411 B/op 1 allocs/op
+BenchmarkAttrs/disabled/5_args-8 100000000 10.86 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/5_args-8 100000000 10.84 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/5_args-8 100000000 10.83 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/5_args-8 100000000 10.85 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/5_args-8 96611278 10.84 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 57230637 18.90 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 62919320 18.88 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 60301082 19.08 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 61005656 19.00 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/10_args-8 60954086 18.90 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18576979 63.79 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18501492 63.94 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18704040 63.76 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18194610 63.67 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/disabled/40_args-8 18648174 63.85 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 13214536 89.16 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 14343093 85.63 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 13142229 88.27 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 13435146 87.02 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/5_args-8 13708140 88.69 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 4533420 280.7 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 4527133 274.6 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 4155231 283.7 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 4192006 282.4 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/10_args-8 4219160 278.0 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 1283365 930.1 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 1301449 943.2 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 1261256 914.3 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 1298919 887.4 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/async_discard/40_args-8 1342897 889.1 ns/op 1408 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 6955764 176.7 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 7571996 157.5 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 6186126 178.4 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 5276200 290.7 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/5_args-8 6722038 197.9 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 2247231 511.6 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 2304651 530.3 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 2294930 521.2 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 2327901 525.4 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/10_args-8 2202255 530.2 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 608607 1982 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 645728 1967 ns/op 1411 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 600284 1986 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 595011 2067 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/fastText_discard/40_args-8 554013 2037 ns/op 1412 B/op 1 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 1675130 728.9 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 1702803 744.2 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 1592347 754.2 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 1658474 737.4 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/5_args-8 1641886 742.2 ns/op 8 B/op 2 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 1000000 1286 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 985032 1301 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 1000000 1285 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 937164 1285 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/10_args-8 1000000 1256 ns/op 224 B/op 5 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 291607 4362 ns/op 1476 B/op 17 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 276548 4369 ns/op 1476 B/op 17 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 274687 4354 ns/op 1476 B/op 17 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 299493 4397 ns/op 1476 B/op 17 allocs/op
+BenchmarkAttrs/Text_discard/40_args-8 283070 4328 ns/op 1476 B/op 17 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 1918728 645.7 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 2024074 606.1 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 1769748 657.8 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 1868710 640.6 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/5_args-8 1965302 627.2 ns/op 0 B/op 0 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 1000000 1191 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 1000000 1190 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 1000000 1240 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 1000000 1195 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/10_args-8 1000000 1203 ns/op 208 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 349972 4133 ns/op 1411 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 298383 4087 ns/op 1411 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 324619 4116 ns/op 1411 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 302998 4223 ns/op 1411 B/op 1 allocs/op
+BenchmarkAttrs/JSON_discard/40_args-8 323319 4187 ns/op 1411 B/op 1 allocs/op
PASS
-ok golang.org/x/exp/slog/benchmarks 111.625s
+ok golang.org/x/exp/slog/benchmarks 109.134s
diff --git a/slog/context.go b/slog/context.go
index 7d5b98e..538e350 100644
--- a/slog/context.go
+++ b/slog/context.go
@@ -10,15 +10,21 @@
// NewContext returns a context that contains the given Logger.
// Use FromContext to retrieve the Logger.
-func NewContext(ctx context.Context, l *Logger) context.Context {
+func NewContext(ctx context.Context, l Logger) context.Context {
return context.WithValue(ctx, contextKey{}, l)
}
// FromContext returns the Logger stored in ctx by NewContext, or the default
// Logger if there is none.
-func FromContext(ctx context.Context) *Logger {
- if l, ok := ctx.Value(contextKey{}).(*Logger); ok {
+func FromContext(ctx context.Context) Logger {
+ if l, ok := ctx.Value(contextKey{}).(Logger); ok {
return l
}
return Default()
}
+
+// Ctx retrieves a Logger from the given context using FromContext. Then it adds
+// the given context to the Logger using WithContext and returns the result.
+func Ctx(ctx context.Context) Logger {
+ return FromContext(ctx).WithContext(ctx)
+}
diff --git a/slog/context_test.go b/slog/context_test.go
new file mode 100644
index 0000000..126bd1e
--- /dev/null
+++ b/slog/context_test.go
@@ -0,0 +1,49 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package slog
+
+import (
+ "context"
+ "testing"
+)
+
+func TestContext(t *testing.T) {
+ // If there is no Logger in the context, FromContext returns the default
+ // Logger.
+ ctx := context.Background()
+ gotl := FromContext(ctx)
+ if _, ok := gotl.Handler().(*defaultHandler); !ok {
+ t.Error("did not get default Logger")
+ }
+
+ // If there is a Logger in the context, FromContext returns it, with the ctx
+ // arg.
+ h := &captureHandler{}
+ ctx = NewContext(ctx, New(h))
+ ctx = context.WithValue(ctx, "ID", 1)
+ gotl = FromContext(ctx)
+ if gotl.Handler() != h {
+ t.Fatal("wrong handler")
+ }
+ // FromContext preserves the context of the Logger that was stored
+ // with NewContext, in this case nil.
+ gotl.Info("")
+ if g := h.r.Context(); g != nil {
+ t.Errorf("got %v, want nil", g)
+ }
+ gotl = Ctx(ctx)
+ if gotl.Handler() != h {
+ t.Fatal("wrong handler")
+ }
+ // Ctx adds the argument context to the Logger.
+ gotl.Info("")
+ c := h.r.Context()
+ if c == nil {
+ t.Fatal("got nil Context")
+ }
+ if g, w := c.Value("ID"), 1; g != w {
+ t.Errorf("got ID %v, want %v", g, w)
+ }
+}
diff --git a/slog/handler_test.go b/slog/handler_test.go
index eaa230f..8a967ef 100644
--- a/slog/handler_test.go
+++ b/slog/handler_test.go
@@ -88,7 +88,7 @@
if test.with != nil {
h = test.with(h)
}
- r := NewRecord(time.Time{}, InfoLevel, "message", 0)
+ r := NewRecord(time.Time{}, InfoLevel, "message", 0, nil)
r.AddAttrs(test.attrs...)
if err := h.Handle(r); err != nil {
t.Fatal(err)
@@ -271,7 +271,7 @@
wantJSON: `{"msg":"message","p1":1,"s1":{"s2":{"a":"one","b":2}}}`,
},
} {
- r := NewRecord(testTime, InfoLevel, "message", 1)
+ r := NewRecord(testTime, InfoLevel, "message", 1, nil)
r.AddAttrs(test.attrs...)
var buf bytes.Buffer
opts := HandlerOptions{ReplaceAttr: test.replace}
diff --git a/slog/json_handler_test.go b/slog/json_handler_test.go
index df0628b..4a3d4fe 100644
--- a/slog/json_handler_test.go
+++ b/slog/json_handler_test.go
@@ -39,7 +39,7 @@
t.Run(test.name, func(t *testing.T) {
var buf bytes.Buffer
h := test.opts.NewJSONHandler(&buf)
- r := NewRecord(testTime, InfoLevel, "m", 0)
+ r := NewRecord(testTime, InfoLevel, "m", 0, nil)
r.AddAttrs(Int("a", 1), Any("m", map[string]int{"b": 2}))
if err := h.Handle(r); err != nil {
t.Fatal(err)
diff --git a/slog/logger.go b/slog/logger.go
index 8ac87a6..34e1bd7 100644
--- a/slog/logger.go
+++ b/slog/logger.go
@@ -5,6 +5,7 @@
package slog
import (
+ "context"
"log"
"sync/atomic"
"time"
@@ -13,18 +14,18 @@
var defaultLogger atomic.Value
func init() {
- defaultLogger.Store(&Logger{
+ defaultLogger.Store(Logger{
handler: newDefaultHandler(log.Output),
})
}
// Default returns the default Logger.
-func Default() *Logger { return defaultLogger.Load().(*Logger) }
+func Default() Logger { return defaultLogger.Load().(Logger) }
// SetDefault makes l the default Logger.
// After this call, output from the log package's default Logger
// (as with [log.Print], etc.) will be logged at InfoLevel using l's Handler.
-func SetDefault(l *Logger) {
+func SetDefault(l Logger) {
defaultLogger.Store(l)
// If the default's handler is a defaultHandler, then don't use a handleWriter,
// or we'll deadlock as they both try to acquire the log default mutex.
@@ -55,7 +56,7 @@
if len(buf) > 0 && buf[len(buf)-1] == '\n' {
buf = buf[:len(buf)-1]
}
- r := NewRecord(time.Now(), InfoLevel, string(buf), depth)
+ r := NewRecord(time.Now(), InfoLevel, string(buf), depth, nil)
return origLen, w.h.Handle(r)
}
@@ -63,18 +64,23 @@
// Log, Debug, Info, Warn, and Error methods.
// For each call, it creates a Record and passes it to a Handler.
//
-// Loggers are immutable; to create a new one, call [New] or [Logger.With].
+// To create a new Logger, call [New] or a Logger method
+// that begins "With".
type Logger struct {
handler Handler // for structured logging
+ ctx context.Context
}
// Handler returns l's Handler.
-func (l *Logger) Handler() Handler { return l.handler }
+func (l Logger) Handler() Handler { return l.handler }
+
+// Context returns l's context.
+func (l Logger) Context() context.Context { return l.ctx }
// With returns a new Logger whose handler's attributes are a concatenation of
// l's attributes and the given arguments, converted to Attrs as in
// [Logger.Log].
-func (l *Logger) With(args ...any) *Logger {
+func (l Logger) With(args ...any) Logger {
var (
attr Attr
attrs []Attr
@@ -83,7 +89,8 @@
attr, args = argsToAttr(args)
attrs = append(attrs, attr)
}
- return &Logger{handler: l.handler.With(attrs)}
+ l.handler = l.handler.With(attrs)
+ return l
}
// WithScope returns a new Logger with the named scope.
@@ -91,20 +98,28 @@
// to the given name. Two loggers with different scope names
// but identical Attr keys will result in output without
// duplicate keys.
-func (l *Logger) WithScope(name string) *Logger {
- return &Logger{handler: l.handler.WithScope(name)}
+func (l Logger) WithScope(name string) Logger {
+ l.handler = l.handler.WithScope(name)
+ return l
+}
+
+// WithContext returns a new Logger with the same handler
+// as the receiver and the given context.
+func (l Logger) WithContext(ctx context.Context) Logger {
+ l.ctx = ctx
+ return l
}
// New creates a new Logger with the given Handler.
-func New(h Handler) *Logger { return &Logger{handler: h} }
+func New(h Handler) Logger { return Logger{handler: h} }
// With calls Logger.With on the default logger.
-func With(args ...any) *Logger {
+func With(args ...any) Logger {
return Default().With(args...)
}
// Enabled reports whether l emits log records at the given level.
-func (l *Logger) Enabled(level Level) bool {
+func (l Logger) Enabled(level Level) bool {
return l.Handler().Enabled(level)
}
@@ -118,14 +133,14 @@
// the following argument is treated as the value and the two are combined
// into an Attr.
// - Otherwise, the argument is treated as a value with key "!BADKEY".
-func (l *Logger) Log(level Level, msg string, args ...any) {
+func (l Logger) Log(level Level, msg string, args ...any) {
l.LogDepth(0, level, msg, args...)
}
// LogDepth is like [Logger.Log], but accepts a call depth to adjust the
// file and line number in the log record. 0 refers to the caller
// of LogDepth; 1 refers to the caller's caller; and so on.
-func (l *Logger) LogDepth(calldepth int, level Level, msg string, args ...any) {
+func (l Logger) LogDepth(calldepth int, level Level, msg string, args ...any) {
if !l.Enabled(level) {
return
}
@@ -134,19 +149,18 @@
_ = l.Handler().Handle(r)
}
-func (l *Logger) makeRecord(msg string, level Level, depth int) Record {
- depth += 5
- return NewRecord(time.Now(), level, msg, depth)
+func (l Logger) makeRecord(msg string, level Level, depth int) Record {
+ return NewRecord(time.Now(), level, msg, depth+5, l.ctx)
}
// LogAttrs is a more efficient version of [Logger.Log] that accepts only Attrs.
-func (l *Logger) LogAttrs(level Level, msg string, attrs ...Attr) {
+func (l Logger) LogAttrs(level Level, msg string, attrs ...Attr) {
l.LogAttrsDepth(0, level, msg, attrs...)
}
// LogAttrsDepth is like [Logger.LogAttrs], but accepts a call depth argument
// which it interprets like [Logger.LogDepth].
-func (l *Logger) LogAttrsDepth(calldepth int, level Level, msg string, attrs ...Attr) {
+func (l Logger) LogAttrsDepth(calldepth int, level Level, msg string, attrs ...Attr) {
if !l.Enabled(level) {
return
}
@@ -156,24 +170,24 @@
}
// Debug logs at DebugLevel.
-func (l *Logger) Debug(msg string, args ...any) {
+func (l Logger) Debug(msg string, args ...any) {
l.LogDepth(0, DebugLevel, msg, args...)
}
// Info logs at InfoLevel.
-func (l *Logger) Info(msg string, args ...any) {
+func (l Logger) Info(msg string, args ...any) {
l.LogDepth(0, InfoLevel, msg, args...)
}
// Warn logs at WarnLevel.
-func (l *Logger) Warn(msg string, args ...any) {
+func (l Logger) Warn(msg string, args ...any) {
l.LogDepth(0, WarnLevel, msg, args...)
}
// Error logs at ErrorLevel.
// If err is non-nil, Error appends Any("err", err)
// to the list of attributes.
-func (l *Logger) Error(msg string, err error, args ...any) {
+func (l Logger) Error(msg string, err error, args ...any) {
if err != nil {
// TODO: avoid the copy.
args = append(args[:len(args):len(args)], Any("err", err))
diff --git a/slog/logger_test.go b/slog/logger_test.go
index ddcf4fa..f502537 100644
--- a/slog/logger_test.go
+++ b/slog/logger_test.go
@@ -158,7 +158,7 @@
func TestAlloc(t *testing.T) {
dl := New(discardHandler{})
- defer func(d *Logger) { SetDefault(d) }(Default())
+ defer func(d Logger) { SetDefault(d) }(Default())
SetDefault(dl)
t.Run("Info", func(t *testing.T) {
@@ -265,7 +265,7 @@
{[]any{"a", 1, "b"}, []Attr{Int("a", 1), String(badKey, "b")}},
{[]any{"a", 1, 2, 3}, []Attr{Int("a", 1), Int(badKey, 2), Int(badKey, 3)}},
} {
- r := NewRecord(time.Time{}, 0, "", 0)
+ r := NewRecord(time.Time{}, 0, "", 0, nil)
r.setAttrsFromArgs(test.args)
got := attrsSlice(r)
if !attrsEqual(got, test.want) {
diff --git a/slog/record.go b/slog/record.go
index 7e58b87..852ed2e 100644
--- a/slog/record.go
+++ b/slog/record.go
@@ -5,6 +5,7 @@
package slog
import (
+ "context"
"runtime"
"time"
@@ -27,6 +28,9 @@
// The level of the event.
level Level
+ // The Logger's context.
+ ctx context.Context
+
// The pc at the time the record was constructed, as determined
// by runtime.Callers using the calldepth argument to NewRecord.
pc uintptr
@@ -52,9 +56,12 @@
// return the file and line number at that depth,
// where 1 means the caller of NewRecord.
//
+// A Record stores a context solely to provide Handlers access to the context's
+// values. Cancellation of the context does not affect record processing.
+//
// NewRecord is intended for logging APIs that want to support a [Handler] as
// a backend.
-func NewRecord(t time.Time, level Level, msg string, calldepth int) Record {
+func NewRecord(t time.Time, level Level, msg string, calldepth int, ctx context.Context) Record {
var p uintptr
if calldepth > 0 {
p = pc(calldepth + 1)
@@ -63,6 +70,7 @@
time: t,
message: msg,
level: level,
+ ctx: ctx,
pc: p,
}
}
@@ -76,6 +84,11 @@
// Level returns the level of the log event.
func (r Record) Level() Level { return r.level }
+// Context returns the context in the Record.
+// If the Record was created from a Logger,
+// this will be the Logger's context.
+func (r Record) Context() context.Context { return r.ctx }
+
// SourceLine returns the file and line of the log event.
// If the Record was created without the necessary information,
// or if the location is unavailable, it returns ("", 0).
diff --git a/slog/record_test.go b/slog/record_test.go
index afbd142..08e3a09 100644
--- a/slog/record_test.go
+++ b/slog/record_test.go
@@ -36,7 +36,7 @@
{-16, "", false},
{1, "record.go", true},
} {
- r := NewRecord(time.Time{}, 0, "", test.depth)
+ r := NewRecord(time.Time{}, 0, "", test.depth, nil)
gotFile, gotLine := r.SourceLine()
if i := strings.LastIndexByte(gotFile, '/'); i >= 0 {
gotFile = gotFile[i+1:]
@@ -67,7 +67,7 @@
// Create a record whose Attrs overflow the inline array,
// creating a slice in r.back.
- r1 := NewRecord(time.Time{}, 0, "", 0)
+ r1 := NewRecord(time.Time{}, 0, "", 0, nil)
r1.AddAttrs(intAttrs(0, nAttrsInline+1)...)
// Ensure that r1.back's capacity exceeds its length.
b := make([]Attr, len(r1.back), len(r1.back)+1)
@@ -90,7 +90,7 @@
}
func newRecordWithAttrs(as []Attr) Record {
- r := NewRecord(time.Now(), InfoLevel, "", 0)
+ r := NewRecord(time.Now(), InfoLevel, "", 0, nil)
r.AddAttrs(as...)
return r
}
@@ -117,7 +117,7 @@
}
func BenchmarkSourceLine(b *testing.B) {
- r := NewRecord(time.Now(), InfoLevel, "", 5)
+ r := NewRecord(time.Now(), InfoLevel, "", 5, nil)
b.Run("alone", func(b *testing.B) {
for i := 0; i < b.N; i++ {
file, line := r.SourceLine()
@@ -144,7 +144,7 @@
var a Attr
for i := 0; i < b.N; i++ {
- r := NewRecord(time.Time{}, InfoLevel, "", 0)
+ r := NewRecord(time.Time{}, InfoLevel, "", 0, nil)
for j := 0; j < nAttrs; j++ {
r.AddAttrs(Int("k", j))
}
diff --git a/slog/text_handler_test.go b/slog/text_handler_test.go
index dc75c91..8628ba5 100644
--- a/slog/text_handler_test.go
+++ b/slog/text_handler_test.go
@@ -72,7 +72,7 @@
t.Run(opts.name, func(t *testing.T) {
var buf bytes.Buffer
h := opts.opts.NewTextHandler(&buf)
- r := NewRecord(testTime, InfoLevel, "a message", 0)
+ r := NewRecord(testTime, InfoLevel, "a message", 0, nil)
r.AddAttrs(test.attr)
if err := h.Handle(r); err != nil {
t.Fatal(err)
@@ -114,7 +114,7 @@
func TestTextHandlerSource(t *testing.T) {
var buf bytes.Buffer
h := HandlerOptions{AddSource: true}.NewTextHandler(&buf)
- r := NewRecord(testTime, InfoLevel, "m", 2)
+ r := NewRecord(testTime, InfoLevel, "m", 2, nil)
if err := h.Handle(r); err != nil {
t.Fatal(err)
}
@@ -142,7 +142,7 @@
var h Handler = NewTextHandler(&buf)
h = h.With([]Attr{Duration("dur", time.Minute), Bool("b", true)})
// Also test omitting time.
- r := NewRecord(time.Time{}, 0 /* 0 Level is INFO */, "m", 0)
+ r := NewRecord(time.Time{}, 0 /* 0 Level is INFO */, "m", 0, nil)
r.AddAttrs(Int("a", 1))
if err := h.Handle(r); err != nil {
t.Fatal(err)
@@ -155,7 +155,7 @@
}
func TestTextHandlerAlloc(t *testing.T) {
- r := NewRecord(time.Now(), InfoLevel, "msg", 0)
+ r := NewRecord(time.Now(), InfoLevel, "msg", 0, nil)
for i := 0; i < 10; i++ {
r.AddAttrs(Int("x = y", i))
}