diff --git a/cmd/logmania/main.go b/cmd/logmania/main.go new file mode 100644 index 0000000..1b1383b --- /dev/null +++ b/cmd/logmania/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "flag" + "os" + "os/signal" + "syscall" + + "github.com/genofire/logmania/lib" + "github.com/genofire/logmania/log" + _ "github.com/genofire/logmania/log/hook/output" +) + +var ( + configPath string + config *lib.Config +) + +func main() { + flag.StringVar(&configPath, "config", "logmania.conf", "config file") + log.Info("starting logmania") + config, err := lib.ReadConfig(configPath) + if config == nil || err != nil { + log.Panicf("Could not load '%s' for configuration.", configPath) + } + + // Wait for system signal + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGUSR1) + for sig := range sigchan { + switch sig { + case syscall.SIGTERM: + log.Warn("terminated of logmania") + os.Exit(0) + case syscall.SIGQUIT: + quit() + case syscall.SIGHUP: + quit() + case syscall.SIGUSR1: + reload() + } + } +} + +func quit() { + log.Info("quit of logmania") + os.Exit(0) +} + +func reload() { + log.Info("reload config file") + config, err := lib.ReadConfig(configPath) + if config == nil || err != nil { + log.Errorf("Could not load '%s' for new configuration. Skip reload.", configPath) + return + } +} diff --git a/entry/output.go b/entry/output.go deleted file mode 100644 index 40ea9ce..0000000 --- a/entry/output.go +++ /dev/null @@ -1,39 +0,0 @@ -package entry - -import ( - "fmt" - "os" - "time" -) - -var TimeFormat = "2006-01-02 15:04:05" - -var InternelSend = func(e *Entry) { - format := "%s [%s] %s\n" - v := []interface{}{time.Now().Format(TimeFormat), e.Level.String(), e.Text} - if len(e.Fields) > 0 { - format = "%s [%s] %s (%s)\n" - v = append(v, e.FieldString()) - } - text := fmt.Sprintf(format, v...) - - if e.Level == PanicLevel { - panic(text) - } else if e.Level > WarnLevel { - os.Stderr.WriteString(text) - } else { - os.Stdout.WriteString(text) - } -} - -var FieldOutput = func(fields map[string]interface{}) string { - text := "" - for key, value := range fields { - text = fmt.Sprintf("%s %s=%v", text, key, value) - } - return text[1:] -} - -func save(e *Entry) { - InternelSend(e) -} diff --git a/examples/with/main.go b/examples/with/main.go new file mode 100644 index 0000000..1b4bbe8 --- /dev/null +++ b/examples/with/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "time" + + "github.com/genofire/logmania/log" + logClient "github.com/genofire/logmania/log/hook/client" + logOutput "github.com/genofire/logmania/log/hook/output" +) + +func main() { + logClient.Init("ws://localhost:8081/blub", "example") + log.Info("startup") + log.New().AddField("answer", 42).AddFields(map[string]interface{}{"answer": 3, "foo": "bar"}).Warn("Some spezial") + log.Debug("Never shown up") + logOutput.ShowTime = false + logOutput.AboveLevel = log.DebugLevel + log.Debugf("Startup %v", time.Now()) + logOutput.ShowTime = true + log.Panic("let it crash") +} diff --git a/examples/without/main.go b/examples/without/main.go new file mode 100644 index 0000000..c0605bd --- /dev/null +++ b/examples/without/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "time" + + "github.com/genofire/logmania/log" + logOutput "github.com/genofire/logmania/log/hook/output" +) + +func main() { + log.Info("startup") + log.New().AddField("answer", 42).AddFields(map[string]interface{}{"answer": 3, "foo": "bar"}).Warn("Some spezial") + log.Debug("Never shown up") + logOutput.ShowTime = false + logOutput.AboveLevel = log.DebugLevel + log.Debugf("Startup %v", time.Now()) + logOutput.ShowTime = true + log.Panic("let it crash") +} diff --git a/lib/config.go b/lib/config.go new file mode 100644 index 0000000..21c6e5f --- /dev/null +++ b/lib/config.go @@ -0,0 +1,34 @@ +package lib + +import ( + "io/ioutil" + + "github.com/BurntSushi/toml" + "github.com/genofire/logmania/log" +) + +type Config struct { + API struct { + Bind string `toml:"bind"` + Interactive bool `toml:"interactive"` + } `toml:"api"` + Database struct { + Type string `toml:"type"` + Connect string `toml:"connect"` + } `toml:"database"` + Webserver struct { + Enable bool `toml:"enable"` + Bind string `toml:"bind"` + } `toml:"webserver"` +} + +func ReadConfig(path string) (*Config, error) { + log.Debugf("load of configfile: %s", path) + var config Config + file, _ := ioutil.ReadFile(path) + err := toml.Unmarshal(file, &config) + if err != nil { + return nil, err + } + return &config, nil +} diff --git a/log/hook.go b/log/hook.go new file mode 100644 index 0000000..d13505a --- /dev/null +++ b/log/hook.go @@ -0,0 +1,15 @@ +package log + +type Hook func(e *Entry) + +var hooks = make([]Hook, 0) + +func AddHook(hook Hook) { + hooks = append(hooks, hook) +} + +func save(e *Entry) { + for _, hook := range hooks { + hook(e) + } +} diff --git a/log/hook/client/main.go b/log/hook/client/main.go new file mode 100644 index 0000000..b91f75e --- /dev/null +++ b/log/hook/client/main.go @@ -0,0 +1,46 @@ +package client + +import ( + "fmt" + + "github.com/gorilla/websocket" + + "github.com/genofire/logmania/log" +) + +type Logger struct { + AboveLevel log.LogLevel + conn *websocket.Conn +} + +func (l *Logger) hook(e *log.Entry) { + if e.Level < l.AboveLevel { + return + } + err := l.conn.WriteJSON(e) + if err != nil { + log.Panic("[logmania] could not send token") + } +} +func (l *Logger) Close() { + l.conn.Close() +} + +func Init(url, token string) *Logger { + logger := &Logger{ + AboveLevel: log.InfoLevel, + } + c, _, err := websocket.DefaultDialer.Dial(fmt.Sprint(url, "/logger"), nil) + if err != nil { + log.Panic("[logmania] error on connect") + return nil + } + err = c.WriteJSON(token) + if err != nil { + log.Panic("[logmania] could not send token") + return nil + } + logger.conn = c + log.AddHook(logger.hook) + return logger +} diff --git a/log/hook/output/main.go b/log/hook/output/main.go new file mode 100644 index 0000000..c83eb23 --- /dev/null +++ b/log/hook/output/main.go @@ -0,0 +1,51 @@ +package output + +import ( + "fmt" + "os" + "time" + + "github.com/genofire/logmania/log" +) + +var ( + TimeFormat = "2006-01-02 15:04:05" + ShowTime = true + AboveLevel = log.InfoLevel +) + +func hook(e *log.Entry) { + if e.Level < AboveLevel { + return + } + v := []interface{}{} + format := "[%s] %s" + + if ShowTime { + format = "%s [%s] %s" + v = append(v, time.Now().Format(TimeFormat)) + } + + v = append(v, e.Level.String(), e.Text) + + if len(e.Fields) > 0 { + v = append(v, e.FieldString()) + format = fmt.Sprintf("%s (%%s)\n", format) + } else { + format = fmt.Sprintf("%s\n", format) + } + + text := fmt.Sprintf(format, v...) + + if e.Level == log.PanicLevel { + panic(text) + } else if e.Level > log.WarnLevel { + os.Stderr.WriteString(text) + } else { + os.Stdout.WriteString(text) + } +} + +func init() { + log.AddHook(hook) +} diff --git a/entry/level.go b/log/level.go similarity index 90% rename from entry/level.go rename to log/level.go index 1fa2451..c067226 100644 --- a/entry/level.go +++ b/log/level.go @@ -1,16 +1,16 @@ -package entry +package log -type logLevel int32 +type LogLevel int32 const ( - DebugLevel = -1 - InfoLevel = 0 - WarnLevel = 1 - ErrorLevel = 2 - PanicLevel = 3 + DebugLevel = LogLevel(-1) + InfoLevel = LogLevel(0) + WarnLevel = LogLevel(1) + ErrorLevel = LogLevel(2) + PanicLevel = LogLevel(3) ) -func (l logLevel) String() string { +func (l LogLevel) String() string { switch l { case DebugLevel: return "Debug" diff --git a/entry/main.go b/log/main.go similarity index 67% rename from entry/main.go rename to log/main.go index 49a1dd2..61756ab 100644 --- a/entry/main.go +++ b/log/main.go @@ -1,19 +1,19 @@ -package entry +package log import "fmt" type Entry struct { - Level logLevel `json:"level"` + Level LogLevel `json:"level"` Fields map[string]interface{} `json:"fields"` Text string `json:"text"` } -func (e *Entry) Log(level logLevel, v ...interface{}) { +func (e *Entry) Log(level LogLevel, v ...interface{}) { e.Text = fmt.Sprint(v...) e.Level = level save(e) } -func (e *Entry) Logf(level logLevel, format string, v ...interface{}) { +func (e *Entry) Logf(level LogLevel, format string, v ...interface{}) { e.Text = fmt.Sprintf(format, v...) e.Level = level save(e) @@ -35,5 +35,9 @@ func (e *Entry) AddFields(fields map[string]interface{}) *Entry { } func (e *Entry) FieldString() string { - return FieldOutput(e.Fields) + text := "" + for key, value := range e.Fields { + text = fmt.Sprintf("%s %s=%v", text, key, value) + } + return text[1:] } diff --git a/logmania_example.conf b/logmania_example.conf new file mode 100644 index 0000000..95cdb67 --- /dev/null +++ b/logmania_example.conf @@ -0,0 +1,6 @@ +[api] + +[database] + +[webserver] +enable = true