|
9 | 9 | "io"
|
10 | 10 | "os"
|
11 | 11 | "path/filepath"
|
| 12 | + "runtime/debug" |
12 | 13 | "strconv"
|
13 | 14 | "strings"
|
14 | 15 | "time"
|
@@ -64,7 +65,8 @@ func Fmt(w io.Writer, ent slog.SinkEntry) string {
|
64 | 65 | ents += fmt.Sprintf("%v\t", loggerName)
|
65 | 66 | }
|
66 | 67 |
|
67 |
| - loc := fmt.Sprintf("<%v:%v>", filepath.Base(ent.File), ent.Line) |
| 68 | + hpath, hfn := humanPathAndFunc(ent.File, ent.Func) |
| 69 | + loc := fmt.Sprintf("<%v:%v> %v", hpath, ent.Line, hfn) |
68 | 70 | loc = c(w, color.FgCyan).Sprint(loc)
|
69 | 71 | ents += fmt.Sprintf("%v\t", loc)
|
70 | 72 |
|
@@ -194,3 +196,64 @@ func quoteKey(key string) string {
|
194 | 196 | // Replace spaces in the map keys with underscores.
|
195 | 197 | return strings.ReplaceAll(key, " ", "_")
|
196 | 198 | }
|
| 199 | + |
| 200 | +var mainPackagePath string |
| 201 | +var mainModulePath string |
| 202 | + |
| 203 | +func init() { |
| 204 | + // Unfortunately does not work for tests yet :( |
| 205 | + // See https://github.com/golang/go/issues/33976 |
| 206 | + bi, ok := debug.ReadBuildInfo() |
| 207 | + if !ok { |
| 208 | + return |
| 209 | + } |
| 210 | + mainPackagePath = bi.Path |
| 211 | + mainModulePath = bi.Main.Path |
| 212 | +} |
| 213 | + |
| 214 | +// humanPathAndFunc takes the absolute path to a file and an absolute module path to a |
| 215 | +// function in that file and returns the module path to the file. It also returns |
| 216 | +// the path to the function stripped of its module prefix. |
| 217 | +// |
| 218 | +// If the file is in the main Go module then its path is returned |
| 219 | +// relative to the main Go module's root. |
| 220 | +// |
| 221 | +// fn is from https://pkg.go.dev/runtime#Func.Name |
| 222 | +func humanPathAndFunc(filename, fn string) (hpath, hfn string) { |
| 223 | + // pkgDir is the dir of the pkg. |
| 224 | + // e.g. cdr.dev/slog/internal |
| 225 | + // base is the package name and the function name separated by a period. |
| 226 | + // e.g. entryhuman.humanPathAndFunc |
| 227 | + // There can be multiple periods when methods of types are involved. |
| 228 | + pkgDir, base := filepath.Split(fn) |
| 229 | + s := strings.Split(base, ".") |
| 230 | + pkg := s[0] |
| 231 | + hfn = strings.Join(s[1:], ".") |
| 232 | + |
| 233 | + if pkg == "main" { |
| 234 | + // This happens with go build main.go |
| 235 | + if mainPackagePath == "command-line-arguments" { |
| 236 | + // Without a real mainPath, we can't find the path to the file |
| 237 | + // relative to the module. So we just return the base. |
| 238 | + return filepath.Base(filename), hfn |
| 239 | + } |
| 240 | + // Go doesn't return the full main path in runtime.Func.Name() |
| 241 | + // It just returns the path "main" |
| 242 | + // Only runtime.ReadBuildInfo returns it so we have to check and replace. |
| 243 | + pkgDir = mainPackagePath |
| 244 | + // pkg main isn't reflected on the disk so we should not add it |
| 245 | + // into the pkgpath. |
| 246 | + pkg = "" |
| 247 | + } |
| 248 | + |
| 249 | + hpath = filepath.Join(pkgDir, pkg, filepath.Base(filename)) |
| 250 | + |
| 251 | + if mainModulePath != "" { |
| 252 | + relhpath, err := filepath.Rel(mainModulePath, hpath) |
| 253 | + if err == nil { |
| 254 | + hpath = "./" + relhpath |
| 255 | + } |
| 256 | + } |
| 257 | + |
| 258 | + return hpath, hfn |
| 259 | +} |
0 commit comments