nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: rs/zerolog
Pull request #604 (2023-10-20 09:21:00)

hlog: add HostHandler
hlog has MethodHandler, URLHandler, etc..., but doesn't have HostHandler.
"Host" is often recorded in logs, so I think it would be useful to have it.


Patch:
diff --git a/hlog/hlog.go b/hlog/hlog.go
index 3773400..e0dd512 100644
--- a/hlog/hlog.go
+++ b/hlog/hlog.go
@@ -216,3 +216,17 @@ func AccessHandler(f func(r *http.Request, status, size int, duration time.Durat
 		})
 	}
 }
+
+// HostHandler adds the request's host as a field to the context's logger
+// using fieldKey as field key.
+func HostHandler(fieldKey string) func(next http.Handler) http.Handler {
+	return func(next http.Handler) http.Handler {
+		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			log := zerolog.Ctx(r.Context())
+			log.UpdateContext(func(c zerolog.Context) zerolog.Context {
+				return c.Str(fieldKey, r.Host)
+			})
+			next.ServeHTTP(w, r)
+		})
+	}
+}


Test patch:
diff --git a/hlog/hlog_test.go b/hlog/hlog_test.go
index c584e98..1f5a1bc 100644
--- a/hlog/hlog_test.go
+++ b/hlog/hlog_test.go
@@ -292,3 +292,17 @@ func TestCtxWithID(t *testing.T) {
 		t.Errorf("CtxWithID() = %v, want %v", got, want)
 	}
 }
+
+func TestHostHandler(t *testing.T) {
+	out := &bytes.Buffer{}
+	r := &http.Request{Host: "example.com"}
+	h := HostHandler("host")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		l := FromRequest(r)
+		l.Log().Msg("")
+	}))
+	h = NewHandler(zerolog.New(out))(h)
+	h.ServeHTTP(nil, r)
+	if want, got := `{"host":"example.com"}`+"\n", decodeIfBinary(out); want != got {
+		t.Errorf("Invalid log output, got: %s, want: %s", got, want)
+	}
+}
Repository: rs/zerolog
Pull request #629 (2023-12-24 12:59:16)

Refactor: change Hook(h Hook) to Hook(hooks ...Hook)
This PR add support for setting multiple hooks at one time, without multiple times of deep copy (compare to call `Hook()` multi-times)

Also,  `func (l Logger) Hook(hooks ...Hook) Logger {}` is compatible with `func (l Logger) Hook(h Hook) Logger {}`


Patch:
diff --git a/log.go b/log.go
index caa1845..c6baa3a 100644
--- a/log.go
+++ b/log.go
@@ -325,10 +325,13 @@ func (l Logger) Sample(s Sampler) Logger {
 }
 
 // Hook returns a logger with the h Hook.
