diff --git a/.test-coverage b/.test-coverage index cd46eda..8eb8329 100755 --- a/.test-coverage +++ b/.test-coverage @@ -27,6 +27,7 @@ done # Failures have incomplete results, so don't send if [ "$FAIL" -eq 0 ]; then goveralls -v -coverprofile=profile.cov -service=$CI -repotoken=$COVERALLS_REPO_TOKEN + bash <(curl -s https://codecov.io/bash) -t $CODECOV_TOKEN -f profile.cov fi exit $FAIL diff --git a/bot/command.go b/bot/command.go index 47ef583..367eb91 100644 --- a/bot/command.go +++ b/bot/command.go @@ -32,10 +32,10 @@ func (b *Bot) sendTo(answer func(string), from string, params []string) { to = params[1] } - if _, ok := b.state.HostTo[host]; !ok { - b.state.HostTo[host] = make(map[string]bool) + if _, ok := b.db.HostTo[host]; !ok { + b.db.HostTo[host] = make(map[string]bool) } - b.state.HostTo[host][to] = true + b.db.HostTo[host][to] = true answer(fmt.Sprintf("added %s in list of %s", to, host)) } @@ -52,9 +52,9 @@ func (b *Bot) sendRemove(answer func(string), from string, params []string) { to = params[1] } - if list, ok := b.state.HostTo[host]; ok { + if list, ok := b.db.HostTo[host]; ok { delete(list, to) - b.state.HostTo[host] = list + b.db.HostTo[host] = list answer(fmt.Sprintf("removed %s in list of %s", to, host)) } else { answer("not found host") @@ -74,7 +74,7 @@ func (b *Bot) sendList(answer func(string), from string, params []string) { of = params[0] } } - for ip, toMap := range b.state.HostTo { + for ip, toMap := range b.db.HostTo { toList := "" show := all for to := range toMap { @@ -90,7 +90,7 @@ func (b *Bot) sendList(answer func(string), from string, params []string) { if len(toList) > 3 { toList = toList[3:] } - if hostname, ok := b.state.Hostname[ip]; ok { + if hostname, ok := b.db.Hostname[ip]; ok { msg = fmt.Sprintf("%s%s (%s): %s\n", msg, ip, hostname, toList) } else { msg = fmt.Sprintf("%s%s: %s\n", msg, ip, toList) @@ -103,8 +103,8 @@ func (b *Bot) sendList(answer func(string), from string, params []string) { // list all host with his ip func (b *Bot) listHostname(answer func(string), from string, params []string) { msg := "hostnames:\n" - for ip, hostname := range b.state.Hostname { - if last, ok := b.state.Lastseen[ip]; ok { + for ip, hostname := range b.db.Hostname { + if last, ok := b.db.Lastseen[ip]; ok { got, _ := timeago.TimeAgoWithTime(time.Now(), last) msg = fmt.Sprintf("%s%s - %s (%s)\n", msg, ip, hostname, got) } else { @@ -123,7 +123,7 @@ func (b *Bot) setHostname(answer func(string), from string, params []string) { host := params[0] name := params[1] - b.state.Hostname[host] = name + b.db.Hostname[host] = name answer(fmt.Sprintf("set for %s the hostname %s", host, name)) } @@ -133,7 +133,7 @@ func (b *Bot) listMaxfilter(answer func(string), from string, params []string) { msg := "filters: " if len(params) > 0 && params[0] == "all" { msg = fmt.Sprintf("%s\n", msg) - for to, filter := range b.state.MaxPrioIn { + for to, filter := range b.db.MaxPrioIn { msg = fmt.Sprintf("%s%s - %s\n", msg, to, filter.String()) } } else { @@ -141,7 +141,7 @@ func (b *Bot) listMaxfilter(answer func(string), from string, params []string) { if len(params) > 0 { of = params[0] } - if filter, ok := b.state.MaxPrioIn[of]; ok { + if filter, ok := b.db.MaxPrioIn[of]; ok { msg = fmt.Sprintf("%s of %s is %s", msg, of, filter) } } @@ -169,7 +169,7 @@ func (b *Bot) setMaxfilter(answer func(string), from string, params []string) { return } - b.state.MaxPrioIn[to] = max + b.db.MaxPrioIn[to] = max answer(fmt.Sprintf("set filter for %s to %s", to, max.String())) } @@ -178,7 +178,7 @@ func (b *Bot) setMaxfilter(answer func(string), from string, params []string) { func (b *Bot) listRegex(answer func(string), from string, params []string) { msg := "regexs:\n" if len(params) > 0 && params[0] == "all" { - for to, regexs := range b.state.RegexIn { + for to, regexs := range b.db.RegexIn { msg = fmt.Sprintf("%s%s\n-------------\n", msg, to) for expression := range regexs { msg = fmt.Sprintf("%s - %s\n", msg, expression) @@ -189,7 +189,7 @@ func (b *Bot) listRegex(answer func(string), from string, params []string) { if len(params) > 0 { of = params[0] } - if regexs, ok := b.state.RegexIn[of]; ok { + if regexs, ok := b.db.RegexIn[of]; ok { msg = fmt.Sprintf("%s%s\n-------------\n", msg, from) for expression := range regexs { msg = fmt.Sprintf("%s - %s\n", msg, expression) @@ -207,7 +207,7 @@ func (b *Bot) addRegex(answer func(string), from string, params []string) { } regex := strings.Join(params, " ") - if err := b.state.AddRegex(from, regex); err == nil { + if err := b.db.AddRegex(from, regex); err == nil { answer(fmt.Sprintf("add regex for \"%s\" to %s", from, regex)) } else { answer(fmt.Sprintf("\"%s\" is no valid regex expression: %s", regex, err)) @@ -221,6 +221,6 @@ func (b *Bot) delRegex(answer func(string), from string, params []string) { return } regex := strings.Join(params, " ") - delete(b.state.RegexIn[from], regex) + delete(b.db.RegexIn[from], regex) b.listRegex(answer, from, []string{}) } diff --git a/bot/main.go b/bot/main.go index 0db4800..0d5b77b 100644 --- a/bot/main.go +++ b/bot/main.go @@ -4,17 +4,17 @@ import ( "fmt" "strings" - configNotify "github.com/genofire/logmania/notify/config" + "github.com/genofire/logmania/database" ) type Bot struct { - state *configNotify.NotifyState + db *database.DB commands map[string]commandFunc } -func NewBot(state *configNotify.NotifyState) *Bot { +func NewBot(db *database.DB) *Bot { b := &Bot{ - state: state, + db: db, } b.commands = map[string]commandFunc{ "help": b.help, diff --git a/circle.yml b/circle.yml index 2b790b7..64a38fd 100644 --- a/circle.yml +++ b/circle.yml @@ -1,36 +1,55 @@ -machine: - environment: - GOROOT: "" - PATH: "/usr/local/go/bin:/usr/local/go_workspace/bin:~/.go_workspace/bin:${PATH}" - GOPATH: "${HOME}/.go_workspace" - -dependencies: - override: - - mkdir -p ~/.go_workspace/src/github.com/${CIRCLE_PROJECT_USERNAME} - - ln -s ${HOME}/${CIRCLE_PROJECT_REPONAME} ${HOME}/.go_workspace/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} - - go get -t -d -v ./... - - go install github.com/genofire/logmania - post: - - cp ~/.go_workspace/bin/logmania logmania.bin - - tar -cvzf logmania-builded.tar.gz logmania.bin logmania_example.conf - - mv logmania-builded.tar.gz $CIRCLE_ARTIFACTS - - - -test: - pre: - - go get github.com/mattn/goveralls - - go get golang.org/x/tools/cmd/cover - override: - - ./.test-coverage circle-ci - -deployment: - staging: - branch: master - commands: - - ./deploy.sh $HOST_FOR_STAGING $PORT_FOR_STAGING - -notify: - webhooks: - - url: https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN - - url: https://hook2xmpp.pub.warehost.de/circleci +version: 2 +jobs: + build: + docker: + - image: circleci/golang:latest + working_directory: /go/src/github.com/genofire/logmania + steps: + - checkout + - run: go get -t -d -v ./... + - run: go install github.com/genofire/logmania + - store_artifacts: + path: /go/bin/ + destination: logmania + test: + docker: + - image: circleci/golang:latest + working_directory: /go/src/github.com/genofire/logmania + steps: + - checkout + - run: go get -t -d -v ./... + - run: go get github.com/mattn/goveralls + - run: go get golang.org/x/tools/cmd/cover + - run: ./.test-coverage circle-ci + - store_test_results: + path: ./ + destination: profile.cov + test_race: + docker: + - image: circleci/golang:latest + working_directory: /go/src/github.com/genofire/logmania + steps: + - checkout + - run: go get -t -d -v ./... + - run: go test -race ./... + deploy: + docker: + - image: circleci/golang:latest + working_directory: /go/src/github.com/genofire/logmania + steps: + - checkout + - run: go get -t -d -v ./... + - run: go install github.com/genofire/logmania + - run: ./deploy.sh $HOST_FOR_STAGING $PORT_FOR_STAGING +workflows: + version: 2 + build_and_tests: + jobs: + - build + - test + - test_race + - deploy: + requires: + - build + - test + - test_race diff --git a/cmd/server.go b/cmd/server.go index b196bf0..84501c3 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -1,6 +1,7 @@ package cmd import ( + "net/http" "os" "os/signal" "syscall" @@ -12,23 +13,23 @@ import ( "github.com/genofire/golang-lib/file" "github.com/genofire/golang-lib/worker" "github.com/genofire/logmania/bot" + "github.com/genofire/logmania/database" "github.com/genofire/logmania/lib" "github.com/genofire/logmania/notify" allNotify "github.com/genofire/logmania/notify/all" - configNotify "github.com/genofire/logmania/notify/config" "github.com/genofire/logmania/receive" allReceiver "github.com/genofire/logmania/receive/all" ) var ( - configPath string - config *lib.Config - notifyState *configNotify.NotifyState - stateSaveWorker *worker.Worker - notifier notify.Notifier - receiver receive.Receiver - logChannel chan *log.Entry - logmaniaBot *bot.Bot + configPath string + config *lib.Config + db *database.DB + dbSaveWorker *worker.Worker + notifier notify.Notifier + receiver receive.Receiver + logChannel chan *log.Entry + logmaniaBot *bot.Bot ) // serverCmd represents the serve command @@ -46,23 +47,22 @@ var serverCmd = &cobra.Command{ log.Panicf("Could not load '%s' for configuration.", configPath) } - notifyState = configNotify.ReadStateFile(config.Notify.StateFile) - stateSaveWorker = file.NewSaveJSONWorker(time.Minute, config.Notify.StateFile, notifyState) + db = database.ReadDBFile(config.DB) + dbSaveWorker = file.NewSaveJSONWorker(time.Minute, config.DB, db) - logmaniaBot = bot.NewBot(notifyState) + logmaniaBot = bot.NewBot(db) - notifier = allNotify.Init(&config.Notify, notifyState, logmaniaBot) - log.AddHook(notifier) + notifier = allNotify.Init(&config.Notify, db, logmaniaBot) logChannel = make(chan *log.Entry) go func() { for a := range logChannel { - notifier.Fire(a) + notifier.Send(a) } }() if config.Notify.AlertCheck.Duration > time.Duration(time.Second) { - go notifyState.Alert(config.Notify.AlertCheck.Duration, notifier.Fire) + go db.Alert(config.Notify.AlertCheck.Duration, notifier.Send) } log.Info("starting logmania") @@ -71,6 +71,16 @@ var serverCmd = &cobra.Command{ go receiver.Listen() + srv := &http.Server{ + Addr: config.HTTPAddress, + } + + go func() { + if err := srv.ListenAndServe(); err != nil { + panic(err) + } + }() + // Wait for system signal sigchan := make(chan os.Signal, 1) signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGUSR1) @@ -91,8 +101,8 @@ var serverCmd = &cobra.Command{ } func quit() { - stateSaveWorker.Close() - file.SaveJSON(config.Notify.StateFile, notifyState) + dbSaveWorker.Close() + file.SaveJSON(config.DB, db) receiver.Close() notifier.Close() log.Info("quit of logmania") @@ -112,7 +122,7 @@ func reload() { go receiver.Listen() notifier.Close() - notifier = allNotify.Init(&config.Notify, notifyState, logmaniaBot) + notifier = allNotify.Init(&config.Notify, db, logmaniaBot) } func init() { diff --git a/notify/config/config.go b/database/main.go similarity index 57% rename from notify/config/config.go rename to database/main.go index 8b3aa69..3402717 100644 --- a/notify/config/config.go +++ b/database/main.go @@ -1,4 +1,4 @@ -package config +package database import ( "regexp" @@ -10,7 +10,7 @@ import ( const AlertMsg = "alert service from logmania, device did not send new message for a while" -type NotifyState struct { +type DB struct { Hostname map[string]string `json:"hostname"` HostTo map[string]map[string]bool `json:"host_to"` MaxPrioIn map[string]log.Level `json:"maxLevel"` @@ -19,21 +19,21 @@ type NotifyState struct { LastseenNotify map[string]time.Time `json:"-"` } -func (state *NotifyState) SendTo(e *log.Entry) (*log.Entry, []string) { +func (db *DB) SendTo(e *log.Entry) (*log.Entry, []string) { hostname, ok := e.Data["hostname"].(string) if !ok { return e, nil } - if to, ok := state.HostTo[hostname]; ok { + if to, ok := db.HostTo[hostname]; ok { if e.Message != AlertMsg && hostname != "" { - state.Lastseen[hostname] = time.Now() + db.Lastseen[hostname] = time.Now() } var toList []string for toEntry, _ := range to { - if lvl := state.MaxPrioIn[toEntry]; e.Level >= lvl { + if lvl := db.MaxPrioIn[toEntry]; e.Level >= lvl { continue } - if regex, ok := state.RegexIn[toEntry]; ok { + if regex, ok := db.RegexIn[toEntry]; ok { stopForTo := false for _, expr := range regex { if expr.MatchString(e.Message) { @@ -47,7 +47,7 @@ func (state *NotifyState) SendTo(e *log.Entry) (*log.Entry, []string) { } toList = append(toList, toEntry) } - if replaceHostname, ok := state.Hostname[hostname]; ok { + if replaceHostname, ok := db.Hostname[hostname]; ok { entry := e.WithField("hostname", replaceHostname) entry.Level = e.Level entry.Message = e.Message @@ -55,48 +55,48 @@ func (state *NotifyState) SendTo(e *log.Entry) (*log.Entry, []string) { } return e, toList } else { - state.HostTo[hostname] = make(map[string]bool) + db.HostTo[hostname] = make(map[string]bool) } return e, nil } -func (state *NotifyState) AddRegex(to, expression string) error { +func (db *DB) AddRegex(to, expression string) error { regex, err := regexp.Compile(expression) if err == nil { - if _, ok := state.RegexIn[to]; !ok { - state.RegexIn[to] = make(map[string]*regexp.Regexp) + if _, ok := db.RegexIn[to]; !ok { + db.RegexIn[to] = make(map[string]*regexp.Regexp) } - state.RegexIn[to][expression] = regex + db.RegexIn[to][expression] = regex return nil } return err } -func ReadStateFile(path string) *NotifyState { - var state NotifyState +func ReadDBFile(path string) *DB { + var db DB - if err := file.ReadJSON(path, &state); err == nil { - log.Infof("loaded %d hosts", len(state.HostTo)) - if state.Lastseen == nil { - state.Lastseen = make(map[string]time.Time) + if err := file.ReadJSON(path, &db); err == nil { + log.Infof("loaded %d hosts", len(db.HostTo)) + if db.Lastseen == nil { + db.Lastseen = make(map[string]time.Time) } - if state.LastseenNotify == nil { - state.LastseenNotify = make(map[string]time.Time) + if db.LastseenNotify == nil { + db.LastseenNotify = make(map[string]time.Time) } - if state.RegexIn == nil { - state.RegexIn = make(map[string]map[string]*regexp.Regexp) + if db.RegexIn == nil { + db.RegexIn = make(map[string]map[string]*regexp.Regexp) } else { - for to, regexs := range state.RegexIn { + for to, regexs := range db.RegexIn { for exp, _ := range regexs { - state.AddRegex(to, exp) + db.AddRegex(to, exp) } } } - return &state + return &db } else { - log.Error("failed to open state notify file: ", path, ":", err) + log.Error("failed to open db file: ", path, ":", err) } - return &NotifyState{ + return &DB{ Hostname: make(map[string]string), HostTo: make(map[string]map[string]bool), MaxPrioIn: make(map[string]log.Level), @@ -106,15 +106,15 @@ func ReadStateFile(path string) *NotifyState { } } -func (state *NotifyState) Alert(expired time.Duration, send func(e *log.Entry) error) { +func (db *DB) Alert(expired time.Duration, send func(e *log.Entry) error) { c := time.Tick(time.Minute) for range c { now := time.Now() - for host, time := range state.Lastseen { + for host, time := range db.Lastseen { if time.Before(now.Add(expired * -2)) { - if timeNotify, ok := state.LastseenNotify[host]; !ok || !time.Before(timeNotify) { - state.LastseenNotify[host] = now + if timeNotify, ok := db.LastseenNotify[host]; !ok || !time.Before(timeNotify) { + db.LastseenNotify[host] = now entry := log.NewEntry(log.New()) entry.Level = log.ErrorLevel entry.Message = AlertMsg diff --git a/deploy.sh b/deploy.sh index a79bef8..d339b1c 100755 --- a/deploy.sh +++ b/deploy.sh @@ -3,10 +3,10 @@ host=$1 port=$2 remote="circleci@${host}" echo "deploying..." -ssh -p $port $remote sudo systemctl stop logmania; +ssh -o StrictHostKeyChecking=no -p $port $remote sudo systemctl stop logmania; RETVAL=$? [ $RETVAL -ne 0 ] && exit 1 -scp -q -P $port ~/.go_workspace/bin/logmania $remote:~/bin/logmania; +scp -q -P $port /go/bin/logmania $remote:~/bin/logmania; RETVAL=$? ssh -p $port $remote sudo systemctl start logmania; [ $RETVAL -eq 0 ] && RETVAL=$? diff --git a/lib/config.go b/lib/config.go index c775d01..86216c9 100644 --- a/lib/config.go +++ b/lib/config.go @@ -3,12 +3,13 @@ package lib // Struct of the configuration // e.g. under github.com/genofire/logmania/logmania_example.conf type Config struct { - Notify NotifyConfig `toml:"notify"` - Receive ReceiveConfig `toml:"receive"` + Notify NotifyConfig `toml:"notify"` + Receive ReceiveConfig `toml:"receive"` + DB string `toml:"database"` + HTTPAddress string `toml:"http_address"` } type NotifyConfig struct { - StateFile string `toml:"state_file"` AlertCheck Duration `toml:"alert_check"` Console bool `toml:"debug"` XMPP struct { @@ -22,8 +23,6 @@ type NotifyConfig struct { StatusMessage string `toml:"status_message"` StartupNotify string `toml:"startup_notify"` } `toml:"xmpp"` - IRC struct { - } `toml:"irc"` } type ReceiveConfig struct { diff --git a/notify/all/internal.go b/notify/all/internal.go index de572cc..2687d68 100644 --- a/notify/all/internal.go +++ b/notify/all/internal.go @@ -4,9 +4,9 @@ import ( log "github.com/sirupsen/logrus" "github.com/genofire/logmania/bot" + "github.com/genofire/logmania/database" "github.com/genofire/logmania/lib" "github.com/genofire/logmania/notify" - configNotify "github.com/genofire/logmania/notify/config" ) type Notifier struct { @@ -15,10 +15,10 @@ type Notifier struct { channelNotify chan *log.Entry } -func Init(config *lib.NotifyConfig, state *configNotify.NotifyState, bot *bot.Bot) notify.Notifier { +func Init(config *lib.NotifyConfig, db *database.DB, bot *bot.Bot) notify.Notifier { var list []notify.Notifier for _, init := range notify.NotifyRegister { - notify := init(config, state, bot) + notify := init(config, db, bot) if notify == nil { continue @@ -37,12 +37,12 @@ func Init(config *lib.NotifyConfig, state *configNotify.NotifyState, bot *bot.Bo func (n *Notifier) sender() { for c := range n.channelNotify { for _, item := range n.list { - item.Fire(c) + item.Send(c) } } } -func (n *Notifier) Fire(e *log.Entry) error { +func (n *Notifier) Send(e *log.Entry) error { n.channelNotify <- e return nil } @@ -52,14 +52,3 @@ func (n *Notifier) Close() { item.Close() } } - -func (n *Notifier) Levels() []log.Level { - return []log.Level{ - log.DebugLevel, - log.InfoLevel, - log.WarnLevel, - log.ErrorLevel, - log.FatalLevel, - log.PanicLevel, - } -} diff --git a/notify/main.go b/notify/main.go index 518e2b9..c7082eb 100644 --- a/notify/main.go +++ b/notify/main.go @@ -4,19 +4,18 @@ import ( log "github.com/sirupsen/logrus" "github.com/genofire/logmania/bot" + "github.com/genofire/logmania/database" "github.com/genofire/logmania/lib" - configNotify "github.com/genofire/logmania/notify/config" ) var NotifyRegister []NotifyInit type Notifier interface { - Fire(entry *log.Entry) error - Levels() []log.Level + Send(entry *log.Entry) error Close() } -type NotifyInit func(*lib.NotifyConfig, *configNotify.NotifyState, *bot.Bot) Notifier +type NotifyInit func(*lib.NotifyConfig, *database.DB, *bot.Bot) Notifier func AddNotifier(n NotifyInit) { NotifyRegister = append(NotifyRegister, n) diff --git a/notify/xmpp/main.go b/notify/xmpp/main.go index 4c20501..3e39c26 100644 --- a/notify/xmpp/main.go +++ b/notify/xmpp/main.go @@ -3,27 +3,35 @@ package xmpp import ( "errors" "fmt" + "io" "strings" xmpp "github.com/mattn/go-xmpp" log "github.com/sirupsen/logrus" "github.com/genofire/logmania/bot" + "github.com/genofire/logmania/database" "github.com/genofire/logmania/lib" "github.com/genofire/logmania/notify" - configNotify "github.com/genofire/logmania/notify/config" ) -var logger = log.WithField("notify", "xmpp") +const ( + proto = "xmpp:" + protoGroup = "xmpp-muc:" + nickname = "logmania" +) + +var logger = log.WithField("notify", proto) type Notifier struct { notify.Notifier client *xmpp.Client - state *configNotify.NotifyState + channels map[string]bool + db *database.DB formatter *log.TextFormatter } -func Init(config *lib.NotifyConfig, state *configNotify.NotifyState, bot *bot.Bot) notify.Notifier { +func Init(config *lib.NotifyConfig, db *database.DB, bot *bot.Bot) notify.Notifier { options := xmpp.Options{ Host: config.XMPP.Host, User: config.XMPP.Username, @@ -43,6 +51,14 @@ func Init(config *lib.NotifyConfig, state *configNotify.NotifyState, bot *bot.Bo for { chat, err := client.Recv() if err != nil { + if err == io.EOF { + client, err = options.NewClient() + log.Warn("reconnect") + if err != nil { + log.Panic(err) + } + continue + } logger.Warn(err) } switch v := chat.(type) { @@ -53,18 +69,24 @@ func Init(config *lib.NotifyConfig, state *configNotify.NotifyState, bot *bot.Bo } } }() + for _, toAddresses := range db.HostTo { + for to, _ := range toAddresses { + toAddr := strings.TrimPrefix(to, protoGroup) + client.JoinMUCNoHistory(toAddr, nickname) + } + } logger.Info("startup") return &Notifier{ client: client, - state: state, + db: db, formatter: &log.TextFormatter{ DisableTimestamp: true, }, } } -func (n *Notifier) Fire(e *log.Entry) error { - e, to := n.state.SendTo(e) +func (n *Notifier) Send(e *log.Entry) error { + e, to := n.db.SendTo(e) if to == nil { return errors.New("no reciever found") } @@ -73,15 +95,18 @@ func (n *Notifier) Fire(e *log.Entry) error { return err } for _, toAddr := range to { - to := strings.TrimPrefix(toAddr, "xmpp:") - if strings.Contains(toAddr, "conference") || strings.Contains(toAddr, "irc") { - n.client.JoinMUCNoHistory(to, "logmania") - _, err = n.client.SendHtml(xmpp.Chat{Remote: to, Type: "groupchat", Text: string(text)}) + if strings.HasPrefix(toAddr, protoGroup) { + toAddr = strings.TrimPrefix(toAddr, protoGroup) + if _, ok := n.channels[toAddr]; ok { + n.client.JoinMUCNoHistory(toAddr, nickname) + } + _, err = n.client.SendHtml(xmpp.Chat{Remote: toAddr, Type: "groupchat", Text: string(text)}) if err != nil { logger.Error("xmpp to ", to, " error:", err) } } else { - _, err := n.client.SendHtml(xmpp.Chat{Remote: to, Type: "chat", Text: string(text)}) + toAddr = strings.TrimPrefix(toAddr, proto) + _, err := n.client.SendHtml(xmpp.Chat{Remote: toAddr, Type: "chat", Text: string(text)}) if err != nil { logger.Error("xmpp to ", to, " error:", err) } @@ -90,19 +115,13 @@ func (n *Notifier) Fire(e *log.Entry) error { return nil } -func (n *Notifier) Levels() []log.Level { - return []log.Level{ - log.DebugLevel, - log.InfoLevel, - log.WarnLevel, - log.ErrorLevel, - log.FatalLevel, - log.PanicLevel, +func (n *Notifier) Close() { + for jid := range n.channels { + n.client.LeaveMUC(jid) } + n.client.Close() } -func (n *Notifier) Close() {} - func init() { notify.AddNotifier(Init) } diff --git a/receive/all/main.go b/receive/all/main.go index 5125274..313bce6 100644 --- a/receive/all/main.go +++ b/receive/all/main.go @@ -2,5 +2,6 @@ package all import ( _ "github.com/genofire/logmania/receive/journald_json" + _ "github.com/genofire/logmania/receive/logrus" _ "github.com/genofire/logmania/receive/syslog" ) diff --git a/receive/logrus/client/main.go b/receive/logrus/client/main.go new file mode 100644 index 0000000..74457b9 --- /dev/null +++ b/receive/logrus/client/main.go @@ -0,0 +1,85 @@ +package client + +import ( + "io" + + websocketLib "github.com/genofire/golang-lib/websocket" + "github.com/genofire/logmania/receive/logrus" + "github.com/google/uuid" + "github.com/gorilla/websocket" + log "github.com/sirupsen/logrus" +) + +// client logger +type Logmania struct { + URL string + Token uuid.UUID + Levels []log.Level + quere chan *log.Entry + conn *websocket.Conn +} + +func NewClient(url string, token uuid.UUID, lvls ...log.Level) *Logmania { + logger := &Logmania{ + URL: url, + Token: token, + Levels: lvls, + quere: make(chan *log.Entry), + } + conn, _, err := websocket.DefaultDialer.Dial(url, nil) + if err != nil { + log.Error("[logmania] error on connect: ", err) + return nil + } + logger.conn = conn + go logger.Start() + return logger +} + +// Listen if logmania server want to close the connection +func (l *Logmania) listen() { + for { + var msg websocketLib.Message + err := websocket.ReadJSON(l.conn, &msg) + if err == io.EOF { + l.Close() + log.Warn("[logmania] close listener:", err) + } else if err != nil { + log.Println(err) + } else { + if msg.Subject == websocketLib.SessionMessageInit { + l.conn.WriteJSON(&websocketLib.Message{ + Subject: websocketLib.SessionMessageInit, + ID: l.Token, + }) + } + } + } +} + +func (l *Logmania) writer() { + for e := range l.quere { + err := l.conn.WriteJSON(&websocketLib.Message{ + Subject: logrus.WS_LOG_ENTRY, + Body: e, + }) + if err != nil { + log.Error("[logmania] could not send log entry:", err) + } + } +} + +func (l *Logmania) Start() { + go l.listen() + l.writer() +} + +func (l *Logmania) Fire(e *log.Entry) { + l.quere <- e +} + +// close connection to logger +func (l *Logmania) Close() { + l.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + close(l.quere) +} diff --git a/receive/logrus/client/main_deadtest.go b/receive/logrus/client/main_deadtest.go new file mode 100644 index 0000000..da13c8e --- /dev/null +++ b/receive/logrus/client/main_deadtest.go @@ -0,0 +1 @@ +package client diff --git a/receive/logrus/main.go b/receive/logrus/main.go new file mode 100644 index 0000000..fd8e5d8 --- /dev/null +++ b/receive/logrus/main.go @@ -0,0 +1,55 @@ +package logrus + +import ( + "net/http" + + "github.com/genofire/golang-lib/websocket" + log "github.com/sirupsen/logrus" + + "github.com/genofire/logmania/lib" + "github.com/genofire/logmania/receive" +) + +const WS_LOG_ENTRY = "log" + +var logger = log.WithField("receive", "logrus") + +type Receiver struct { + receive.Receiver + input chan *websocket.Message + exportChannel chan *log.Entry + serverSocket *websocket.Server +} + +func Init(config *lib.ReceiveConfig, exportChannel chan *log.Entry) receive.Receiver { + inputMsg := make(chan *websocket.Message) + ws := websocket.NewServer(inputMsg, websocket.NewSessionManager()) + + http.HandleFunc("/receiver", ws.Handler) + + recv := &Receiver{ + input: inputMsg, + serverSocket: ws, + exportChannel: exportChannel, + } + + logger.Info("init") + + return recv +} + +func (rc *Receiver) Listen() { + logger.Info("listen") + for msg := range rc.input { + if event, ok := msg.Body.(log.Entry); ok { + rc.exportChannel <- &event + } + } +} + +func (rc *Receiver) Close() { +} + +func init() { + receive.AddReceiver("websocket", Init) +}