[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
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

View File

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

View File

@ -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,

View File

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

View File

@ -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() {

View File

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

View File

@ -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=$?

View File

@ -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 {

View File

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

View File

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

View File

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

View File

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

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