As a background, I have a logging facility that wants to output filename and line number, as well as manage some other unique-per-caller information. In c++ this is relatively straightforward using static variables, but I'm having trouble coming up with an equivalent in Go.
I've come across runtime.Caller()
, which will get me the PC of the caller (and thus uniquely identify it), but clocking in at ~500ns it's not a cost I want to pay on every invocation of every log statement!
As a really basic example of the behaviour I have
package main
import (
"fmt"
"runtime"
)
type Info struct {
file string
line int
// otherData
}
var infoMap = map[uintptr]Info{}
func Log(str string, args ...interface{}) {
pc, file, line, _ := runtime.Caller(1)
info, found := infoMap[pc]
if !found {
info = Info{file, line}
infoMap[pc] = info
// otherData would get generated here too
}
fmt.Println(info.file, info.line)
}
// client code
func foo() {
// do some work
Log("Here's a message with <> some <> args", "foo", "bar")
// do more work
Log("The cow jumped over the moon")
}
func main() {
foo()
foo()
}
This outputs
/tmp/foo/main.go 33
/tmp/foo/main.go 35
/tmp/foo/main.go 33
/tmp/foo/main.go 35
Now, runtime.Caller(1)
is being evaluated on every call here. Ideally, it is evaluated once per statement.
Something like
func Log(str string, args ...interface{}) {
uniqueId = doSomethingFasterThanCaller()
info, found := infoMap[uniqueId]
if !found {
_, file, line, _ := runtime.Caller(1)
info = Info{file, line}
infoMap[pc] = info
// otherData would get generated here too
}
fmt.Println(info.file, info.line)
}
Or even something done from the caller that allows it to be uniquely identified without hardcoding ids.
func Log(uniqueId int, str string, args ...interface{}) {
info, found := infoMap[uniqueId]
if !found {
_, file, line, _ := runtime.Caller(1)
info = Info{file, line}
infoMap[uniqueId] = info
// otherData would get generated here too
}
fmt.Println(info.file, info.line)
}
func Foo() {
Log( uniqueIdGetter(), "message" )
Log( uniqueIdGetter(), "another message" )
// If I could get the offset from the beginning of the function
// to this line, something like this could work.
//
//Log((int)(reflect.ValueOf(Foo).Pointer()) + CODE_OFFSET, "message")
}
Is this something that can be done natively, or am I stuck with the cost of runtime.Caller
(which does a bunch of extra work above-and-beyond just getting the pc, which is really all I need)?
Aucun commentaire:
Enregistrer un commentaire