nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: sirupsen/logrus
Pull request #857 (2018-11-05 12:36:29)

fix race condition caused by writing to entry.Data
This PR should be able to fix https://github.com/sirupsen/logrus/issues/468, and it uses the same technique as JSONFormatter.




Patch:
diff --git a/text_formatter.go b/text_formatter.go
index 49ec92f..17f0004 100644
--- a/text_formatter.go
+++ b/text_formatter.go
@@ -107,14 +107,17 @@ func (f *TextFormatter) isColored() bool {
 
 // Format renders a single log entry
 func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
-	prefixFieldClashes(entry.Data, f.FieldMap, entry.HasCaller())
-
-	keys := make([]string, 0, len(entry.Data))
-	for k := range entry.Data {
+	data := make(Fields)
+	for k, v := range entry.Data {
+		data[k] = v
+	}
+	prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
+	keys := make([]string, 0, len(data))
+	for k := range data {
 		keys = append(keys, k)
 	}
 
-	fixedKeys := make([]string, 0, 4+len(entry.Data))
+	fixedKeys := make([]string, 0, 4+len(data))
 	if !f.DisableTimestamp {
 		fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
 	}
@@ -160,7 +163,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
 		timestampFormat = defaultTimestampFormat
 	}
 	if f.isColored() {
-		f.printColored(b, entry, keys, timestampFormat)
+		f.printColored(b, entry, keys, data, timestampFormat)
 	} else {
 		for _, key := range fixedKeys {
 			var value interface{}
@@ -178,7 +181,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
 			case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
 				value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
 			default:
-				value = entry.Data[key]
+				value = data[key]
 			}
 			f.appendKeyValue(b, key, value)
 		}
@@ -188,7 +191,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
 	return b.Bytes(), nil
 }
 
-func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
+func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
 	var levelColor int
 	switch entry.Level {
 	case DebugLevel, TraceLevel:
@@ -225,7 +228,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
 	}
 	for _, k := range keys {
-		v := entry.Data[k]
+		v := data[k]
 		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
 		f.appendValue(b, v)
 	}


Test patch:
diff --git a/text_formatter_test.go b/text_formatter_test.go
index b0d3a91..e07809d 100644
--- a/text_formatter_test.go
+++ b/text_formatter_test.go
@@ -144,7 +144,7 @@ func TestDisableLevelTruncation(t *testing.T) {
 		tf := &TextFormatter{DisableLevelTruncation: disabled}
 		var b bytes.Buffer
 		entry.Level = level
-		tf.printColored(&b, entry, keys, timestampFormat)
+		tf.printColored(&b, entry, keys, nil, timestampFormat)
 		logLine := (&b).String()
 		if disabled {
 			expected := strings.ToUpper(level.String())
Repository: sirupsen/logrus
Pull request #870 (2018-12-05 18:24:25)

Skip func pointer type value in fields
Before there was introduced a fix for JSONFormatter when func type value
passed as Field. This commit does the same but for pointer to func.

Ref:
https://github.com/sirupsen/logrus/issues/642
https://github.com/sirupsen/logrus/pull/832


Patch:
diff --git a/entry.go b/entry.go
index cc85d3a..4c00ba5 100644
--- a/entry.go
+++ b/entry.go
@@ -108,18 +108,27 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
 	for k, v := range entry.Data {
 		data[k] = v
 	}
-	var field_err string
+	var fieldErr string
 	for k, v := range fields {
-		if t := reflect.TypeOf(v); t != nil && t.Kind() == reflect.Func {
-			field_err = fmt.Sprintf("can not add field %q", k)
+		isErrField := false
+		if t := reflect.TypeOf(v); t != nil {
+			switch t.Kind() {
+			case reflect.Func:
+				isErrField = true
+			case reflect.Ptr:
+				isErrField = t.Elem().Kind() == reflect.Func
+			}
+		}
+		if isErrField {
+			fieldErr = fmt.Sprintf("can not add field %q", k)
 			if entry.err != "" {
-				field_err = entry.err + ", " + field_err
+				fieldErr = entry.err + ", " + fieldErr
 			}
 		} else {
 			data[k] = v
 		}
 	}
-	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: field_err}
+	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr}
 }
 
 // Overrides the time of the Entry.


Test patch:
diff --git a/entry_test.go b/entry_test.go
index a81e2b3..7afcc11 100644
--- a/entry_test.go
+++ b/entry_test.go
@@ -113,3 +113,16 @@ func TestEntryHooksPanic(t *testing.T) {
 	entry := NewEntry(logger)
 	entry.Info(badMessage)
 }
