Implemented proper logging and added more log messages
This commit is contained in:
parent
5a90f9736b
commit
9534dc3492
7 changed files with 89 additions and 23 deletions
|
@ -31,11 +31,12 @@ Example:
|
|||
```json
|
||||
{
|
||||
"api_fetch_interval": 600,
|
||||
"datafile": "data",
|
||||
"enabled_api_endpoints": [
|
||||
"bay",
|
||||
"bund"
|
||||
],
|
||||
"datafile": "data",
|
||||
"loglevel": 2,
|
||||
"recipients": [
|
||||
{
|
||||
"address": "guenther@example.org",
|
||||
|
@ -59,6 +60,8 @@ Example:
|
|||
}
|
||||
```
|
||||
|
||||
To show debug messages, set the `loglevel` to `3`.
|
||||
|
||||
## Filters
|
||||
|
||||
You define filters for notices to be sent per recipient. Multiple filters can be set per recipient and multiple criteria can be used per filter. The configuration field for those filters is `include`. See [Configuration](#configuration) for an example.
|
||||
|
|
11
datastore.go
11
datastore.go
|
@ -5,7 +5,6 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"time"
|
||||
|
@ -15,6 +14,7 @@ type Config struct {
|
|||
ApiFetchInterval int `json:"api_fetch_interval"` // in seconds
|
||||
EnabledApiEndpoints []string `json:"enabled_api_endpoints"`
|
||||
PersistentDataFilePath string `json:"datafile"`
|
||||
LogLevel int `json:"loglevel"`
|
||||
Recipients []Recipient `json:"recipients"`
|
||||
SmtpConfiguration SmtpSettings `json:"smtp"`
|
||||
Template MailTemplateConfig `json:"template"`
|
||||
|
@ -26,6 +26,7 @@ func NewConfig() Config {
|
|||
ApiFetchInterval: 60 * 10, // every 10 minutes,
|
||||
EnabledApiEndpoints: []string{"bay", "bund"},
|
||||
PersistentDataFilePath: "data",
|
||||
LogLevel: 2,
|
||||
Recipients: []Recipient{},
|
||||
SmtpConfiguration: SmtpSettings{
|
||||
From: "from@example.org",
|
||||
|
@ -43,21 +44,21 @@ func NewConfig() Config {
|
|||
|
||||
func checkConfig(config Config) {
|
||||
if len(config.Recipients) < 1 {
|
||||
fmt.Println("ERROR\tConfiguration is incomplete.")
|
||||
logger.error("Configuration is incomplete")
|
||||
panic(errors.New("no recipients are configured"))
|
||||
}
|
||||
for _, r := range config.Recipients {
|
||||
if !mailAddressIsValid(r.Address) {
|
||||
fmt.Println("ERROR\tConfiguration includes invalid data.")
|
||||
logger.error("Configuration includes invalid data")
|
||||
panic(errors.New("'" + r.Address + "' is not a valid e-mail address"))
|
||||
}
|
||||
if len(r.Filters) < 1 {
|
||||
fmt.Println("ERROR\tConfiguration is incomplete.")
|
||||
logger.error("Configuration is incomplete")
|
||||
panic(errors.New("recipient " + r.Address + " has no filter defined - at least {'any': true/false} should be configured"))
|
||||
}
|
||||
}
|
||||
if !mailAddressIsValid(config.SmtpConfiguration.From) {
|
||||
fmt.Println("ERROR\tConfiguration includes invalid data.")
|
||||
logger.error("Configuration includes invalid data")
|
||||
panic(errors.New("'" + config.SmtpConfiguration.From + "' is not a valid e-mail address"))
|
||||
}
|
||||
}
|
||||
|
|
48
logging.go
Normal file
48
logging.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) 2023 Julian Müller (ChaoticByte)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
LogLevel int
|
||||
ErrorLogger *log.Logger // 0
|
||||
WarningLogger *log.Logger // 1
|
||||
InfoLogger *log.Logger // 2
|
||||
DebugLogger *log.Logger // 3
|
||||
}
|
||||
|
||||
func (l *Logger) error(msg any) {
|
||||
l.ErrorLogger.Println(msg)
|
||||
}
|
||||
|
||||
func (l *Logger) warn(msg any) {
|
||||
if l.LogLevel >= 1 {
|
||||
l.WarningLogger.Println(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) info(msg any) {
|
||||
if l.LogLevel >= 2 {
|
||||
l.InfoLogger.Println(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) debug(msg any) {
|
||||
if l.LogLevel >= 3 {
|
||||
l.DebugLogger.Println(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func NewLogger(loglevel int) Logger {
|
||||
l := Logger{}
|
||||
l.LogLevel = loglevel
|
||||
l.ErrorLogger = log.New(os.Stderr, "ERROR ", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile)
|
||||
l.WarningLogger = log.New(os.Stderr, "WARN ", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile)
|
||||
l.InfoLogger = log.New(os.Stderr, "INFO ", log.Ldate|log.Ltime|log.Lmicroseconds)
|
||||
l.DebugLogger = log.New(os.Stderr, "DEBUG ", log.Ldate|log.Ltime|log.Lmicroseconds)
|
||||
return l
|
||||
}
|
8
mail.go
8
mail.go
|
@ -64,11 +64,13 @@ func (r Recipient) filterAndSendNotices(notices []WidNotice, template MailTempla
|
|||
}
|
||||
}
|
||||
slices.Reverse(filteredNotices)
|
||||
logger.debug(fmt.Sprintf("Including %v of %v notices for recipient %v", len(filteredNotices), len(notices), r.Address))
|
||||
logger.debug("Templating and sending mails for recipient " + r.Address + " ...")
|
||||
for _, n := range filteredNotices {
|
||||
mailContent, err := template.generate(n)
|
||||
if err != nil {
|
||||
fmt.Println("ERROR\tCould not create mail from template.")
|
||||
fmt.Println(err)
|
||||
logger.error("Could not create mail from template")
|
||||
logger.error(err)
|
||||
}
|
||||
// serialize & send mail
|
||||
data := mailContent.serializeValidMail(smtpConfig.From, r.Address)
|
||||
|
@ -82,8 +84,8 @@ func (r Recipient) filterAndSendNotices(notices []WidNotice, template MailTempla
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// fmt.Printf("%v", strings.ReplaceAll(string(data), "\r", "\\r"))
|
||||
}
|
||||
logger.debug("Sent all mails for recipient " + r.Address)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
31
main.go
31
main.go
|
@ -9,6 +9,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var logger Logger
|
||||
|
||||
func main() {
|
||||
// get cli arguments
|
||||
args := os.Args
|
||||
|
@ -17,8 +19,11 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
configFilePath := os.Args[1]
|
||||
// create logger
|
||||
logger = NewLogger(2)
|
||||
// init
|
||||
println("INFO\tInitializing ...")
|
||||
logger.info("Initializing ...")
|
||||
defer logger.info("Exiting ...")
|
||||
config := NewDataStore(
|
||||
configFilePath,
|
||||
NewConfig(),
|
||||
|
@ -30,15 +35,17 @@ func main() {
|
|||
NewPersistentData(config),
|
||||
false,
|
||||
0640)
|
||||
// exit handler
|
||||
defer println("INFO\tExiting ...")
|
||||
// check config
|
||||
logger.LogLevel = config.LogLevel
|
||||
logger.debug("Checking configuration file ...")
|
||||
checkConfig(config)
|
||||
// create mail template from mail template config
|
||||
logger.debug("Parsing mail template ...")
|
||||
if config.Template.SubjectTemplate == "" {
|
||||
logger.debug("Using default template for mail subject")
|
||||
config.Template.SubjectTemplate = DEFAULT_SUBJECT_TEMPLATE
|
||||
}
|
||||
if config.Template.BodyTemplate == "" {
|
||||
logger.debug("Using default template for mail body")
|
||||
config.Template.BodyTemplate = DEFAULT_BODY_TEMPLATE
|
||||
}
|
||||
mailTemplate := NewTemplateFromTemplateConfig(config.Template)
|
||||
|
@ -54,42 +61,48 @@ func main() {
|
|||
for _, a := range apiEndpoints {
|
||||
for _, b := range config.EnabledApiEndpoints {
|
||||
if a.Id == b {
|
||||
logger.debug("Endpoint '" + b + "' is enabled")
|
||||
enabledApiEndpoints = append(enabledApiEndpoints, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
// main loop
|
||||
logger.debug("Entering main loop ...")
|
||||
for {
|
||||
t1 := time.Now().UnixMilli()
|
||||
newNotices := []WidNotice{}
|
||||
for _, a := range enabledApiEndpoints {
|
||||
fmt.Printf("INFO\t%v Querying endpoint '%v' for new notices ...\n", time.Now().Format(time.RFC3339Nano), a.Id)
|
||||
logger.info("Querying endpoint '" + a.Id + "' for new notices ...")
|
||||
n, t, err := a.getNotices(persistent.data.(PersistentData).LastPublished[a.Id])
|
||||
if err != nil {
|
||||
// retry
|
||||
logger.warn("Couldn't query notices from API endpoint '" + a.Id + "'. Retrying ...")
|
||||
logger.warn(err)
|
||||
n, t, err = a.getNotices(persistent.data.(PersistentData).LastPublished[a.Id])
|
||||
}
|
||||
if err != nil {
|
||||
// ok then...
|
||||
fmt.Print("ERROR\t", err)
|
||||
logger.error("Couldn't query notices from API endpoint '" + a.Id + "'")
|
||||
logger.error(err)
|
||||
} else {
|
||||
newNotices = append(newNotices, n...)
|
||||
persistent.data.(PersistentData).LastPublished[a.Id] = t
|
||||
persistent.save()
|
||||
}
|
||||
}
|
||||
logger.debug(fmt.Sprintf("Got %v new notices", len(newNotices)))
|
||||
if len(newNotices) > 0 {
|
||||
fmt.Printf("INFO\t%v Sending email notifications ...\n", time.Now().Format(time.RFC3339Nano))
|
||||
logger.info("Sending email notifications ...")
|
||||
recipientsNotified := 0
|
||||
for _, r := range config.Recipients {
|
||||
err := r.filterAndSendNotices(newNotices, mailTemplate, mailAuth, config.SmtpConfiguration)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR\t%v\n", err)
|
||||
logger.error(err)
|
||||
} else {
|
||||
recipientsNotified++
|
||||
}
|
||||
}
|
||||
fmt.Printf("INFO\t%v Email notifications sent to %v of %v recipients.\n", time.Now().Format(time.RFC3339Nano), recipientsNotified, len(config.Recipients))
|
||||
logger.info(fmt.Sprintf("Email notifications sent to %v of %v recipients", recipientsNotified, len(config.Recipients)))
|
||||
}
|
||||
t2 := time.Now().UnixMilli()
|
||||
dt := int(t2 - t1)
|
||||
|
|
|
@ -4,7 +4,6 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
|
@ -58,12 +57,12 @@ func (t MailTemplate) generate(notice WidNotice) (MailContent, error) {
|
|||
func NewTemplateFromTemplateConfig(tc MailTemplateConfig) MailTemplate {
|
||||
subjectTemplate, err := template.New("subject").Parse(tc.SubjectTemplate)
|
||||
if err != nil {
|
||||
fmt.Println("ERROR\tCould not parse template.")
|
||||
logger.error("Could not parse template")
|
||||
panic(err)
|
||||
}
|
||||
bodyTemplate, err := template.New("body").Parse(tc.BodyTemplate)
|
||||
if err != nil {
|
||||
fmt.Println("ERROR\tCould not parse template.")
|
||||
logger.error("Could not parse template")
|
||||
panic(err)
|
||||
}
|
||||
return MailTemplate{
|
||||
|
|
|
@ -66,7 +66,7 @@ func (e ApiEndpoint) getNotices(since time.Time) ([]WidNotice, time.Time, error)
|
|||
}
|
||||
notices = parseApiResponse(decodedData, e)
|
||||
} else {
|
||||
fmt.Printf("ERROR\tGet \"%v\": %v\n", url, res.Status)
|
||||
logger.error(fmt.Sprintf("Get \"%v\": %v\n", url, res.Status))
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
} else {
|
||||
|
@ -104,7 +104,7 @@ func parseApiResponse(data map[string]interface{}, apiEndpoint ApiEndpoint) []Wi
|
|||
}
|
||||
published, err := time.Parse(PUBLISHED_TIME_FORMAT, d["published"].(string))
|
||||
if err != nil {
|
||||
fmt.Println("ERROR\t", err)
|
||||
logger.error(err)
|
||||
}
|
||||
notice.Published = published
|
||||
// optional fields
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue