[TASK] extract database, xmpp reconnect & websocket/logrus server (client WIP)

This commit is contained in:
Martin Geno 2017-11-10 18:57:36 +01:00
parent c46cea3c7a
commit a96efdf3cc
No known key found for this signature in database
GPG Key ID: F0D39A37E925E941
15 changed files with 335 additions and 157 deletions

View File

@ -27,6 +27,7 @@ done
# Failures have incomplete results, so don't send # Failures have incomplete results, so don't send
if [ "$FAIL" -eq 0 ]; then if [ "$FAIL" -eq 0 ]; then
goveralls -v -coverprofile=profile.cov -service=$CI -repotoken=$COVERALLS_REPO_TOKEN 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 fi
exit $FAIL exit $FAIL

View File

@ -32,10 +32,10 @@ func (b *Bot) sendTo(answer func(string), from string, params []string) {
to = params[1] to = params[1]
} }
if _, ok := b.state.HostTo[host]; !ok { if _, ok := b.db.HostTo[host]; !ok {
b.state.HostTo[host] = make(map[string]bool) 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)) 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] to = params[1]
} }
if list, ok := b.state.HostTo[host]; ok { if list, ok := b.db.HostTo[host]; ok {
delete(list, to) delete(list, to)
b.state.HostTo[host] = list b.db.HostTo[host] = list
answer(fmt.Sprintf("removed %s in list of %s", to, host)) answer(fmt.Sprintf("removed %s in list of %s", to, host))
} else { } else {
answer("not found host") answer("not found host")
@ -74,7 +74,7 @@ func (b *Bot) sendList(answer func(string), from string, params []string) {
of = params[0] of = params[0]
} }
} }
for ip, toMap := range b.state.HostTo { for ip, toMap := range b.db.HostTo {
toList := "" toList := ""
show := all show := all
for to := range toMap { for to := range toMap {
@ -90,7 +90,7 @@ func (b *Bot) sendList(answer func(string), from string, params []string) {
if len(toList) > 3 { if len(toList) > 3 {
toList = 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) msg = fmt.Sprintf("%s%s (%s): %s\n", msg, ip, hostname, toList)
} else { } else {
msg = fmt.Sprintf("%s%s: %s\n", msg, ip, toList) 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 // list all host with his ip
func (b *Bot) listHostname(answer func(string), from string, params []string) { func (b *Bot) listHostname(answer func(string), from string, params []string) {
msg := "hostnames:\n" msg := "hostnames:\n"
for ip, hostname := range b.state.Hostname { for ip, hostname := range b.db.Hostname {
if last, ok := b.state.Lastseen[ip]; ok { if last, ok := b.db.Lastseen[ip]; ok {
got, _ := timeago.TimeAgoWithTime(time.Now(), last) got, _ := timeago.TimeAgoWithTime(time.Now(), last)
msg = fmt.Sprintf("%s%s - %s (%s)\n", msg, ip, hostname, got) msg = fmt.Sprintf("%s%s - %s (%s)\n", msg, ip, hostname, got)
} else { } else {
@ -123,7 +123,7 @@ func (b *Bot) setHostname(answer func(string), from string, params []string) {
host := params[0] host := params[0]
name := params[1] name := params[1]
b.state.Hostname[host] = name b.db.Hostname[host] = name
answer(fmt.Sprintf("set for %s the hostname %s", 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: " msg := "filters: "
if len(params) > 0 && params[0] == "all" { if len(params) > 0 && params[0] == "all" {
msg = fmt.Sprintf("%s\n", msg) 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()) msg = fmt.Sprintf("%s%s - %s\n", msg, to, filter.String())
} }
} else { } else {
@ -141,7 +141,7 @@ func (b *Bot) listMaxfilter(answer func(string), from string, params []string) {
if len(params) > 0 { if len(params) > 0 {
of = 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) 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 return
} }
b.state.MaxPrioIn[to] = max b.db.MaxPrioIn[to] = max
answer(fmt.Sprintf("set filter for %s to %s", to, max.String())) 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) { func (b *Bot) listRegex(answer func(string), from string, params []string) {
msg := "regexs:\n" msg := "regexs:\n"
if len(params) > 0 && params[0] == "all" { 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) msg = fmt.Sprintf("%s%s\n-------------\n", msg, to)
for expression := range regexs { for expression := range regexs {
msg = fmt.Sprintf("%s - %s\n", msg, expression) 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 { if len(params) > 0 {
of = 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) msg = fmt.Sprintf("%s%s\n-------------\n", msg, from)
for expression := range regexs { for expression := range regexs {
msg = fmt.Sprintf("%s - %s\n", msg, expression) 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, " ") 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)) answer(fmt.Sprintf("add regex for \"%s\" to %s", from, regex))
} else { } else {
answer(fmt.Sprintf("\"%s\" is no valid regex expression: %s", regex, err)) 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 return
} }
regex := strings.Join(params, " ") regex := strings.Join(params, " ")
delete(b.state.RegexIn[from], regex) delete(b.db.RegexIn[from], regex)
b.listRegex(answer, from, []string{}) b.listRegex(answer, from, []string{})
} }

View File

@ -4,17 +4,17 @@ import (
"fmt" "fmt"
"strings" "strings"
configNotify "github.com/genofire/logmania/notify/config" "github.com/genofire/logmania/database"
) )
type Bot struct { type Bot struct {
state *configNotify.NotifyState db *database.DB
commands map[string]commandFunc commands map[string]commandFunc
} }
func NewBot(state *configNotify.NotifyState) *Bot { func NewBot(db *database.DB) *Bot {
b := &Bot{ b := &Bot{
state: state, db: db,
} }
b.commands = map[string]commandFunc{ b.commands = map[string]commandFunc{
"help": b.help, "help": b.help,

View File

@ -1,36 +1,55 @@
machine: version: 2
environment: jobs:
GOROOT: "" build:
PATH: "/usr/local/go/bin:/usr/local/go_workspace/bin:~/.go_workspace/bin:${PATH}" docker:
GOPATH: "${HOME}/.go_workspace" - image: circleci/golang:latest
working_directory: /go/src/github.com/genofire/logmania
dependencies: steps:
override: - checkout
- mkdir -p ~/.go_workspace/src/github.com/${CIRCLE_PROJECT_USERNAME} - run: go get -t -d -v ./...
- ln -s ${HOME}/${CIRCLE_PROJECT_REPONAME} ${HOME}/.go_workspace/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} - run: go install github.com/genofire/logmania
- go get -t -d -v ./... - store_artifacts:
- go install github.com/genofire/logmania path: /go/bin/
post: destination: logmania
- cp ~/.go_workspace/bin/logmania logmania.bin test:
- tar -cvzf logmania-builded.tar.gz logmania.bin logmania_example.conf docker:
- mv logmania-builded.tar.gz $CIRCLE_ARTIFACTS - image: circleci/golang:latest
working_directory: /go/src/github.com/genofire/logmania
steps:
- checkout
test: - run: go get -t -d -v ./...
pre: - run: go get github.com/mattn/goveralls
- go get github.com/mattn/goveralls - run: go get golang.org/x/tools/cmd/cover
- go get golang.org/x/tools/cmd/cover - run: ./.test-coverage circle-ci
override: - store_test_results:
- ./.test-coverage circle-ci path: ./
destination: profile.cov
deployment: test_race:
staging: docker:
branch: master - image: circleci/golang:latest
commands: working_directory: /go/src/github.com/genofire/logmania
- ./deploy.sh $HOST_FOR_STAGING $PORT_FOR_STAGING steps:
- checkout
notify: - run: go get -t -d -v ./...
webhooks: - run: go test -race ./...
- url: https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN deploy:
- url: https://hook2xmpp.pub.warehost.de/circleci 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

View File

@ -1,6 +1,7 @@
package cmd package cmd
import ( import (
"net/http"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
@ -12,23 +13,23 @@ import (
"github.com/genofire/golang-lib/file" "github.com/genofire/golang-lib/file"
"github.com/genofire/golang-lib/worker" "github.com/genofire/golang-lib/worker"
"github.com/genofire/logmania/bot" "github.com/genofire/logmania/bot"
"github.com/genofire/logmania/database"
"github.com/genofire/logmania/lib" "github.com/genofire/logmania/lib"
"github.com/genofire/logmania/notify" "github.com/genofire/logmania/notify"
allNotify "github.com/genofire/logmania/notify/all" allNotify "github.com/genofire/logmania/notify/all"
configNotify "github.com/genofire/logmania/notify/config"
"github.com/genofire/logmania/receive" "github.com/genofire/logmania/receive"
allReceiver "github.com/genofire/logmania/receive/all" allReceiver "github.com/genofire/logmania/receive/all"
) )
var ( var (
configPath string configPath string
config *lib.Config config *lib.Config
notifyState *configNotify.NotifyState db *database.DB
stateSaveWorker *worker.Worker dbSaveWorker *worker.Worker
notifier notify.Notifier notifier notify.Notifier
receiver receive.Receiver receiver receive.Receiver
logChannel chan *log.Entry logChannel chan *log.Entry
logmaniaBot *bot.Bot logmaniaBot *bot.Bot
) )
// serverCmd represents the serve command // serverCmd represents the serve command
@ -46,23 +47,22 @@ var serverCmd = &cobra.Command{
log.Panicf("Could not load '%s' for configuration.", configPath) log.Panicf("Could not load '%s' for configuration.", configPath)
} }
notifyState = configNotify.ReadStateFile(config.Notify.StateFile) db = database.ReadDBFile(config.DB)
stateSaveWorker = file.NewSaveJSONWorker(time.Minute, config.Notify.StateFile, notifyState) dbSaveWorker = file.NewSaveJSONWorker(time.Minute, config.DB, db)
logmaniaBot = bot.NewBot(notifyState) logmaniaBot = bot.NewBot(db)
notifier = allNotify.Init(&config.Notify, notifyState, logmaniaBot) notifier = allNotify.Init(&config.Notify, db, logmaniaBot)
log.AddHook(notifier)
logChannel = make(chan *log.Entry) logChannel = make(chan *log.Entry)
go func() { go func() {
for a := range logChannel { for a := range logChannel {
notifier.Fire(a) notifier.Send(a)
} }
}() }()
if config.Notify.AlertCheck.Duration > time.Duration(time.Second) { 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") log.Info("starting logmania")
@ -71,6 +71,16 @@ var serverCmd = &cobra.Command{
go receiver.Listen() go receiver.Listen()
srv := &http.Server{
Addr: config.HTTPAddress,
}
go func() {
if err := srv.ListenAndServe(); err != nil {
panic(err)
}
}()
// Wait for system signal // Wait for system signal
sigchan := make(chan os.Signal, 1) sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGUSR1) signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGUSR1)
@ -91,8 +101,8 @@ var serverCmd = &cobra.Command{
} }
func quit() { func quit() {
stateSaveWorker.Close() dbSaveWorker.Close()
file.SaveJSON(config.Notify.StateFile, notifyState) file.SaveJSON(config.DB, db)
receiver.Close() receiver.Close()
notifier.Close() notifier.Close()
log.Info("quit of logmania") log.Info("quit of logmania")
@ -112,7 +122,7 @@ func reload() {
go receiver.Listen() go receiver.Listen()
notifier.Close() notifier.Close()
notifier = allNotify.Init(&config.Notify, notifyState, logmaniaBot) notifier = allNotify.Init(&config.Notify, db, logmaniaBot)
} }
func init() { func init() {

View File

@ -1,4 +1,4 @@
package config package database
import ( import (
"regexp" "regexp"
@ -10,7 +10,7 @@ import (
const AlertMsg = "alert service from logmania, device did not send new message for a while" 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"` Hostname map[string]string `json:"hostname"`
HostTo map[string]map[string]bool `json:"host_to"` HostTo map[string]map[string]bool `json:"host_to"`
MaxPrioIn map[string]log.Level `json:"maxLevel"` MaxPrioIn map[string]log.Level `json:"maxLevel"`
@ -19,21 +19,21 @@ type NotifyState struct {
LastseenNotify map[string]time.Time `json:"-"` 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) hostname, ok := e.Data["hostname"].(string)
if !ok { if !ok {
return e, nil return e, nil
} }
if to, ok := state.HostTo[hostname]; ok { if to, ok := db.HostTo[hostname]; ok {
if e.Message != AlertMsg && hostname != "" { if e.Message != AlertMsg && hostname != "" {
state.Lastseen[hostname] = time.Now() db.Lastseen[hostname] = time.Now()
} }
var toList []string var toList []string
for toEntry, _ := range to { for toEntry, _ := range to {
if lvl := state.MaxPrioIn[toEntry]; e.Level >= lvl { if lvl := db.MaxPrioIn[toEntry]; e.Level >= lvl {
continue continue
} }
if regex, ok := state.RegexIn[toEntry]; ok { if regex, ok := db.RegexIn[toEntry]; ok {
stopForTo := false stopForTo := false
for _, expr := range regex { for _, expr := range regex {
if expr.MatchString(e.Message) { if expr.MatchString(e.Message) {
@ -47,7 +47,7 @@ func (state *NotifyState) SendTo(e *log.Entry) (*log.Entry, []string) {
} }
toList = append(toList, toEntry) toList = append(toList, toEntry)
} }
if replaceHostname, ok := state.Hostname[hostname]; ok { if replaceHostname, ok := db.Hostname[hostname]; ok {
entry := e.WithField("hostname", replaceHostname) entry := e.WithField("hostname", replaceHostname)
entry.Level = e.Level entry.Level = e.Level
entry.Message = e.Message entry.Message = e.Message
@ -55,48 +55,48 @@ func (state *NotifyState) SendTo(e *log.Entry) (*log.Entry, []string) {
} }
return e, toList return e, toList
} else { } else {
state.HostTo[hostname] = make(map[string]bool) db.HostTo[hostname] = make(map[string]bool)
} }
return e, nil return e, nil
} }
func (state *NotifyState) AddRegex(to, expression string) error { func (db *DB) AddRegex(to, expression string) error {
regex, err := regexp.Compile(expression) regex, err := regexp.Compile(expression)
if err == nil { if err == nil {
if _, ok := state.RegexIn[to]; !ok { if _, ok := db.RegexIn[to]; !ok {
state.RegexIn[to] = make(map[string]*regexp.Regexp) db.RegexIn[to] = make(map[string]*regexp.Regexp)
} }
state.RegexIn[to][expression] = regex db.RegexIn[to][expression] = regex
return nil return nil
} }
return err return err
} }
func ReadStateFile(path string) *NotifyState { func ReadDBFile(path string) *DB {
var state NotifyState var db DB
if err := file.ReadJSON(path, &state); err == nil { if err := file.ReadJSON(path, &db); err == nil {
log.Infof("loaded %d hosts", len(state.HostTo)) log.Infof("loaded %d hosts", len(db.HostTo))
if state.Lastseen == nil { if db.Lastseen == nil {
state.Lastseen = make(map[string]time.Time) db.Lastseen = make(map[string]time.Time)
} }
if state.LastseenNotify == nil { if db.LastseenNotify == nil {
state.LastseenNotify = make(map[string]time.Time) db.LastseenNotify = make(map[string]time.Time)
} }
if state.RegexIn == nil { if db.RegexIn == nil {
state.RegexIn = make(map[string]map[string]*regexp.Regexp) db.RegexIn = make(map[string]map[string]*regexp.Regexp)
} else { } else {
for to, regexs := range state.RegexIn { for to, regexs := range db.RegexIn {
for exp, _ := range regexs { for exp, _ := range regexs {
state.AddRegex(to, exp) db.AddRegex(to, exp)
} }
} }
} }
return &state return &db
} else { } 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), Hostname: make(map[string]string),
HostTo: make(map[string]map[string]bool), HostTo: make(map[string]map[string]bool),
MaxPrioIn: make(map[string]log.Level), 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) c := time.Tick(time.Minute)
for range c { for range c {
now := time.Now() now := time.Now()
for host, time := range state.Lastseen { for host, time := range db.Lastseen {
if time.Before(now.Add(expired * -2)) { if time.Before(now.Add(expired * -2)) {
if timeNotify, ok := state.LastseenNotify[host]; !ok || !time.Before(timeNotify) { if timeNotify, ok := db.LastseenNotify[host]; !ok || !time.Before(timeNotify) {
state.LastseenNotify[host] = now db.LastseenNotify[host] = now
entry := log.NewEntry(log.New()) entry := log.NewEntry(log.New())
entry.Level = log.ErrorLevel entry.Level = log.ErrorLevel
entry.Message = AlertMsg entry.Message = AlertMsg

View File

@ -3,10 +3,10 @@ host=$1
port=$2 port=$2
remote="circleci@${host}" remote="circleci@${host}"
echo "deploying..." echo "deploying..."
ssh -p $port $remote sudo systemctl stop logmania; ssh -o StrictHostKeyChecking=no -p $port $remote sudo systemctl stop logmania;
RETVAL=$? RETVAL=$?
[ $RETVAL -ne 0 ] && exit 1 [ $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=$? RETVAL=$?
ssh -p $port $remote sudo systemctl start logmania; ssh -p $port $remote sudo systemctl start logmania;
[ $RETVAL -eq 0 ] && RETVAL=$? [ $RETVAL -eq 0 ] && RETVAL=$?

View File

@ -3,12 +3,13 @@ package lib
// Struct of the configuration // Struct of the configuration
// e.g. under github.com/genofire/logmania/logmania_example.conf // e.g. under github.com/genofire/logmania/logmania_example.conf
type Config struct { type Config struct {
Notify NotifyConfig `toml:"notify"` Notify NotifyConfig `toml:"notify"`
Receive ReceiveConfig `toml:"receive"` Receive ReceiveConfig `toml:"receive"`
DB string `toml:"database"`
HTTPAddress string `toml:"http_address"`
} }
type NotifyConfig struct { type NotifyConfig struct {
StateFile string `toml:"state_file"`
AlertCheck Duration `toml:"alert_check"` AlertCheck Duration `toml:"alert_check"`
Console bool `toml:"debug"` Console bool `toml:"debug"`
XMPP struct { XMPP struct {
@ -22,8 +23,6 @@ type NotifyConfig struct {
StatusMessage string `toml:"status_message"` StatusMessage string `toml:"status_message"`
StartupNotify string `toml:"startup_notify"` StartupNotify string `toml:"startup_notify"`
} `toml:"xmpp"` } `toml:"xmpp"`
IRC struct {
} `toml:"irc"`
} }
type ReceiveConfig struct { type ReceiveConfig struct {

View File

@ -4,9 +4,9 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/genofire/logmania/bot" "github.com/genofire/logmania/bot"
"github.com/genofire/logmania/database"
"github.com/genofire/logmania/lib" "github.com/genofire/logmania/lib"
"github.com/genofire/logmania/notify" "github.com/genofire/logmania/notify"
configNotify "github.com/genofire/logmania/notify/config"
) )
type Notifier struct { type Notifier struct {
@ -15,10 +15,10 @@ type Notifier struct {
channelNotify chan *log.Entry 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 var list []notify.Notifier
for _, init := range notify.NotifyRegister { for _, init := range notify.NotifyRegister {
notify := init(config, state, bot) notify := init(config, db, bot)
if notify == nil { if notify == nil {
continue continue
@ -37,12 +37,12 @@ func Init(config *lib.NotifyConfig, state *configNotify.NotifyState, bot *bot.Bo
func (n *Notifier) sender() { func (n *Notifier) sender() {
for c := range n.channelNotify { for c := range n.channelNotify {
for _, item := range n.list { 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 n.channelNotify <- e
return nil return nil
} }
@ -52,14 +52,3 @@ func (n *Notifier) Close() {
item.Close() item.Close()
} }
} }
func (n *Notifier) Levels() []log.Level {
return []log.Level{
log.DebugLevel,
log.InfoLevel,
log.WarnLevel,
log.ErrorLevel,
log.FatalLevel,
log.PanicLevel,
}
}

View File

@ -4,19 +4,18 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/genofire/logmania/bot" "github.com/genofire/logmania/bot"
"github.com/genofire/logmania/database"
"github.com/genofire/logmania/lib" "github.com/genofire/logmania/lib"
configNotify "github.com/genofire/logmania/notify/config"
) )
var NotifyRegister []NotifyInit var NotifyRegister []NotifyInit
type Notifier interface { type Notifier interface {
Fire(entry *log.Entry) error Send(entry *log.Entry) error
Levels() []log.Level
Close() 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) { func AddNotifier(n NotifyInit) {
NotifyRegister = append(NotifyRegister, n) NotifyRegister = append(NotifyRegister, n)

View File

@ -3,27 +3,35 @@ package xmpp
import ( import (
"errors" "errors"
"fmt" "fmt"
"io"
"strings" "strings"
xmpp "github.com/mattn/go-xmpp" xmpp "github.com/mattn/go-xmpp"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/genofire/logmania/bot" "github.com/genofire/logmania/bot"
"github.com/genofire/logmania/database"
"github.com/genofire/logmania/lib" "github.com/genofire/logmania/lib"
"github.com/genofire/logmania/notify" "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 { type Notifier struct {
notify.Notifier notify.Notifier
client *xmpp.Client client *xmpp.Client
state *configNotify.NotifyState channels map[string]bool
db *database.DB
formatter *log.TextFormatter 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{ options := xmpp.Options{
Host: config.XMPP.Host, Host: config.XMPP.Host,
User: config.XMPP.Username, User: config.XMPP.Username,
@ -43,6 +51,14 @@ func Init(config *lib.NotifyConfig, state *configNotify.NotifyState, bot *bot.Bo
for { for {
chat, err := client.Recv() chat, err := client.Recv()
if err != nil { if err != nil {
if err == io.EOF {
client, err = options.NewClient()
log.Warn("reconnect")
if err != nil {
log.Panic(err)
}
continue
}
logger.Warn(err) logger.Warn(err)
} }
switch v := chat.(type) { 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") logger.Info("startup")
return &Notifier{ return &Notifier{
client: client, client: client,
state: state, db: db,
formatter: &log.TextFormatter{ formatter: &log.TextFormatter{
DisableTimestamp: true, DisableTimestamp: true,
}, },
} }
} }
func (n *Notifier) Fire(e *log.Entry) error { func (n *Notifier) Send(e *log.Entry) error {
e, to := n.state.SendTo(e) e, to := n.db.SendTo(e)
if to == nil { if to == nil {
return errors.New("no reciever found") return errors.New("no reciever found")
} }
@ -73,15 +95,18 @@ func (n *Notifier) Fire(e *log.Entry) error {
return err return err
} }
for _, toAddr := range to { for _, toAddr := range to {
to := strings.TrimPrefix(toAddr, "xmpp:") if strings.HasPrefix(toAddr, protoGroup) {
if strings.Contains(toAddr, "conference") || strings.Contains(toAddr, "irc") { toAddr = strings.TrimPrefix(toAddr, protoGroup)
n.client.JoinMUCNoHistory(to, "logmania") if _, ok := n.channels[toAddr]; ok {
_, err = n.client.SendHtml(xmpp.Chat{Remote: to, Type: "groupchat", Text: string(text)}) n.client.JoinMUCNoHistory(toAddr, nickname)
}
_, err = n.client.SendHtml(xmpp.Chat{Remote: toAddr, Type: "groupchat", Text: string(text)})
if err != nil { if err != nil {
logger.Error("xmpp to ", to, " error:", err) logger.Error("xmpp to ", to, " error:", err)
} }
} else { } 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 { if err != nil {
logger.Error("xmpp to ", to, " error:", err) logger.Error("xmpp to ", to, " error:", err)
} }
@ -90,19 +115,13 @@ func (n *Notifier) Fire(e *log.Entry) error {
return nil return nil
} }
func (n *Notifier) Levels() []log.Level { func (n *Notifier) Close() {
return []log.Level{ for jid := range n.channels {
log.DebugLevel, n.client.LeaveMUC(jid)
log.InfoLevel,
log.WarnLevel,
log.ErrorLevel,
log.FatalLevel,
log.PanicLevel,
} }
n.client.Close()
} }
func (n *Notifier) Close() {}
func init() { func init() {
notify.AddNotifier(Init) notify.AddNotifier(Init)
} }

View File

@ -2,5 +2,6 @@ package all
import ( import (
_ "github.com/genofire/logmania/receive/journald_json" _ "github.com/genofire/logmania/receive/journald_json"
_ "github.com/genofire/logmania/receive/logrus"
_ "github.com/genofire/logmania/receive/syslog" _ "github.com/genofire/logmania/receive/syslog"
) )

View File

@ -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)
}

View File

@ -0,0 +1 @@
package client

55
receive/logrus/main.go Normal file
View File

@ -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)
}