From 6088d5b0e079a42825ad0b2c98bbed1c06ffb10f Mon Sep 17 00:00:00 2001 From: Jonathan Amsterdam Date: Wed, 26 Jul 2023 07:33:34 -0400 Subject: [PATCH] slog-handler-guide: discuss Record.Clone Change-Id: I55c6367da953b9a59c4d5cd8ca817d3c051e12de Reviewed-on: https://go-review.googlesource.com/c/example/+/513138 TryBot-Result: Gopher Robot Reviewed-by: Ian Cottrell Run-TryBot: Jonathan Amsterdam --- slog-handler-guide/README.md | 40 +++++++++++++++++++++++++++++++++++- slog-handler-guide/guide.md | 39 ++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/slog-handler-guide/README.md b/slog-handler-guide/README.md index 0dbb3404..cadc5017 100644 --- a/slog-handler-guide/README.md +++ b/slog-handler-guide/README.md @@ -16,6 +16,7 @@ This document is maintained by Jonathan Amsterdam `jba@google.com`. 1. [The `WithGroup` method](#the-`withgroup`-method) 1. [Testing](#testing) 1. [General considerations](#general-considerations) + 1. [Copying records](#copying-records) 1. [Concurrency safety](#concurrency-safety) 1. [Robustness](#robustness) 1. [Speed](#speed) @@ -766,7 +767,44 @@ in 65 lines. # General considerations -TODO(jba): reintroduce the material on Record.Clone that used to be here. +## Copying records + +Most handlers won't need to copy the `slog.Record` that is passed +to the `Handle` method. +Those that do must take special care in some cases. + +A handler can make a single copy of a `Record` with an ordinary Go +assignment, channel send or function call if it doesn't retain the +original. +But if its actions result in more than one copy, it should call `Record.Clone` +to make the copies so that they don't share state. +This `Handle` method passes the record to a single handler, so it doesn't require `Clone`: + + type Handler1 struct { + h slog.Handler + // ... + } + + func (h *Handler1) Handle(ctx context.Context, r slog.Record) error { + return h.h.Handle(ctx, r) + } + +This `Handle` method might pass the record to more than one handler, so it +should use `Clone`: + + type Handler2 struct { + hs []slog.Handler + // ... + } + + func (h *Handler2) Handle(ctx context.Context, r slog.Record) error { + for _, hh := range h.hs { + if err := hh.Handle(ctx, r.Clone()); err != nil { + return err + } + } + return nil + } ## Concurrency safety diff --git a/slog-handler-guide/guide.md b/slog-handler-guide/guide.md index 578236b0..229589b1 100644 --- a/slog-handler-guide/guide.md +++ b/slog-handler-guide/guide.md @@ -485,7 +485,44 @@ in 65 lines. # General considerations -TODO(jba): reintroduce the material on Record.Clone that used to be here. +## Copying records + +Most handlers won't need to copy the `slog.Record` that is passed +to the `Handle` method. +Those that do must take special care in some cases. + +A handler can make a single copy of a `Record` with an ordinary Go +assignment, channel send or function call if it doesn't retain the +original. +But if its actions result in more than one copy, it should call `Record.Clone` +to make the copies so that they don't share state. +This `Handle` method passes the record to a single handler, so it doesn't require `Clone`: + + type Handler1 struct { + h slog.Handler + // ... + } + + func (h *Handler1) Handle(ctx context.Context, r slog.Record) error { + return h.h.Handle(ctx, r) + } + +This `Handle` method might pass the record to more than one handler, so it +should use `Clone`: + + type Handler2 struct { + hs []slog.Handler + // ... + } + + func (h *Handler2) Handle(ctx context.Context, r slog.Record) error { + for _, hh := range h.hs { + if err := hh.Handle(ctx, r.Clone()); err != nil { + return err + } + } + return nil + } ## Concurrency safety