+
+func TestEntryWithIncorrectField(t *testing.T) {
+	assert := assert.New(t)
+
+	fn := func() {}
+
+	e := Entry{}
+	eWithFunc := e.WithFields(Fields{"func": fn})
+	eWithFuncPtr := e.WithFields(Fields{"funcPtr": &fn})
+
+	assert.Equal(eWithFunc.err, `can not add field "func"`)
+	assert.Equal(eWithFuncPtr.err, `can not add field "funcPtr"`)
+}
Repository: sirupsen/logrus
Pull request #909 (2019-02-27 12:09:16)

Add a CallerPrettyfier callback to the json formatter


Patch:
diff --git a/entry.go b/entry.go
index c5b489e..fd37728 100644
--- a/entry.go
+++ b/entry.go
@@ -392,4 +392,4 @@ func (entry *Entry) Panicln(args ...interface{}) {
 func (entry *Entry) sprintlnn(args ...interface{}) string {
 	msg := fmt.Sprintln(args...)
 	return msg[:len(msg)-1]
-}
\ No newline at end of file
+}
diff --git a/json_formatter.go b/json_formatter.go
index 2605753..c494d07 100644
--- a/json_formatter.go
+++ b/json_formatter.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
+	"runtime"
 )
 
 type fieldKey string
@@ -42,6 +43,12 @@ type JSONFormatter struct {
 	// }
 	FieldMap FieldMap
 
+	// CallerPrettyfier can be set by the user to modify the content
+	// of the function and file keys in the json data when ReportCaller is
+	// activated. If any of the returned value is the empty string the
+	// corresponding key will be removed from json fields.
+	CallerPrettyfier func(*runtime.Frame) (function string, file string)
+
 	// PrettyPrint will indent all json logs
 	PrettyPrint bool
 }
@@ -82,8 +89,18 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 	data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
 	data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
 	if entry.HasCaller() {
-		data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller.Function
-		data[f.FieldMap.resolve(FieldKeyFile)] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
+		funcVal := entry.Caller.Function
+		fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
+		if f.CallerPrettyfier != nil {
+			funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
+			fmt.Println(funcVal, fileVal)
+		}
+		if funcVal != "" {
+			data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
+		}
+		if fileVal != "" {
+			data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
+		}
 	}
 
 	var b *bytes.Buffer


Test patch:
diff --git a/logrus_test.go b/logrus_test.go
index f6fe8bf..47a386f 100644
--- a/logrus_test.go
+++ b/logrus_test.go
@@ -40,7 +40,18 @@ func TestReportCallerWhenConfigured(t *testing.T) {
 		assert.Equal(t, "testWithCaller", fields["msg"])
 		assert.Equal(t, "info", fields["level"])
 		assert.Equal(t,
-			"github.com/sirupsen/logrus_test.TestReportCallerWhenConfigured.func3", fields["func"])
+			"github.com/sirupsen/logrus_test.TestReportCallerWhenConfigured.func3", fields[FieldKeyFunc])
+	})
+
+	LogAndAssertJSON(t, func(log *Logger) {
+		log.ReportCaller = true
+		log.Formatter.(*JSONFormatter).CallerPrettyfier = func(f *runtime.Frame) (string, string) {
+			return "somekindoffunc", "thisisafilename"
+		}
+		log.Print("testWithCallerPrettyfier")
+	}, func(fields Fields) {
+		assert.Equal(t, "somekindoffunc", fields[FieldKeyFunc])
+		assert.Equal(t, "thisisafilename", fields[FieldKeyFile])
 	})
 }
Repository: sirupsen/logrus
Pull request #913 (2019-03-03 10:54:37)

Example caller prettyfier


