Repository: hashicorp/go-hclog
Pull request #93 (2021-05-10 20:17:39)

Add an AdditionalLocationOffset option to LoggerOptions.
Add the ability for wrappers of hclog to have the file and line number
point to their caller, instead of the wrappers, by specifying additional
offsets for the callstack when creating a logger.


Patch:
diff --git a/intlogger.go b/intlogger.go
index f8f6ebd..6099e67 100644
--- a/intlogger.go
+++ b/intlogger.go
@@ -124,7 +124,7 @@ func newLogger(opts *LoggerOptions) *intLogger {
 		independentLevels: opts.IndependentLevels,
 	}
 	if opts.IncludeLocation {
-		l.callerOffset = offsetIntLogger
+		l.callerOffset = offsetIntLogger + opts.AdditionalLocationOffset
 	}
 
 	if l.json {
diff --git a/logger.go b/logger.go
index 83eafc1..7f36b1f 100644
--- a/logger.go
+++ b/logger.go
@@ -235,6 +235,10 @@ type LoggerOptions struct {
 	// Include file and line information in each log line
 	IncludeLocation bool
 
+	// AdditionalLocationOffset is the number of additional stack levels to skip
+	// when finding the file and line information for the log line
+	AdditionalLocationOffset int
+
 	// The time format to use instead of the default
 	TimeFormat string
 


Test patch:
diff --git a/logger_test.go b/logger_test.go
index b1ef347..6e4bd58 100644
--- a/logger_test.go
+++ b/logger_test.go
@@ -176,6 +176,30 @@ func TestLogger(t *testing.T) {
 		assert.Equal(t, "[INFO]  go-hclog/logger_test.go:169: test: this is test: who=programmer why=\"testing is fun\"\n", rest)
 	})
 
+	t.Run("includes the caller location excluding helper functions", func(t *testing.T) {
+		var buf bytes.Buffer
+
+		logMe := func(l Logger) {
+			l.Info("this is test", "who", "programmer", "why", "testing is fun")
+		}
+
+		logger := New(&LoggerOptions{
+			Name:             "test",
+			Output:           &buf,
+			IncludeLocation:  true,
+			AdditionalLocationOffset: 1,
+		})
+
+		logMe(logger)
+
+		str := buf.String()
+		dataIdx := strings.IndexByte(str, ' ')
+		rest := str[dataIdx+1:]
+
+		// This test will break if you move this around, it's line dependent, just fyi
+		assert.Equal(t, "[INFO]  go-hclog/logger_test.go:193: test: this is test: who=programmer why=\"testing is fun\"\n", rest)
+	})
+
 	t.Run("prefixes the name", func(t *testing.T) {
 		var buf bytes.Buffer
 
@@ -805,6 +829,36 @@ func TestLogger_JSON(t *testing.T) {
 		assert.Equal(t, fmt.Sprintf("%v:%d", file, line-1), raw["@caller"])
 	})
 
+	t.Run("includes the caller location excluding helper functions", func(t *testing.T) {
+		var buf bytes.Buffer
+
+		logMe := func(l Logger) {
+			l.Info("this is test", "who", "programmer", "why", "testing is fun")
+		}
+
+		logger := New(&LoggerOptions{
+			Name:             "test",
+			Output:           &buf,
+			JSONFormat:       true,
+			IncludeLocation:  true,
+			AdditionalLocationOffset: 1,
+		})
+
+		logMe(logger)
+		_, file, line, ok := runtime.Caller(0)
+		require.True(t, ok)
+
+		b := buf.Bytes()
+
+		var raw map[string]interface{}
+		if err := json.Unmarshal(b, &raw); err != nil {
+			t.Fatal(err)
+		}
+
+		assert.Equal(t, "this is test", raw["@message"])
+		assert.Equal(t, fmt.Sprintf("%v:%d", file, line-1), raw["@caller"])
+	})
+
 	t.Run("handles non-serializable entries", func(t *testing.T) {
 		var buf bytes.Buffer
 
diff --git a/stdlog_test.go b/stdlog_test.go
index 446fa56..de7c3bf 100644
--- a/stdlog_test.go
+++ b/stdlog_test.go
@@ -181,3 +181,32 @@ func TestFromStandardLogger(t *testing.T) {
 	prefix := "test-stdlib-log "
 	require.Equal(t, prefix, actual[:16])
 }
+
+func TestFromStandardLogger_helper(t *testing.T) {
+	var buf bytes.Buffer
+
+	sl := log.New(&buf, "test-stdlib-log ", log.Ltime)
+
+	hl := FromStandardLogger(sl, &LoggerOptions{
+		Name:             "hclog-inner",
+		IncludeLocation:  true,
+		AdditionalLocationOffset: 1,
+	})
+
+	helper := func() {
+		hl.Info("this is a test", "name", "tester", "count", 1)
+	}
+
+	helper()
+	_, file, line, ok := runtime.Caller(0)
+	require.True(t, ok)
+
+	actual := buf.String()
+	suffix := fmt.Sprintf(
+		"[INFO]  go-hclog/%s:%d: hclog-inner: this is a test: name=tester count=1\n",
+		filepath.Base(file), line-1)
+	require.Equal(t, suffix, actual[25:])
+
+	prefix := "test-stdlib-log "
+	require.Equal(t, prefix, actual[:16])
+}