From a9215af98c6f8594f8dbb619007d3b12c6dc506a Mon Sep 17 00:00:00 2001 From: Lorenzo Pierfederici Date: Sat, 2 Mar 2013 20:18:32 -0800 Subject: [PATCH 1/5] simplify int/string level names, make Logger public. Users of the package can now create their own loggers and extend them. --- log/log.go | 129 +++++++++++++++++++++++++---------------------------- 1 file changed, 61 insertions(+), 68 deletions(-) diff --git a/log/log.go b/log/log.go index df36ed5..67665ca 100644 --- a/log/log.go +++ b/log/log.go @@ -37,100 +37,93 @@ const ( DEBUG ) -// ErrLevel indicates that the specified log level is not valid +// ErrLevel indicates that the specified log level is not valid. var ErrLevel = errors.New("Unknown log level") -// SetLevel sets the output level for the global logger -func SetLevel(level int) error { - if level > DEBUG { - return ErrLevel - } - l.level = level - return nil +// FlagLevel returns a string flag that can be used with the flag package +// to set the log level in command line applications. +func FlagLevel(flagname string) *string { + return flag.String(flagname, "ERROR", "set loglevel [ERROR|INFO|DEBUG]") } -// SetLevelString sets the output level for the global logger -func SetLevelString(name string) error { - level, err := StringToLevel(name) - if err != nil || level > DEBUG { - return ErrLevel - } - l.level = level - return nil +type Logger struct { + level int } -// LevelToString converts a log level to its string representation -func LevelToString(level int) (string, error) { - var s string +// SetLevel sets the output level for the logger. +// 'level' can be either a string or an int. +func (l *Logger) SetLevel(level interface{}) error { switch level { - case ERROR: - s = "ERROR" - case INFO: - s = "INFO" - case DEBUG: - s = "DEBUG" + case ERROR, "ERROR": + l.level = ERROR + case INFO, "INFO": + l.level = INFO + case DEBUG, "DEBUG": + l.level = DEBUG default: - return "", ErrLevel + return ErrLevel } - return s, nil + return nil } -// StringToLevel converts a string to the corresponding log level -func StringToLevel(name string) (int, error) { - var level int - switch name { - case "ERROR": - level = ERROR - case "INFO": - level = INFO - case "DEBUG": - level = DEBUG - default: - return -1, ErrLevel +// Log logs a message with custom level and type. +// To log messages at predefined levels you can use the convenience +// functions Fatal(), Error(), Info(), Debug(). +func (l *Logger) Log(level int, msgType string, format string, v ...interface{}) { + if level <= l.level { + msg := fmt.Sprintf(format, v...) + t := time.Now().Format(time.RFC1123) + fmt.Printf("%s [%s]: %s\n", t, msgType, msg) + } - return level, nil } -func FlagLevel(flagname string) *string { - return flag.String(flagname, "ERROR", "set loglevel [ERROR|INFO|DEBUG]") +// Fatal logs a message at "ERROR" level and terminates the program. +func (l *Logger) Fatal(format string, v ...interface{}) { + l.Log(ERROR, "ERROR", format, v...) + os.Exit(1) } -type logger struct { - level int +// Error logs a message at "ERROR" level. +func (l *Logger) Error(format string, v ...interface{}) { + l.Log(ERROR, "ERROR", format, v...) } -var l logger +// Info logs a message at "INFO" level. +func (l *Logger) Info(format string, v ...interface{}) { + l.Log(INFO, "INFO", format, v...) +} -func Log(level int, format string, v ...interface{}) { - if level <= l.level { - msgType, err := LevelToString(level) - if err != nil { - Fatal(err.Error()) - } - msg := fmt.Sprintf(format, v...) - t := time.Now().Format(time.RFC1123) - fmt.Printf("%s [%s]: %s\n", t, msgType, msg) +// Debug logs a message at "DEBUG" level. +func (l *Logger) Debug(format string, v ...interface{}) { + l.Log(DEBUG, "DEBUG", format, v...) +} - } + +var DefaultLogger Logger + +// SetLevel sets the output level for the default logger. +func SetLevel(level interface{}) error { + return DefaultLogger.SetLevel(level) } -// Fatal logs a message at "ERROR" level and terminates the program -func Fatal(format string, v ...interface{}) { - Log(ERROR, format, v...) - os.Exit(1) +// Fatal logs a message at "ERROR" level with the default logger and +// terminates the program. +func Fatal(msg string, v ...interface{}) { + DefaultLogger.Fatal(msg, v...) } -// Error logs a message at "ERROR" level -func Error(format string, v ...interface{}) { - Log(ERROR, format, v...) +// Error logs a message at "ERROR" level with the default logger. +func Error(msg string, v ...interface{}) { + DefaultLogger.Error(msg, v...) } -// Info logs a message at "INFO" level -func Info(format string, v ...interface{}) { - Log(INFO, format, v...) +// Info logs a message at "INFO" level with the default logger. +func Info(msg string, v ...interface{}) { + DefaultLogger.Info(msg, v...) } -// Debug logs a message at "DEBUG" level -func Debug(format string, v ...interface{}) { - Log(DEBUG, format, v...) +// Debug logs a message at "DEBUG" level with the default logger. +func Debug(msg string, v ...interface{}) { + DefaultLogger.Debug(msg, v...) } From 1e7b582a4187576d6d18ff6f7d3d99010799eaca Mon Sep 17 00:00:00 2001 From: Lorenzo Pierfederici Date: Sat, 2 Mar 2013 21:29:32 -0800 Subject: [PATCH 2/5] remove convenience function to create flag. We don't really need this, applications using the package can easily create their own flag. --- log/log.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/log/log.go b/log/log.go index 67665ca..a4a4383 100644 --- a/log/log.go +++ b/log/log.go @@ -40,12 +40,6 @@ const ( // ErrLevel indicates that the specified log level is not valid. var ErrLevel = errors.New("Unknown log level") -// FlagLevel returns a string flag that can be used with the flag package -// to set the log level in command line applications. -func FlagLevel(flagname string) *string { - return flag.String(flagname, "ERROR", "set loglevel [ERROR|INFO|DEBUG]") -} - type Logger struct { level int } From ea1a2392d67a87223434050cda053cd3316bea9d Mon Sep 17 00:00:00 2001 From: Lorenzo Pierfederici Date: Sat, 2 Mar 2013 22:41:26 -0800 Subject: [PATCH 3/5] make Logger more customizable. - a Logger can now write to any io.Writer. - the error return by SetLevel indicates the valued that caused it. --- log/log.go | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/log/log.go b/log/log.go index a4a4383..14cf510 100644 --- a/log/log.go +++ b/log/log.go @@ -23,11 +23,10 @@ Package log contains a simple multi-level logger. package log import ( - "errors" - "flag" "fmt" "os" "time" + "io" ) // Available log levels @@ -37,11 +36,24 @@ const ( DEBUG ) -// ErrLevel indicates that the specified log level is not valid. -var ErrLevel = errors.New("Unknown log level") +// BadLevelError indicates that the specified log level is not valid. +type BadLevelError struct { + level interface{} +} + +func (e *BadLevelError) Error() string { return fmt.Sprintf("Unknown log level: %v", e.level) } +// Logger is a simple multi-level logger. type Logger struct { - level int + Level int + Writer io.Writer +} + +func NewLogger(level interface{}, writer io.Writer) *Logger { + var l Logger + l.SetLevel(level) + l.Writer = writer + return &l } // SetLevel sets the output level for the logger. @@ -49,13 +61,13 @@ type Logger struct { func (l *Logger) SetLevel(level interface{}) error { switch level { case ERROR, "ERROR": - l.level = ERROR + l.Level = ERROR case INFO, "INFO": - l.level = INFO + l.Level = INFO case DEBUG, "DEBUG": - l.level = DEBUG + l.Level = DEBUG default: - return ErrLevel + return &BadLevelError{level} } return nil } @@ -64,10 +76,10 @@ func (l *Logger) SetLevel(level interface{}) error { // To log messages at predefined levels you can use the convenience // functions Fatal(), Error(), Info(), Debug(). func (l *Logger) Log(level int, msgType string, format string, v ...interface{}) { - if level <= l.level { + if level <= l.Level { msg := fmt.Sprintf(format, v...) t := time.Now().Format(time.RFC1123) - fmt.Printf("%s [%s]: %s\n", t, msgType, msg) + fmt.Fprintf(l.Writer, "%s [%s]: %s\n", t, msgType, msg) } } @@ -94,7 +106,7 @@ func (l *Logger) Debug(format string, v ...interface{}) { } -var DefaultLogger Logger +var DefaultLogger = NewLogger(ERROR, os.Stdout) // SetLevel sets the output level for the default logger. func SetLevel(level interface{}) error { From 0a711a4342615a61dbf7826ddbfda4c54ecf51a9 Mon Sep 17 00:00:00 2001 From: Lorenzo Pierfederici Date: Sat, 2 Mar 2013 22:48:56 -0800 Subject: [PATCH 4/5] gofmt --- log/log.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/log/log.go b/log/log.go index 14cf510..74b1131 100644 --- a/log/log.go +++ b/log/log.go @@ -24,9 +24,9 @@ package log import ( "fmt" + "io" "os" "time" - "io" ) // Available log levels @@ -45,7 +45,7 @@ func (e *BadLevelError) Error() string { return fmt.Sprintf("Unknown log level: // Logger is a simple multi-level logger. type Logger struct { - Level int + Level int Writer io.Writer } @@ -105,7 +105,6 @@ func (l *Logger) Debug(format string, v ...interface{}) { l.Log(DEBUG, "DEBUG", format, v...) } - var DefaultLogger = NewLogger(ERROR, os.Stdout) // SetLevel sets the output level for the default logger. From d46474daae331430e453b95761cbc84abda9c68c Mon Sep 17 00:00:00 2001 From: Lorenzo Pierfederici Date: Sun, 3 Mar 2013 22:35:28 -0800 Subject: [PATCH 5/5] add conf package Package conf contains a simple loader for configuration files. --- conf/conf.go | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 conf/conf.go diff --git a/conf/conf.go b/conf/conf.go new file mode 100644 index 0000000..de0ed0e --- /dev/null +++ b/conf/conf.go @@ -0,0 +1,183 @@ +/* +Copyright 2013 Petru Ciobanu, Francesco Paglia, Lorenzo Pierfederici + +This file is part of maponet/utils. + +maponet/utils is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +maponet/utils is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with maponet/utils. If not, see . +*/ + +/* +Package conf contains a simple loader for configuration files. +*/ +package conf + +import ( + "bufio" + "fmt" + "io" + "os" + "regexp" + "strconv" + "sync" + "bytes" +) + +type NotFoundError struct { + key string +} + +func (e *NotFoundError) Error() string { return fmt.Sprintf("Key not found: %s", e.key) } + +type ConfigSyntaxError struct { + line string +} + +func (e *ConfigSyntaxError) Error() string { + return fmt.Sprintf("Config syntax error on line: %s", e.line) +} + +// Regexp patterns +var ( + PatternOption, _ = regexp.Compile("(.*)=([^#]*)") + PatternComment, _ = regexp.Compile("^#") + PatternEmpty, _ = regexp.Compile("^[\t\n\f\r ]$") +) + +// Config is a simple synchronized object that can be used to parse +// configuration files and store key=value pairs +type Config struct { + values map[string]string + mutex sync.RWMutex +} + +func NewConfig() *Config { + var c Config + c.values = make(map[string]string) + return &c +} + +// Parse +func (c *Config) Parse(r *bufio.Reader) error { + for { + l, err := r.ReadBytes('\n') + if err == io.EOF { + break + } else if err != nil { + return err + } + switch { + case PatternEmpty.Match(l): + continue + case PatternComment.Match(l): + continue + case PatternOption.Match(l): + m := PatternOption.FindSubmatch(l) + c.Set(string(bytes.TrimSpace(m[1])), string(bytes.TrimSpace(m[2]))) + default: + return &ConfigSyntaxError{string(l)} + } + } + return nil +} + +// ParseFile +func (c *Config) ParseFile(path string) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + + err = c.Parse(bufio.NewReader(f)) + if err != nil { + return err + } + return nil +} + +// Set +func (c *Config) Set(key, value string) { + c.mutex.Lock() + defer c.mutex.Unlock() + + c.values[key] = value +} + +// GetString +func (c *Config) GetString(key string) (string, error) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + v, ok := c.values[key] + if !ok { + return v, &NotFoundError{key} + } + return v, nil +} + +// GetBool +func (c *Config) GetBool(key string) (bool, error) { + s, err := c.GetString(key) + if err != nil { + return *new(bool), err + } + + return strconv.ParseBool(s) +} + +// GetInt +func (c *Config) GetInt(key string) (int, error) { + s, err := c.GetString(key) + if err != nil { + return *new(int), err + } + + return strconv.Atoi(s) +} + +// GetFloat64 +func (c *Config) GetFloat64(key string) (float64, error) { + s, err := c.GetString(key) + if err != nil { + return *new(float64), err + } + + return strconv.ParseFloat(s, 64) +} + +var DefaultConfig = NewConfig() + +func ParseFile(path string) error { + return DefaultConfig.ParseFile(path) +} + +func Set(key, value string) { + DefaultConfig.Set(key, value) +} + +func GetString(key string) (string, error) { + return DefaultConfig.GetString(key) +} + +func GetBool(key string) (bool, error) { + return DefaultConfig.GetBool(key) +} + +func GetInt(key string) (int, error) { + return DefaultConfig.GetInt(key) +} + +func GetFloat64(key string) (float64, error) { + return DefaultConfig.GetFloat64(key) +}