Patch:
diff --git a/json_formatter.go b/json_formatter.go
index c494d07..ae95040 100644
--- a/json_formatter.go
+++ b/json_formatter.go
@@ -93,7 +93,6 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 		fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
 		if f.CallerPrettyfier != nil {
 			funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
-			fmt.Println(funcVal, fileVal)
 		}
 		if funcVal != "" {
 			data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal


Test patch:
diff --git a/example_custom_caller_test.go b/example_custom_caller_test.go
new file mode 100644
index 0000000..e0023b9
--- /dev/null
+++ b/example_custom_caller_test.go
@@ -0,0 +1,28 @@
+package logrus_test
+
+import (
+	"os"
+	"path"
+	"runtime"
+	"strings"
+
+	"github.com/sirupsen/logrus"
+)
+
+func ExampleCustomFormatter() {
+	l := logrus.New()
+	l.SetReportCaller(true)
+	l.Out = os.Stdout
+	l.Formatter = &logrus.JSONFormatter{
+		DisableTimestamp: true,
+		CallerPrettyfier: func(f *runtime.Frame) (string, string) {
+			s := strings.Split(f.Function, ".")
+			funcname := s[len(s)-1]
+			_, filename := path.Split(f.File)
+			return funcname, filename
+		},
+	}
+	l.Info("example of custom format caller")
+	// Output:
+	// {"file":"example_custom_caller_test.go","func":"ExampleCustomFormatter","level":"info","msg":"example of custom format caller"}
+}
Repository: sirupsen/logrus
Pull request #1113 (2020-03-13 17:24:29)

resolved conflicts for DisableHTMLEscape in json_formatter.go pull request #524 
resolved conflicts in pull request https://github.com/sirupsen/logrus/pull/524


Patch:
diff --git a/json_formatter.go b/json_formatter.go
index 098a21a..ba7f237 100644
--- a/json_formatter.go
+++ b/json_formatter.go
@@ -28,6 +28,9 @@ type JSONFormatter struct {
 	// DisableTimestamp allows disabling automatic timestamps in output
 	DisableTimestamp bool
 
+	// DisableHTMLEscape allows disabling html escaping in output
+	DisableHTMLEscape bool
+
 	// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
 	DataKey string
 
@@ -110,6 +113,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 	}
 
 	encoder := json.NewEncoder(b)
+	encoder.SetEscapeHTML(!f.DisableHTMLEscape)
 	if f.PrettyPrint {
 		encoder.SetIndent("", "  ")
 	}


Test patch:
diff --git a/json_formatter_test.go b/json_formatter_test.go
index 695c36e..7a48f2d 100644
--- a/json_formatter_test.go
+++ b/json_formatter_test.go
@@ -344,3 +344,29 @@ func TestJSONEnableTimestamp(t *testing.T) {
 		t.Error("Timestamp not present", s)
 	}
 }
+
+func TestJSONDisableHTMLEscape(t *testing.T) {
+	formatter := &JSONFormatter{DisableHTMLEscape: true}
+
+	b, err := formatter.Format(&Entry{Message: "& < >"})
+	if err != nil {
+		t.Fatal("Unable to format entry: ", err)
+	}
+	s := string(b)
+	if !strings.Contains(s, "& < >") {
+		t.Error("Message should not be HTML escaped", s)
+	}
+}
+
+func TestJSONEnableHTMLEscape(t *testing.T) {
+	formatter := &JSONFormatter{}
+
+	b, err := formatter.Format(&Entry{Message: "& < >"})
+	if err != nil {
+		t.Fatal("Unable to format entry: ", err)
+	}
+	s := string(b)
+	if !(strings.Contains(s, "u0026") && strings.Contains(s, "u003e") && strings.Contains(s, "u003c")) {
+		t.Error("Message should be HTML escaped", s)
+	}
+}
Repository: sirupsen/logrus
Pull request #1116 (2020-03-19 09:38:14)

