@@ -150,9 +150,7 @@ Changes to `LevelVar`s are goroutine-safe.
150
150
The mutex will be used to ensure that writes to the ` io.Writer ` happen atomically.
151
151
Unusually, ` IndentHandler ` holds a pointer to a ` sync.Mutex ` rather than holding a
152
152
` sync.Mutex ` directly.
153
- There is a good reason for that, which we'll explain later.
154
-
155
- TODO(jba): add link to that later explanation.
153
+ There is a good reason for that, which we'll explain [ later] ( #getting-the-mutex-right ) .
156
154
157
155
Our handler will need additional state to track calls to ` WithGroup ` and ` WithAttrs ` .
158
156
We will describe that state when we get to those methods.
@@ -508,6 +506,33 @@ you can use a linked list instead,
508
506
which ` Handle ` will have to reverse or visit recursively.
509
507
See [ github.com/jba/slog/withsupport] ( https://github.com/jba/slog/withsupport ) for an implementation.
510
508
509
+ #### Getting the mutex right
510
+
511
+ Let us revisit the last few lines of ` Handle ` :
512
+
513
+ h.mu.Lock()
514
+ defer h.mu.Unlock()
515
+ _, err := h.out.Write(buf)
516
+ return err
517
+
518
+ This code hasn't changed, but we can now appreciate why ` h.mu ` is a
519
+ pointer to a ` sync.Mutex ` . Both ` WithGroup ` and ` WithAttrs ` copy the handler.
520
+ Both copies point to the same mutex.
521
+ If the copy and the original used different mutexes and were used concurrently,
522
+ then their output could be interleaved, or some output could be lost.
523
+ Code like this:
524
+
525
+ l2 := l1.With("a", 1)
526
+ go l1.Info("hello")
527
+ l2.Info("goodbye")
528
+
529
+ could produce output like this:
530
+
531
+ hegoollo a=dbye1
532
+
533
+ See [ this bug report] ( https://go.dev/issue/61321 ) for more detail.
534
+
535
+
511
536
### With pre-formatting
512
537
513
538
Our second implementation implements pre-formatting.
@@ -708,8 +733,10 @@ mutable state.
708
733
- The ` Handle ` method typically works only with its arguments and stored fields.
709
734
710
735
Calls to output methods like ` io.Writer.Write ` should be synchronized unless
711
- it can be verified that no locking is needed. Beware of facile claims like
712
- "Unix writes are atomic"; the situation is a lot more nuanced than that.
736
+ it can be verified that no locking is needed.
737
+ As we saw in our example, storing a pointer to a mutex enables a logger and
738
+ all of its clones to synchronize with each other.
739
+ Beware of facile claims like "Unix writes are atomic"; the situation is a lot more nuanced than that.
713
740
714
741
Some handlers have legitimate reasons for keeping state.
715
742
For example, a handler might support a ` SetLevel ` method to change its configured level
0 commit comments