Merge pull request #2136 from fjl/glog-prefix

logger/glog: improve vmodule
This commit is contained in:
Felix Lange 2016-01-25 23:35:25 +01:00
commit f2ab351e8d
7 changed files with 112 additions and 92 deletions

View File

@ -63,11 +63,17 @@
// Enable V-leveled logging at the specified level. // Enable V-leveled logging at the specified level.
// -vmodule="" // -vmodule=""
// The syntax of the argument is a comma-separated list of pattern=N, // The syntax of the argument is a comma-separated list of pattern=N,
// where pattern is a literal file name (minus the ".go" suffix) or // where pattern is a literal file name or "glob" pattern matching
// "glob" pattern and N is a V level. For instance, // and N is a V level. For instance,
// -vmodule=gopher*=3
// sets the V level to 3 in all Go files whose names begin "gopher".
// //
// -vmodule=gopher.go=3
// sets the V level to 3 in all Go files named "gopher.go".
//
// -vmodule=foo=3
// sets V to 3 in all files of any packages whose import path ends in "foo".
//
// -vmodule=foo/*=3
// sets V to 3 in all files of any packages whose import path contains "foo".
package glog package glog
import ( import (
@ -78,7 +84,7 @@ import (
"io" "io"
stdLog "log" stdLog "log"
"os" "os"
"path/filepath" "regexp"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
@ -113,11 +119,30 @@ var severityName = []string{
fatalLog: "FATAL", fatalLog: "FATAL",
} }
// these path prefixes are trimmed for display, but not when
// matching vmodule filters.
var trimPrefixes = []string{
"/github.com/ethereum/go-ethereum",
"/github.com/ethereum/ethash",
}
func trimToImportPath(file string) string {
if root := strings.LastIndex(file, "src/"); root != 0 {
file = file[root+3:]
}
return file
}
// SetV sets the global verbosity level // SetV sets the global verbosity level
func SetV(v int) { func SetV(v int) {
logging.verbosity.set(Level(v)) logging.verbosity.set(Level(v))
} }
// SetVmodule sets the global verbosity patterns.
func SetVmodule(pat string) error {
return logging.vmodule.Set(pat)
}
// SetToStderr sets the global output style // SetToStderr sets the global output style
func SetToStderr(toStderr bool) { func SetToStderr(toStderr bool) {
logging.toStderr = toStderr logging.toStderr = toStderr
@ -261,21 +286,10 @@ type moduleSpec struct {
// modulePat contains a filter for the -vmodule flag. // modulePat contains a filter for the -vmodule flag.
// It holds a verbosity level and a file pattern to match. // It holds a verbosity level and a file pattern to match.
type modulePat struct { type modulePat struct {
pattern string pattern *regexp.Regexp
literal bool // The pattern is a literal string
level Level level Level
} }
// match reports whether the file matches the pattern. It uses a string
// comparison if the pattern contains no metacharacters.
func (m *modulePat) match(file string) bool {
if m.literal {
return file == m.pattern
}
match, _ := filepath.Match(m.pattern, file)
return match
}
func (m *moduleSpec) String() string { func (m *moduleSpec) String() string {
// Lock because the type is not atomic. TODO: clean this up. // Lock because the type is not atomic. TODO: clean this up.
logging.mu.Lock() logging.mu.Lock()
@ -322,7 +336,8 @@ func (m *moduleSpec) Set(value string) error {
continue // Ignore. It's harmless but no point in paying the overhead. continue // Ignore. It's harmless but no point in paying the overhead.
} }
// TODO: check syntax of filter? // TODO: check syntax of filter?
filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) re, _ := compileModulePattern(pattern)
filter = append(filter, modulePat{re, Level(v)})
} }
logging.mu.Lock() logging.mu.Lock()
defer logging.mu.Unlock() defer logging.mu.Unlock()
@ -330,10 +345,21 @@ func (m *moduleSpec) Set(value string) error {
return nil return nil
} }
// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters // compiles a vmodule pattern to a regular expression.
// that require filepath.Match to be called to match the pattern. func compileModulePattern(pat string) (*regexp.Regexp, error) {
func isLiteral(pattern string) bool { re := ".*"
return !strings.ContainsAny(pattern, `\*?[]`) for _, comp := range strings.Split(pat, "/") {
if comp == "*" {
re += "(/.*)?"
} else if comp != "" {
// TODO: maybe return error if comp contains *
re += "/" + regexp.QuoteMeta(comp)
}
}
if !strings.HasSuffix(pat, ".go") {
re += "/[^/]+\\.go"
}
return regexp.Compile(re + "$")
} }
// traceLocation represents the setting of the -log_backtrace_at flag. // traceLocation represents the setting of the -log_backtrace_at flag.
@ -556,10 +582,14 @@ func (l *loggingT) header(s severity, depth int) (*buffer, string, int) {
file = "???" file = "???"
line = 1 line = 1
} else { } else {
slash := strings.LastIndex(file, "/") file = trimToImportPath(file)
if slash >= 0 { for _, p := range trimPrefixes {
file = file[slash+1:] if strings.HasPrefix(file, p) {
file = file[len(p):]
break
}
} }
file = file[1:] // drop '/'
} }
return l.formatHeader(s, file, line), file, line return l.formatHeader(s, file, line), file, line
} }
@ -592,9 +622,7 @@ func (l *loggingT) formatHeader(s severity, file string, line int) *buffer {
buf.tmp[14] = '.' buf.tmp[14] = '.'
buf.nDigits(6, 15, now.Nanosecond()/1000, '0') buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
buf.tmp[21] = ' ' buf.tmp[21] = ' '
buf.nDigits(7, 22, pid, ' ') // TODO: should be TID buf.Write(buf.tmp[:22])
buf.tmp[29] = ' '
buf.Write(buf.tmp[:30])
buf.WriteString(file) buf.WriteString(file)
buf.tmp[0] = ':' buf.tmp[0] = ':'
n := buf.someDigits(1, line) n := buf.someDigits(1, line)
@ -976,15 +1004,9 @@ func (lb logBridge) Write(b []byte) (n int, err error) {
func (l *loggingT) setV(pc uintptr) Level { func (l *loggingT) setV(pc uintptr) Level {
fn := runtime.FuncForPC(pc) fn := runtime.FuncForPC(pc)
file, _ := fn.FileLine(pc) file, _ := fn.FileLine(pc)
// The file is something like /a/b/c/d.go. We want just the d. file = trimToImportPath(file)
if strings.HasSuffix(file, ".go") {
file = file[:len(file)-3]
}
if slash := strings.LastIndex(file, "/"); slash >= 0 {
file = file[slash+1:]
}
for _, filter := range l.vmodule.filter { for _, filter := range l.vmodule.filter {
if filter.match(file) { if filter.pattern.MatchString(file) {
l.vmap[pc] = filter.level l.vmap[pc] = filter.level
return filter.level return filter.level
} }

View File

@ -180,7 +180,7 @@ func TestHeader(t *testing.T) {
pid = 1234 pid = 1234
Info("test") Info("test")
var line int var line int
format := "I0102 15:04:05.067890 1234 glog_test.go:%d] test\n" format := "I0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n"
n, err := fmt.Sscanf(contents(infoLog), format, &line) n, err := fmt.Sscanf(contents(infoLog), format, &line)
if n != 1 || err != nil { if n != 1 || err != nil {
t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
@ -253,7 +253,7 @@ func TestV(t *testing.T) {
func TestVmoduleOn(t *testing.T) { func TestVmoduleOn(t *testing.T) {
setFlags() setFlags()
defer logging.swap(logging.newBuffers()) defer logging.swap(logging.newBuffers())
logging.vmodule.Set("glog_test=2") logging.vmodule.Set("glog_test.go=2")
defer logging.vmodule.Set("") defer logging.vmodule.Set("")
if !V(1) { if !V(1) {
t.Error("V not enabled for 1") t.Error("V not enabled for 1")
@ -290,22 +290,43 @@ func TestVmoduleOff(t *testing.T) {
} }
} }
var patternTests = []struct{ input, want string }{
{"foo/bar/x.go", ".*/foo/bar/x\\.go$"},
{"foo/*/x.go", ".*/foo(/.*)?/x\\.go$"},
{"foo/*", ".*/foo(/.*)?/[^/]+\\.go$"},
}
func TestCompileModulePattern(t *testing.T) {
for _, test := range patternTests {
re, err := compileModulePattern(test.input)
if err != nil {
t.Fatalf("%s: %v", err)
}
if re.String() != test.want {
t.Errorf("mismatch for %q: got %q, want %q", test.input, re.String(), test.want)
}
}
}
// vGlobs are patterns that match/don't match this file at V=2. // vGlobs are patterns that match/don't match this file at V=2.
var vGlobs = map[string]bool{ var vGlobs = map[string]bool{
// Easy to test the numeric match here. // Easy to test the numeric match here.
"glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail. "glog_test.go=1": false, // If -vmodule sets V to 1, V(2) will fail.
"glog_test=2": true, "glog_test.go=2": true,
"glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed. "glog_test.go=3": true, // If -vmodule sets V to 1, V(3) will succeed.
// These all use 2 and check the patterns. All are true.
"*=2": true, // Import path prefix matching
"?l*=2": true, "logger/glog=1": false,
"????_*=2": true, "logger/glog=2": true,
"??[mno]?_*t=2": true, "logger/glog=3": true,
// These all use 2 and check the patterns. All are false.
"*x=2": false, // Import path glob matching
"m*=2": false, "logger/*=1": false,
"??_*=2": false, "logger/*=2": true,
"?[abc]?_*t=2": false, "logger/*=3": true,
// These all use 2 and check the patterns.
"*=2": true,
} }
// Test that vmodule globbing works as advertised. // Test that vmodule globbing works as advertised.

View File

@ -138,6 +138,11 @@ func (api *PrivateDebugAPI) Verbosity(level int) {
glog.SetV(level) glog.SetV(level)
} }
// Vmodule updates the node's logging verbosity pattern.
func (api *PrivateDebugAPI) Vmodule(pattern string) error {
return glog.SetVmodule(pattern)
}
// PublicDebugAPI is the collection of debugging related API methods exposed over // PublicDebugAPI is the collection of debugging related API methods exposed over
// both secure and unsecure RPC channels. // both secure and unsecure RPC channels.
type PublicDebugAPI struct { type PublicDebugAPI struct {

View File

@ -31,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
@ -55,7 +54,6 @@ var (
"admin_nodeInfo": (*adminApi).NodeInfo, "admin_nodeInfo": (*adminApi).NodeInfo,
"admin_exportChain": (*adminApi).ExportChain, "admin_exportChain": (*adminApi).ExportChain,
"admin_importChain": (*adminApi).ImportChain, "admin_importChain": (*adminApi).ImportChain,
"admin_verbosity": (*adminApi).Verbosity,
"admin_setSolc": (*adminApi).SetSolc, "admin_setSolc": (*adminApi).SetSolc,
"admin_datadir": (*adminApi).DataDir, "admin_datadir": (*adminApi).DataDir,
"admin_startRPC": (*adminApi).StartRPC, "admin_startRPC": (*adminApi).StartRPC,
@ -225,16 +223,6 @@ func (self *adminApi) ExportChain(req *shared.Request) (interface{}, error) {
return true, nil return true, nil
} }
func (self *adminApi) Verbosity(req *shared.Request) (interface{}, error) {
args := new(VerbosityArgs)
if err := self.coder.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
glog.SetV(args.Level)
return true, nil
}
func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) { func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) {
args := new(SetSolcArgs) args := new(SetSolcArgs)
if err := self.coder.Decode(req.Params, &args); err != nil { if err := self.coder.Decode(req.Params, &args); err != nil {

View File

@ -69,28 +69,6 @@ func (args *ImportExportChainArgs) UnmarshalJSON(b []byte) (err error) {
return nil return nil
} }
type VerbosityArgs struct {
Level int
}
func (args *VerbosityArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{}
if err := json.Unmarshal(b, &obj); err != nil {
return shared.NewDecodeParamError(err.Error())
}
if len(obj) != 1 {
return shared.NewDecodeParamError("Expected enode as argument")
}
level, err := numString(obj[0])
if err == nil {
args.Level = int(level.Int64())
}
return nil
}
type SetSolcArgs struct { type SetSolcArgs struct {
Path string Path string
} }

View File

@ -45,12 +45,6 @@ web3._extend({
params: 2, params: 2,
inputFormatter: [null, null] inputFormatter: [null, null]
}), }),
new web3._extend.Method({
name: 'verbosity',
call: 'admin_verbosity',
params: 1,
inputFormatter: [web3._extend.utils.fromDecimal]
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'setSolc', name: 'setSolc',
call: 'admin_setSolc', call: 'admin_setSolc',

View File

@ -62,7 +62,19 @@ web3._extend({
call: 'debug_metrics', call: 'debug_metrics',
params: 1, params: 1,
inputFormatter: [null] inputFormatter: [null]
}) }),
new web3._extend.Method({
name: 'verbosity',
call: 'debug_verbosity',
params: 1,
inputFormatter: [web3._extend.utils.fromDecimal]
}),
new web3._extend.Method({
name: 'vmodule',
call: 'debug_vmodule',
params: 1,
inputFormatter: [null]
}),
], ],
properties: properties:
[ [