-func (l Logger) Hook(h Hook) Logger {
-	newHooks := make([]Hook, len(l.hooks), len(l.hooks)+1)
+func (l Logger) Hook(hooks ...Hook) Logger {
+	if len(hooks) == 0 {
+		return l
+	}
+	newHooks := make([]Hook, len(l.hooks), len(l.hooks)+len(hooks))
 	copy(newHooks, l.hooks)
-	l.hooks = append(newHooks, h)
+	l.hooks = append(newHooks, hooks...)
 	return l
 }
 


Test patch:
diff --git a/log_example_test.go b/log_example_test.go
index 81e2c20..c48a1e3 100644
--- a/log_example_test.go
+++ b/log_example_test.go
@@ -72,7 +72,7 @@ func ExampleLogger_Hook() {
 	var levelNameHook LevelNameHook
 	var messageHook MessageHook = "The message"
 
-	log := zerolog.New(os.Stdout).Hook(levelNameHook).Hook(messageHook)
+	log := zerolog.New(os.Stdout).Hook(levelNameHook, messageHook)
 
 	log.Info().Msg("hello world")
Repository: rs/zerolog
Pull request #630 (2024-01-02 21:50:11)

Add stacktrace to Context
Allow to set an error with stracktrace in the Context


Patch:
diff --git a/context.go b/context.go
index 48c1d5d..0e4f44b 100644
--- a/context.go
+++ b/context.go
@@ -163,6 +163,22 @@ func (c Context) Errs(key string, errs []error) Context {
 
 // Err adds the field "error" with serialized err to the logger context.
 func (c Context) Err(err error) Context {
+	if c.l.stack && ErrorStackMarshaler != nil {
+		switch m := ErrorStackMarshaler(err).(type) {
+		case nil:
+		case LogObjectMarshaler:
+			c = c.Object(ErrorStackFieldName, m)
+		case error:
+			if m != nil && !isNilValue(m) {
+				c = c.Str(ErrorStackFieldName, m.Error())
+			}
+		case string:
+			c = c.Str(ErrorStackFieldName, m)
+		default:
+			c = c.Interface(ErrorStackFieldName, m)
+		}
+	}
+
 	return c.AnErr(ErrorFieldName, err)
 }
 


Test patch:
diff --git a/diode/diode_test.go b/diode/diode_test.go
index 87f62bc..29b3f86 100644
--- a/diode/diode_test.go
+++ b/diode/diode_test.go
@@ -3,6 +3,7 @@ package diode_test
 import (
 	"bytes"
 	"fmt"
+	"io"
 	"log"
 	"os"
 	"testing"
diff --git a/hlog/hlog_test.go b/hlog/hlog_test.go
index 0d6b31e..445d6b6 100644
--- a/hlog/hlog_test.go
+++ b/hlog/hlog_test.go
@@ -7,6 +7,7 @@ import (
 	"bytes"
 	"context"
 	"fmt"
+	"io"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
diff --git a/pkgerrors/stacktrace_test.go b/pkgerrors/stacktrace_test.go
index a09b986..787a096 100644
--- a/pkgerrors/stacktrace_test.go
+++ b/pkgerrors/stacktrace_test.go
@@ -60,6 +60,22 @@ func TestLogStackFromContext(t *testing.T) {
 	}
 }
 
+func TestLogStackFromContextWith(t *testing.T) {
+	zerolog.ErrorStackMarshaler = MarshalStack
+
+	err := fmt.Errorf("from error: %w", errors.New("error message"))
+	out := &bytes.Buffer{}
+	log := zerolog.New(out).With().Stack().Err(err).Logger() // calling Stack() on log context instead of event
+
+	log.Error().Msg("")
+
+	got := out.String()
+	want := `\{"level":"error","stack":\[\{"func":"TestLogStackFromContextWith","line":"66","source":"stacktrace_test.go"\},.*\],"error":"from error: error message"\}\n`
+	if ok, _ := regexp.MatchString(want, got); !ok {
+		t.Errorf("invalid log output:\ngot:  %v\nwant: %v", got, want)
+	}
+}
+
 func BenchmarkLogStack(b *testing.B) {
 	zerolog.ErrorStackMarshaler = MarshalStack
 	out := &bytes.Buffer{}
Repository: rs/zerolog
Pull request #696 (2024-11-14 14:45:51)

BasicSampler(0), RandomSampler(0), and BurstSampler(0) must not panic but discard logs
When in use, zerolog.BasicSampler{N: 0} panics and the whole app crashes. But for other samplers, zero means disabling all logs. It may seem confusing that writing every zero-th line is writing nothing, but it's helpful to maintain consistency with other samplers.
So my suggestion is to remove the panic, and cover this corner case with tests.


Patch:
diff --git a/sampler.go b/sampler.go
index 83ce2ed..9207a73 100644
--- a/sampler.go
+++ b/sampler.go
@@ -47,6 +47,9 @@ type BasicSampler struct {
 // Sample implements the Sampler interface.
 func (s *BasicSampler) Sample(lvl Level) bool {
 	n := s.N
+	if n == 0 {
+		return false
+	}
 	if n == 1 {
 		return true
 	}


Test patch:
diff --git a/sampler_test.go b/sampler_test.go
index e42ad3a..248bd30 100644
--- a/sampler_test.go
+++ b/sampler_test.go
@@ -28,6 +28,13 @@ var samplers = []struct {
 		},
 		100, 20, 20,
 	},
+	{
+		"BasicSampler_0",
+		func() Sampler {
+			return &BasicSampler{N: 0}
+		},
+		100, 0, 0,
+	},
 	{
 		"RandomSampler",
 		func() Sampler {
@@ -35,6 +42,13 @@ var samplers = []struct {
 		},
 		100, 10, 30,
 	},
+	{
+		"RandomSampler_0",
+		func() Sampler {
+			return RandomSampler(0)
+		},
+		100, 0, 0,
+	},
 	{
 		"BurstSampler",
 		func() Sampler {
@@ -42,6 +56,13 @@ var samplers = []struct {
 		},
 		100, 20, 20,
 	},
+	{
+		"BurstSampler_0",
+		func() Sampler {
+			return &BurstSampler{Burst: 0, Period: time.Second}
+		},
+		100, 0, 0,
+	},
 	{
 		"BurstSamplerNext",
 		func() Sampler {
Repository: rs/zerolog
Pull request #712 (2025-02-26 13:09:52)

fix: reset condition in burst sampler (#711)
This PR solves the bug reported in https://github.com/rs/zerolog/issues/711.

I also added a simple unit test that uses mocked time to cover this feature.


Patch:
diff --git a/sampler.go b/sampler.go
index 9207a73..19e04bd 100644
--- a/sampler.go
+++ b/sampler.go
@@ -90,7 +90,7 @@ func (s *BurstSampler) inc() uint32 {
 	now := TimestampFunc().UnixNano()
 	resetAt := atomic.LoadInt64(&s.resetAt)
 	var c uint32
-	if now > resetAt {
+	if now >= resetAt {
 		c = 1
 		atomic.StoreUint32(&s.counter, c)
 		newResetAt := now + s.Period.Nanoseconds()


Test patch:
diff --git a/sampler_test.go b/sampler_test.go
index 248bd30..b986375 100644
--- a/sampler_test.go
+++ b/sampler_test.go
@@ -1,3 +1,4 @@
+//go:build !binary_log
 // +build !binary_log
 
 package zerolog
@@ -103,3 +104,34 @@ func BenchmarkSamplers(b *testing.B) {
 		})
 	}
 }
+
+func TestBurst(t *testing.T) {
+	sampler := &BurstSampler{Burst: 1, Period: time.Second}
+
+	t0 := time.Now()
+	now := t0
+	mockedTime := func() time.Time {
+		return now
+	}
+
+	TimestampFunc = mockedTime
+	defer func() { TimestampFunc = time.Now }()
+
+	scenario := []struct {
+		tm   time.Time
+		want bool
+	}{
+		{t0, true},
+		{t0.Add(time.Second - time.Nanosecond), false},
+		{t0.Add(time.Second), true},
+		{t0.Add(time.Second + time.Nanosecond), false},
+	}
+
+	for i, step := range scenario {
+		now = step.tm
+		got := sampler.Sample(NoLevel)
+		if got != step.want {
+			t.Errorf("step %d (t=%s): expect %t got %t", i, step.tm, step.want, got)
+		}
+	}
+}