syslog reciever complete: sry filter notifier
This commit is contained in:
parent
992c0eaf02
commit
b38f97d97d
|
@ -24,32 +24,38 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configPath string
|
configPath string
|
||||||
config *lib.Config
|
config *lib.Config
|
||||||
notifier notify.Notifier
|
notifyConfig *notify.NotifyState
|
||||||
receiver receive.Receiver
|
notifier notify.Notifier
|
||||||
logChannel chan *log.Entry
|
receiver receive.Receiver
|
||||||
|
logChannel chan *log.Entry
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.StringVar(&configPath, "config", "logmania.conf", "config file")
|
flag.StringVar(&configPath, "config", "logmania.conf", "config file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
log.Info("starting logmania")
|
|
||||||
|
|
||||||
config, err := lib.ReadConfig(configPath)
|
config, err := lib.ReadConfig(configPath)
|
||||||
if config == nil || err != nil {
|
if config == nil || err != nil {
|
||||||
log.Panicf("Could not load '%s' for configuration.", configPath)
|
log.Panicf("Could not load '%s' for configuration.", configPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
notifier = allNotify.Init(&config.Notify)
|
notifyConfig := notify.ReadStateFile(config.Notify.StateFile)
|
||||||
|
go notifyConfig.Saver(config.Notify.StateFile)
|
||||||
|
|
||||||
|
notifier = allNotify.Init(&config.Notify, notifyConfig)
|
||||||
log.Save = notifier.Send
|
log.Save = notifier.Send
|
||||||
|
logChannel = make(chan *log.Entry)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for a := range logChannel {
|
for a := range logChannel {
|
||||||
notifier.Send(a)
|
log.Save(a)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
log.Info("starting logmania")
|
||||||
|
|
||||||
receiver = allReceiver.Init(&config.Receive, logChannel)
|
receiver = allReceiver.Init(&config.Receive, logChannel)
|
||||||
|
|
||||||
go receiver.Listen()
|
go receiver.Listen()
|
||||||
|
@ -91,5 +97,5 @@ func reload() {
|
||||||
go receiver.Listen()
|
go receiver.Listen()
|
||||||
|
|
||||||
notifier.Close()
|
notifier.Close()
|
||||||
notifier = allNotify.Init(&config.Notify)
|
notifier = allNotify.Init(&config.Notify, notifyConfig)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifyConfig struct {
|
type NotifyConfig struct {
|
||||||
XMPP struct {
|
StateFile string `toml:"state_file"`
|
||||||
|
XMPP struct {
|
||||||
Host string `toml:"host"`
|
Host string `toml:"host"`
|
||||||
Username string `toml:"username"`
|
Username string `toml:"username"`
|
||||||
Password string `toml:"password"`
|
Password string `toml:"password"`
|
||||||
|
@ -33,7 +34,8 @@ type NotifyConfig struct {
|
||||||
|
|
||||||
type ReceiveConfig struct {
|
type ReceiveConfig struct {
|
||||||
Syslog struct {
|
Syslog struct {
|
||||||
Bind string `toml:"bind"`
|
Type string `toml:"type"`
|
||||||
|
Address string `toml:"address"`
|
||||||
} `toml:"syslog"`
|
} `toml:"syslog"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
package syslog
|
|
@ -1,6 +1,7 @@
|
||||||
// logger to bind at github.com/genofire/logmania/log.AddLogger to send log entries to logmania server
|
|
||||||
package client
|
package client
|
||||||
|
|
||||||
|
/* logger to bind at github.com/genofire/logmania/log.AddLogger to send log entries to logmania server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
@ -80,3 +81,4 @@ func Init(url, token string, AboveLevel log.LogLevel) *Logger {
|
||||||
log.AddLogger(LOGGER_NAME, CurrentLogger)
|
log.AddLogger(LOGGER_NAME, CurrentLogger)
|
||||||
return CurrentLogger
|
return CurrentLogger
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
|
[notify]
|
||||||
|
state_file = "/tmp/logmania.state.json"
|
||||||
|
|
||||||
[receive.syslog]
|
[receive.syslog]
|
||||||
bind = ":10001"
|
type = "udp"
|
||||||
|
address = ":10001"
|
||||||
|
|
|
@ -12,10 +12,10 @@ type Notifier struct {
|
||||||
channelNotify chan *log.Entry
|
channelNotify chan *log.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(config *lib.NotifyConfig) notify.Notifier {
|
func Init(config *lib.NotifyConfig, state *notify.NotifyState) notify.Notifier {
|
||||||
var list []notify.Notifier
|
var list []notify.Notifier
|
||||||
for _, init := range notify.NotifyRegister {
|
for _, init := range notify.NotifyRegister {
|
||||||
notify := init(config)
|
notify := init(config, state)
|
||||||
|
|
||||||
if notify == nil {
|
if notify == nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/genofire/logmania/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotifyState struct {
|
||||||
|
Hostname map[string]string `json:"hostname"`
|
||||||
|
HostTo map[string][]string `json:"host_to"`
|
||||||
|
MaxPrioIn map[string]log.LogLevel `json:"maxLevel"`
|
||||||
|
RegexIn map[string][]string `json:"regexIn"`
|
||||||
|
regexIn map[string][]*regexp.Regexp `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *NotifyState) SendTo(e *log.Entry) []string {
|
||||||
|
if to, ok := state.HostTo[e.Hostname]; ok {
|
||||||
|
var toList []string
|
||||||
|
for _, toEntry := range to {
|
||||||
|
if lvl := state.MaxPrioIn[toEntry]; e.Level > lvl {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
toList = append(toList, toEntry)
|
||||||
|
}
|
||||||
|
e.Hostname = state.Hostname[e.Hostname]
|
||||||
|
return toList
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadStateFile(path string) *NotifyState {
|
||||||
|
var state *NotifyState
|
||||||
|
if f, err := os.Open(path); err == nil { // transform data to legacy meshviewer
|
||||||
|
if err = json.NewDecoder(f).Decode(state); err == nil {
|
||||||
|
log.Info("loaded", len(state.HostTo), "nodes")
|
||||||
|
state.regexIn = make(map[string][]*regexp.Regexp)
|
||||||
|
return state
|
||||||
|
} else {
|
||||||
|
log.Error("failed to unmarshal nodes:", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Error("failed to open state notify file: ", path, ":", err)
|
||||||
|
}
|
||||||
|
return &NotifyState{
|
||||||
|
Hostname: make(map[string]string),
|
||||||
|
HostTo: make(map[string][]string),
|
||||||
|
MaxPrioIn: make(map[string]log.LogLevel),
|
||||||
|
RegexIn: make(map[string][]string),
|
||||||
|
regexIn: make(map[string][]*regexp.Regexp),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *NotifyState) Saver(path string) {
|
||||||
|
c := time.Tick(time.Minute)
|
||||||
|
|
||||||
|
for range c {
|
||||||
|
state.SaveJSON(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveJSON to path
|
||||||
|
func (state *NotifyState) SaveJSON(outputFile string) {
|
||||||
|
tmpFile := outputFile + ".tmp"
|
||||||
|
|
||||||
|
f, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewEncoder(f).Encode(state)
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
if err := os.Rename(tmpFile, outputFile); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ type Notifier struct {
|
||||||
ShowTime bool
|
ShowTime bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(config *lib.NotifyConfig) notify.Notifier {
|
func Init(config *lib.NotifyConfig, state *notify.NotifyState) notify.Notifier {
|
||||||
return &Notifier{
|
return &Notifier{
|
||||||
TimeFormat: "2006-01-02 15:04:05",
|
TimeFormat: "2006-01-02 15:04:05",
|
||||||
ShowTime: true,
|
ShowTime: true,
|
||||||
|
@ -35,12 +35,17 @@ func Init(config *lib.NotifyConfig) notify.Notifier {
|
||||||
// handle a log entry (print it on the terminal with color)
|
// handle a log entry (print it on the terminal with color)
|
||||||
func (n *Notifier) Send(e *log.Entry) {
|
func (n *Notifier) Send(e *log.Entry) {
|
||||||
v := []interface{}{}
|
v := []interface{}{}
|
||||||
format := "[%s] %s"
|
format := "[%s]"
|
||||||
|
|
||||||
if n.ShowTime {
|
if n.ShowTime {
|
||||||
format = "%s [%s] %s"
|
format = "%s [%s]"
|
||||||
v = append(v, color.LightBlue(time.Now().Format(n.TimeFormat)))
|
v = append(v, color.LightBlue(time.Now().Format(n.TimeFormat)))
|
||||||
}
|
}
|
||||||
|
if e.Hostname != "" {
|
||||||
|
format = fmt.Sprintf("%s [%%s]", format)
|
||||||
|
v = append(v, color.Purple(e.Hostname))
|
||||||
|
}
|
||||||
|
format = fmt.Sprintf("%s %%s", format)
|
||||||
lvl := e.Level.String()
|
lvl := e.Level.String()
|
||||||
switch e.Level {
|
switch e.Level {
|
||||||
case log.DebugLevel:
|
case log.DebugLevel:
|
||||||
|
|
|
@ -12,7 +12,7 @@ type Notifier interface {
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifyInit func(*lib.NotifyConfig) Notifier
|
type NotifyInit func(*lib.NotifyConfig, *NotifyState) Notifier
|
||||||
|
|
||||||
func AddNotifier(n NotifyInit) {
|
func AddNotifier(n NotifyInit) {
|
||||||
NotifyRegister = append(NotifyRegister, n)
|
NotifyRegister = append(NotifyRegister, n)
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
package xmpp
|
package xmpp
|
||||||
|
|
||||||
import "github.com/genofire/logmania/log"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/genofire/logmania/log"
|
||||||
|
)
|
||||||
|
|
||||||
func formatEntry(e *log.Entry) string {
|
func formatEntry(e *log.Entry) string {
|
||||||
return e.Text
|
return fmt.Sprintf("[%s] [%s] %s", e.Hostname, e.Level, e.Text)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,10 @@ import (
|
||||||
type Notifier struct {
|
type Notifier struct {
|
||||||
notify.Notifier
|
notify.Notifier
|
||||||
client *xmpp.Client
|
client *xmpp.Client
|
||||||
|
state *notify.NotifyState
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(config *lib.NotifyConfig) notify.Notifier {
|
func Init(config *lib.NotifyConfig, state *notify.NotifyState) notify.Notifier {
|
||||||
options := xmpp.Options{
|
options := xmpp.Options{
|
||||||
Host: config.XMPP.Host,
|
Host: config.XMPP.Host,
|
||||||
User: config.XMPP.Username,
|
User: config.XMPP.Username,
|
||||||
|
@ -27,16 +28,17 @@ func Init(config *lib.NotifyConfig) notify.Notifier {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &Notifier{client: client}
|
return &Notifier{client: client, state: state}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notifier) Send(e *log.Entry) {
|
func (n *Notifier) Send(e *log.Entry) {
|
||||||
/*users :=
|
to := n.state.SendTo(e)
|
||||||
for _, user := range users {
|
if to == nil {
|
||||||
if user.NotifyXMPP && log.LogLevel(e.Level) >= user.NotifyAfterLoglevel {
|
return
|
||||||
n.client.SendHtml(xmpp.Chat{Remote: user.XMPP, Type: "chat", Text: formatEntry(e)})
|
}
|
||||||
}
|
for _, to := range to {
|
||||||
}*/
|
n.client.SendHtml(xmpp.Chat{Remote: to, Type: "chat", Text: formatEntry(e)})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notifier) Close() {}
|
func (n *Notifier) Close() {}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
package syslog
|
package syslog
|
||||||
|
|
||||||
import "github.com/genofire/logmania/log"
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
var SyslogPriorityMap = map[uint]log.LogLevel{
|
"github.com/genofire/logmania/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SyslogPriorityMap = map[int]log.LogLevel{
|
||||||
0: log.PanicLevel,
|
0: log.PanicLevel,
|
||||||
1: log.PanicLevel,
|
1: log.PanicLevel,
|
||||||
2: log.PanicLevel,
|
2: log.PanicLevel,
|
||||||
|
@ -13,23 +18,24 @@ var SyslogPriorityMap = map[uint]log.LogLevel{
|
||||||
7: log.DebugLevel,
|
7: log.DebugLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
func toLogEntry(logParts map[string]interface{}) *log.Entry {
|
func toLogEntry(msg []byte, from string) *log.Entry {
|
||||||
severityID := uint(logParts["severity"].(int))
|
re := regexp.MustCompile("<([0-9]*)>(.*)")
|
||||||
level := SyslogPriorityMap[severityID]
|
match := re.FindStringSubmatch(string(msg))
|
||||||
|
|
||||||
if _, ok := logParts["content"]; ok {
|
if len(match) <= 1 {
|
||||||
return &log.Entry{
|
return &log.Entry{
|
||||||
Level: level,
|
Level: log.DebugLevel,
|
||||||
Hostname: logParts["hostname"].(string),
|
Text: string(msg),
|
||||||
Service: logParts["tag"].(string),
|
Hostname: from,
|
||||||
Text: logParts["content"].(string),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
v, _ := strconv.Atoi(match[1])
|
||||||
|
prio := v % 8
|
||||||
|
text := match[2]
|
||||||
|
|
||||||
return &log.Entry{
|
return &log.Entry{
|
||||||
Level: level,
|
Level: SyslogPriorityMap[prio],
|
||||||
Hostname: logParts["hostname"].(string),
|
Text: text,
|
||||||
Service: logParts["app_name"].(string),
|
Hostname: from,
|
||||||
Text: logParts["message"].(string),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package syslog
|
package syslog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/mcuadros/go-syslog.v2"
|
"net"
|
||||||
|
|
||||||
"github.com/genofire/logmania/lib"
|
"github.com/genofire/logmania/lib"
|
||||||
"github.com/genofire/logmania/log"
|
"github.com/genofire/logmania/log"
|
||||||
|
@ -9,43 +9,49 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Receiver struct {
|
type Receiver struct {
|
||||||
channel syslog.LogPartsChannel
|
|
||||||
exportChannel chan *log.Entry
|
|
||||||
server *syslog.Server
|
|
||||||
receive.Receiver
|
receive.Receiver
|
||||||
|
exportChannel chan *log.Entry
|
||||||
|
serverSocket *net.UDPConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(config *lib.ReceiveConfig, exportChannel chan *log.Entry) receive.Receiver {
|
func Init(config *lib.ReceiveConfig, exportChannel chan *log.Entry) receive.Receiver {
|
||||||
channel := make(syslog.LogPartsChannel)
|
addr, err := net.ResolveUDPAddr(config.Syslog.Type, config.Syslog.Address)
|
||||||
handler := syslog.NewChannelHandler(channel)
|
ln, err := net.ListenUDP(config.Syslog.Type, addr)
|
||||||
|
|
||||||
server := syslog.NewServer()
|
if err != nil {
|
||||||
server.SetFormat(syslog.RFC5424)
|
log.Error("syslog init ", err)
|
||||||
server.SetHandler(handler)
|
return nil
|
||||||
server.ListenUDP(config.Syslog.Bind)
|
}
|
||||||
|
recv := &Receiver{
|
||||||
log.Info("syslog binded to: ", config.Syslog.Bind)
|
serverSocket: ln,
|
||||||
|
|
||||||
return &Receiver{
|
|
||||||
channel: channel,
|
|
||||||
server: server,
|
|
||||||
exportChannel: exportChannel,
|
exportChannel: exportChannel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Info("syslog init")
|
||||||
|
|
||||||
|
return recv
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxDataGramSize = 8192
|
||||||
|
|
||||||
|
func (rc *Receiver) Listen() {
|
||||||
|
log.Info("syslog listen")
|
||||||
|
for {
|
||||||
|
buf := make([]byte, maxDataGramSize)
|
||||||
|
n, src, err := rc.serverSocket.ReadFromUDP(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("failed to accept connection", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := make([]byte, n)
|
||||||
|
copy(raw, buf)
|
||||||
|
rc.exportChannel <- toLogEntry(raw, src.IP.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *Receiver) Listen() {
|
|
||||||
rc.server.Boot()
|
|
||||||
log.Info("boot syslog")
|
|
||||||
go func(channel syslog.LogPartsChannel) {
|
|
||||||
for logParts := range channel {
|
|
||||||
rc.exportChannel <- toLogEntry(logParts)
|
|
||||||
}
|
|
||||||
}(rc.channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rc *Receiver) Close() {
|
func (rc *Receiver) Close() {
|
||||||
rc.server.Kill()
|
rc.serverSocket.Close()
|
||||||
rc.server.Wait()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
Loading…
Reference in New Issue