Skip to content
Snippets Groups Projects

Convert to Golang

Merged Erick Hitter requested to merge develop into master
1 file
+ 5
6
Compare changes
  • Side-by-side
  • Inline
+ 95
58
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/signal"
@@ -15,6 +17,22 @@ import (
"github.com/hpcloud/tail"
)
type config struct {
DebugDest string `json:"debug-dest"`
Debug bool `json:"debug"`
Logs []logConfig `json:"logs"`
}
type logConfig struct {
LogPath string `json:"log_path"`
WebhookURL string `json:"webhook_url"`
Username string `json:"username"`
Channel string `json:"channel"`
Color string `json:"color"`
IconURL string `json:"icon_url"`
SearchRegex string `json:"search"`
}
type attachment struct {
Fallback string `json:"fallback,omitempty"`
Pretext string `json:"pretext,omitempty"`
@@ -25,54 +43,73 @@ type attachment struct {
type attachments []attachment
var (
logPath string
webhookURL string
username string
channel string
color string
iconURL string
searchRegex string
mh *matterhook.Client
configPath string
logConfigs []logConfig
logger *log.Logger
debugDest string
debug bool
bad_var_name string
)
func init() {
flag.StringVar(&logPath, "log-path", "", "Log to monitor")
flag.StringVar(&webhookURL, "webhook", "", "Webhook to forward log entries to")
flag.StringVar(&username, "username", "logbot", "Username to post as")
flag.StringVar(&channel, "channel", "", "Channel to post log entries to")
flag.StringVar(&color, "color", "default", "Color for entry, either named or hex with `#`")
flag.StringVar(&iconURL, "icon-url", "", "URL of icon to use for bot")
flag.StringVar(&searchRegex, "search", "", "Search term or regex to match")
flag.StringVar(&debugDest, "debug-dest", "os.Stdout", "Destination for debug and other messages, omit to log to Stdout")
flag.BoolVar(&debug, "debug", false, "Include additional log data for debugging")
flag.StringVar(&configPath, "config", "./config.json", "Path to configuration file")
flag.Parse()
setUpLogger()
cfgPathValid := validatePath(&configPath)
if !cfgPathValid {
usage()
}
validatePath(&logPath)
configFile, err := ioutil.ReadFile(configPath)
if err != nil {
usage()
}
if ! govalidator.IsURL(webhookURL) || len(channel) < 2 {
cfg := config{}
if err = json.Unmarshal(configFile, &cfg); err != nil {
usage()
}
mh = matterhook.New(webhookURL, matterhook.Config{DisableServer: true})
debugDest = cfg.DebugDest
debug = cfg.Debug
logConfigs = cfg.Logs
setUpLogger()
}
func main() {
logger.Printf("Monitoring %s", logPath)
logger.Printf("Forwarding entries to channel \"%s\" as user \"%s\" at %s", channel, username, webhookURL)
logger.Printf("Starting log monitoring with config %s", configPath)
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
t, err := tail.TailFile(logPath, tail.Config{
for _, logCfg := range logConfigs {
go tailLog(logCfg)
}
caughtSig := <-sig
logger.Printf("Stopping, got signal %s", caughtSig)
}
func tailLog(logCfg logConfig) {
if logPathValid := validatePath(&logCfg.LogPath); !logPathValid {
if debug {
logger.Println("Invalid path: ", fmt.Sprintf("%+v\n", logCfg))
}
return
}
if !govalidator.IsURL(logCfg.WebhookURL) || len(logCfg.Username) == 0 || len(logCfg.Channel) < 2 {
if debug {
logger.Println("Invalid webhook, username, channel: ", fmt.Sprintf("%+v\n", logCfg))
}
return
}
t, err := tail.TailFile(logCfg.LogPath, tail.Config{
Follow: true,
Location: &tail.SeekInfo{Offset: 0, Whence: 2},
MustExist: true,
@@ -82,48 +119,46 @@ func main() {
if err != nil {
logger.Println(err)
t.Cleanup()
close(sig)
os.Exit(3)
return
}
go parseLinesAndSend(t)
mh := matterhook.New(logCfg.WebhookURL, matterhook.Config{DisableServer: true})
parseLinesAndSend(t, mh, logCfg)
caughtSig := <-sig
t.Stop()
t.Cleanup()
logger.Printf("Stopping, got signal %s", caughtSig)
}
func parseLinesAndSend(t *tail.Tail) {
func parseLinesAndSend(t *tail.Tail, mh *matterhook.Client, logCfg logConfig) {
for line := range t.Lines {
if line.Err != nil {
continue
}
if len(searchRegex) == 0 {
go sendLine(line)
} else if matched, _ := regexp.MatchString(searchRegex, line.Text); matched {
go sendLine(line)
if len(logCfg.SearchRegex) == 0 {
go sendLine(line, mh, logCfg)
} else if matched, _ := regexp.MatchString(logCfg.SearchRegex, line.Text); matched {
go sendLine(line, mh, logCfg)
}
}
}
func sendLine(line *tail.Line) {
func sendLine(line *tail.Line, mh *matterhook.Client, logCfg logConfig) {
atts := attachments{
attachment{
Fallback: fmt.Sprintf("New entry in %s", logPath),
Pretext: fmt.Sprintf("In `%s` at `%s`:", logPath, line.Time),
Fallback: fmt.Sprintf("New entry in %s", logCfg.LogPath),
Pretext: fmt.Sprintf("In `%s` at `%s`:", logCfg.LogPath, line.Time),
Text: fmt.Sprintf(" %s", line.Text),
Color: color,
Color: logCfg.Color,
},
}
mh.Send(matterhook.OMessage{
Channel: channel,
UserName: username,
Channel: logCfg.Channel,
UserName: logCfg.Username,
Attachments: atts,
IconURL: iconURL,
IconURL: logCfg.IconURL,
})
}
@@ -147,22 +182,24 @@ func setUpLogger() {
}
}
func validatePath(path *string) {
if len(*path) > 1 {
var err error
*path, err = filepath.Abs(*path)
func validatePath(path *string) bool {
if len(*path) <= 1 {
return false
}
if err != nil {
fmt.Printf("Error: %s", err.Error())
os.Exit(3)
}
var err error
*path, err = filepath.Abs(*path)
if _, err = os.Stat(*path); os.IsNotExist(err) {
usage()
}
} else {
usage()
if err != nil {
logger.Printf("Error: %s", err.Error())
return false
}
if _, err = os.Stat(*path); os.IsNotExist(err) {
return false
}
return true
}
func usage() {
Loading