Resolve race condition with SetReportCaller() and Entry
I came across this issue in a project I am working on and discovered that there was already an issue for it (#954).

I added a test to prove that the race was fixed and ensure that it doesn't come back.


Patch:
diff --git a/entry.go b/entry.go
index 1bf127c..c6da562 100644
--- a/entry.go
+++ b/entry.go
@@ -232,9 +232,11 @@ func (entry Entry) log(level Level, msg string) {
 
 	entry.Level = level
 	entry.Message = msg
+	entry.Logger.mu.Lock()
 	if entry.Logger.ReportCaller {
 		entry.Caller = getCaller()
 	}
+	entry.Logger.mu.Unlock()
 
 	entry.fireHooks()
 


Test patch:
diff --git a/entry_test.go b/entry_test.go
index 423e5ed..30eddae 100644
--- a/entry_test.go
+++ b/entry_test.go
@@ -243,3 +243,14 @@ func TestEntryLogfLevel(t *testing.T) {
 	entry.Logf(WarnLevel, "%s", "warn")
 	assert.Contains(t, buffer.String(), "warn")
 }
+
+func TestEntryReportCallerRace(t *testing.T) {
+	logger := New()
+	entry := NewEntry(logger)
+	go func() {
+		logger.SetReportCaller(true)
+	}()
+	go func() {
+		entry.Info("should not race")
+	}()
+}
Repository: sirupsen/logrus
Pull request #1134 (2020-04-23 12:03:17)

Adds flag to disable quotes in TextFormatter
Relates to https://github.com/sirupsen/logrus/issues/654 and https://github.com/sirupsen/logrus/issues/608

In some cases, we don't want the log values to be quoted or escaped, so this is adding a simple flag in TextFormatter to disable quotes (which also disables escaping) in the field values.


Patch:
diff --git a/text_formatter.go b/text_formatter.go
index 2d15a23..431b5fd 100644
--- a/text_formatter.go
+++ b/text_formatter.go
@@ -37,6 +37,9 @@ type TextFormatter struct {
 	// Force quoting of all values
 	ForceQuote bool
 
+	// DisableQuote disables quoting for all values
+	DisableQuote bool
+
 	// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
 	EnvironmentOverrideColors bool
 
@@ -292,6 +295,9 @@ func (f *TextFormatter) needsQuoting(text string) bool {
 	if f.QuoteEmptyFields && len(text) == 0 {
 		return true
 	}
+	if f.DisableQuote {
+		return false
+	}
 	for _, ch := range text {
 		if !((ch >= 'a' && ch <= 'z') ||
 			(ch >= 'A' && ch <= 'Z') ||


Test patch:
diff --git a/text_formatter_test.go b/text_formatter_test.go
index 5d94ebb..5b1cc0a 100644
--- a/text_formatter_test.go
+++ b/text_formatter_test.go
@@ -59,6 +59,7 @@ func TestQuoting(t *testing.T) {
 	checkQuoting(false, "foo@bar")
 	checkQuoting(false, "foobar^")
 	checkQuoting(false, "+/-_^@f.oobar")
+	checkQuoting(true, "foo\n\rbar")
 	checkQuoting(true, "foobar$")
 	checkQuoting(true, "&foobar")
 	checkQuoting(true, "x y")
@@ -70,13 +71,30 @@ func TestQuoting(t *testing.T) {
 	tf.QuoteEmptyFields = true
 	checkQuoting(true, "")
 	checkQuoting(false, "abcd")
+	checkQuoting(true, "foo\n\rbar")
 	checkQuoting(true, errors.New("invalid argument"))
 
 	// Test forcing quotes.
 	tf.ForceQuote = true
 	checkQuoting(true, "")
 	checkQuoting(true, "abcd")
+	checkQuoting(true, "foo\n\rbar")
 	checkQuoting(true, errors.New("invalid argument"))
+
+	// Test forcing quotes when also disabling them.
+	tf.DisableQuote = true
+	checkQuoting(true, "")
+	checkQuoting(true, "abcd")
+	checkQuoting(true, "foo\n\rbar")
+	checkQuoting(true, errors.New("invalid argument"))
+
+	// Test disabling quotes
+	tf.ForceQuote = false
+	tf.QuoteEmptyFields = false
+	checkQuoting(false, "")
+	checkQuoting(false, "abcd")
+	checkQuoting(false, "foo\n\rbar")
+	checkQuoting(false, errors.New("invalid argument"))
 }
 
 func TestEscaping(t *testing.T) {
Repository: sirupsen/logrus
Pull request #1263 (2021-06-16 09:57:14)

fix race condition for SetFormatter and SetReportCaller
Unfortunately https://github.com/sirupsen/logrus/issues/1233 didn't really fix the race condition, the test just wasn't triggering the race condition to be detected...

`*Entry.write` just needs a lock for the whole function.


Patch:
diff --git a/entry.go b/entry.go
index 0a5a832..ad9957b 100644
--- a/entry.go
+++ b/entry.go
@@ -284,13 +284,13 @@ func (entry *Entry) fireHooks() {
 }
 
 func (entry *Entry) write() {
+	entry.Logger.mu.Lock()
+	defer entry.Logger.mu.Unlock()
 	serialized, err := entry.Logger.Formatter.Format(entry)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
 		return
 	}
-	entry.Logger.mu.Lock()
-	defer entry.Logger.mu.Unlock()
 	if _, err := entry.Logger.Out.Write(serialized); err != nil {
 		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
 	}


Test patch:
diff --git a/entry_test.go b/entry_test.go
index 035e411..41c47a2 100644
--- a/entry_test.go
+++ b/entry_test.go
@@ -269,6 +269,12 @@ func TestEntryLogfLevel(t *testing.T) {
 func TestEntryReportCallerRace(t *testing.T) {
 	logger := New()
 	entry := NewEntry(logger)
+
+	// logging before SetReportCaller has the highest chance of causing a race condition
+	// to be detected, but doing it twice just to increase the likelyhood of detecting the race
+	go func() {
+		entry.Info("should not race")
+	}()
 	go func() {
 		logger.SetReportCaller(true)
 	}()
@@ -276,3 +282,20 @@ func TestEntryReportCallerRace(t *testing.T) {
 		entry.Info("should not race")
 	}()
 }
+
+func TestEntryFormatterRace(t *testing.T) {
+	logger := New()
+	entry := NewEntry(logger)
+
+	// logging before SetReportCaller has the highest chance of causing a race condition
+	// to be detected, but doing it twice just to increase the likelyhood of detecting the race
+	go func() {
+		entry.Info("should not race")
+	}()
+	go func() {
+		logger.SetFormatter(&TextFormatter{})
+	}()
+	go func() {
+		entry.Info("should not race")
+	}()
+}