Implemented proper logging and added more log messages

This commit is contained in:
ChaoticByte 2023-10-14 10:41:22 +02:00
parent 5a90f9736b
commit 9534dc3492
7 changed files with 89 additions and 23 deletions

View file

@ -31,11 +31,12 @@ Example:
```json ```json
{ {
"api_fetch_interval": 600, "api_fetch_interval": 600,
"datafile": "data",
"enabled_api_endpoints": [ "enabled_api_endpoints": [
"bay", "bay",
"bund" "bund"
], ],
"datafile": "data", "loglevel": 2,
"recipients": [ "recipients": [
{ {
"address": "guenther@example.org", "address": "guenther@example.org",
@ -59,6 +60,8 @@ Example:
} }
``` ```
To show debug messages, set the `loglevel` to `3`.
## Filters ## 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. 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.

View file

@ -5,7 +5,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io/fs" "io/fs"
"os" "os"
"time" "time"
@ -15,6 +14,7 @@ type Config struct {
ApiFetchInterval int `json:"api_fetch_interval"` // in seconds ApiFetchInterval int `json:"api_fetch_interval"` // in seconds
EnabledApiEndpoints []string `json:"enabled_api_endpoints"` EnabledApiEndpoints []string `json:"enabled_api_endpoints"`
PersistentDataFilePath string `json:"datafile"` PersistentDataFilePath string `json:"datafile"`
LogLevel int `json:"loglevel"`
Recipients []Recipient `json:"recipients"` Recipients []Recipient `json:"recipients"`
SmtpConfiguration SmtpSettings `json:"smtp"` SmtpConfiguration SmtpSettings `json:"smtp"`
Template MailTemplateConfig `json:"template"` Template MailTemplateConfig `json:"template"`
@ -26,6 +26,7 @@ func NewConfig() Config {
ApiFetchInterval: 60 * 10, // every 10 minutes, ApiFetchInterval: 60 * 10, // every 10 minutes,
EnabledApiEndpoints: []string{"bay", "bund"}, EnabledApiEndpoints: []string{"bay", "bund"},
PersistentDataFilePath: "data", PersistentDataFilePath: "data",
LogLevel: 2,
Recipients: []Recipient{}, Recipients: []Recipient{},
SmtpConfiguration: SmtpSettings{ SmtpConfiguration: SmtpSettings{
From: "from@example.org", From: "from@example.org",
@ -43,21 +44,21 @@ func NewConfig() Config {
func checkConfig(config Config) { func checkConfig(config Config) {
if len(config.Recipients) < 1 { if len(config.Recipients) < 1 {
fmt.Println("ERROR\tConfiguration is incomplete.") logger.error("Configuration is incomplete")
panic(errors.New("no recipients are configured")) panic(errors.New("no recipients are configured"))
} }
for _, r := range config.Recipients { for _, r := range config.Recipients {
if !mailAddressIsValid(r.Address) { 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")) panic(errors.New("'" + r.Address + "' is not a valid e-mail address"))
} }
if len(r.Filters) < 1 { 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")) panic(errors.New("recipient " + r.Address + " has no filter defined - at least {'any': true/false} should be configured"))
} }
} }
if !mailAddressIsValid(config.SmtpConfiguration.From) { 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")) panic(errors.New("'" + config.SmtpConfiguration.From + "' is not a valid e-mail address"))
} }
} }

48
logging.go Normal file
View 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
}

View file

@ -64,11 +64,13 @@ func (r Recipient) filterAndSendNotices(notices []WidNotice, template MailTempla
} }
} }
slices.Reverse(filteredNotices) 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 { for _, n := range filteredNotices {
mailContent, err := template.generate(n) mailContent, err := template.generate(n)
if err != nil { if err != nil {
fmt.Println("ERROR\tCould not create mail from template.") logger.error("Could not create mail from template")
fmt.Println(err) logger.error(err)
} }
// serialize & send mail // serialize & send mail
data := mailContent.serializeValidMail(smtpConfig.From, r.Address) data := mailContent.serializeValidMail(smtpConfig.From, r.Address)
@ -82,8 +84,8 @@ func (r Recipient) filterAndSendNotices(notices []WidNotice, template MailTempla
if err != nil { if err != nil {
return err return err
} }
// fmt.Printf("%v", strings.ReplaceAll(string(data), "\r", "\\r"))
} }
logger.debug("Sent all mails for recipient " + r.Address)
return nil return nil
} }

31
main.go
View file

@ -9,6 +9,8 @@ import (
"time" "time"
) )
var logger Logger
func main() { func main() {
// get cli arguments // get cli arguments
args := os.Args args := os.Args
@ -17,8 +19,11 @@ func main() {
os.Exit(1) os.Exit(1)
} }
configFilePath := os.Args[1] configFilePath := os.Args[1]
// create logger
logger = NewLogger(2)
// init // init
println("INFO\tInitializing ...") logger.info("Initializing ...")
defer logger.info("Exiting ...")
config := NewDataStore( config := NewDataStore(
configFilePath, configFilePath,
NewConfig(), NewConfig(),
@ -30,15 +35,17 @@ func main() {
NewPersistentData(config), NewPersistentData(config),
false, false,
0640) 0640)
// exit handler logger.LogLevel = config.LogLevel
defer println("INFO\tExiting ...") logger.debug("Checking configuration file ...")
// check config
checkConfig(config) checkConfig(config)
// create mail template from mail template config // create mail template from mail template config
logger.debug("Parsing mail template ...")
if config.Template.SubjectTemplate == "" { if config.Template.SubjectTemplate == "" {
logger.debug("Using default template for mail subject")
config.Template.SubjectTemplate = DEFAULT_SUBJECT_TEMPLATE config.Template.SubjectTemplate = DEFAULT_SUBJECT_TEMPLATE
} }
if config.Template.BodyTemplate == "" { if config.Template.BodyTemplate == "" {
logger.debug("Using default template for mail body")
config.Template.BodyTemplate = DEFAULT_BODY_TEMPLATE config.Template.BodyTemplate = DEFAULT_BODY_TEMPLATE
} }
mailTemplate := NewTemplateFromTemplateConfig(config.Template) mailTemplate := NewTemplateFromTemplateConfig(config.Template)
@ -54,42 +61,48 @@ func main() {
for _, a := range apiEndpoints { for _, a := range apiEndpoints {
for _, b := range config.EnabledApiEndpoints { for _, b := range config.EnabledApiEndpoints {
if a.Id == b { if a.Id == b {
logger.debug("Endpoint '" + b + "' is enabled")
enabledApiEndpoints = append(enabledApiEndpoints, a) enabledApiEndpoints = append(enabledApiEndpoints, a)
} }
} }
} }
// main loop // main loop
logger.debug("Entering main loop ...")
for { for {
t1 := time.Now().UnixMilli() t1 := time.Now().UnixMilli()
newNotices := []WidNotice{} newNotices := []WidNotice{}
for _, a := range enabledApiEndpoints { 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]) n, t, err := a.getNotices(persistent.data.(PersistentData).LastPublished[a.Id])
if err != nil { if err != nil {
// retry // 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]) n, t, err = a.getNotices(persistent.data.(PersistentData).LastPublished[a.Id])
} }
if err != nil { if err != nil {
// ok then... // ok then...
fmt.Print("ERROR\t", err) logger.error("Couldn't query notices from API endpoint '" + a.Id + "'")
logger.error(err)
} else { } else {
newNotices = append(newNotices, n...) newNotices = append(newNotices, n...)
persistent.data.(PersistentData).LastPublished[a.Id] = t persistent.data.(PersistentData).LastPublished[a.Id] = t
persistent.save() persistent.save()
} }
} }
logger.debug(fmt.Sprintf("Got %v new notices", len(newNotices)))
if len(newNotices) > 0 { 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 recipientsNotified := 0
for _, r := range config.Recipients { for _, r := range config.Recipients {
err := r.filterAndSendNotices(newNotices, mailTemplate, mailAuth, config.SmtpConfiguration) err := r.filterAndSendNotices(newNotices, mailTemplate, mailAuth, config.SmtpConfiguration)
if err != nil { if err != nil {
fmt.Printf("ERROR\t%v\n", err) logger.error(err)
} else { } else {
recipientsNotified++ 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() t2 := time.Now().UnixMilli()
dt := int(t2 - t1) dt := int(t2 - t1)

View file

@ -4,7 +4,6 @@ package main
import ( import (
"bytes" "bytes"
"fmt"
"text/template" "text/template"
) )
@ -58,12 +57,12 @@ func (t MailTemplate) generate(notice WidNotice) (MailContent, error) {
func NewTemplateFromTemplateConfig(tc MailTemplateConfig) MailTemplate { func NewTemplateFromTemplateConfig(tc MailTemplateConfig) MailTemplate {
subjectTemplate, err := template.New("subject").Parse(tc.SubjectTemplate) subjectTemplate, err := template.New("subject").Parse(tc.SubjectTemplate)
if err != nil { if err != nil {
fmt.Println("ERROR\tCould not parse template.") logger.error("Could not parse template")
panic(err) panic(err)
} }
bodyTemplate, err := template.New("body").Parse(tc.BodyTemplate) bodyTemplate, err := template.New("body").Parse(tc.BodyTemplate)
if err != nil { if err != nil {
fmt.Println("ERROR\tCould not parse template.") logger.error("Could not parse template")
panic(err) panic(err)
} }
return MailTemplate{ return MailTemplate{

View file

@ -66,7 +66,7 @@ func (e ApiEndpoint) getNotices(since time.Time) ([]WidNotice, time.Time, error)
} }
notices = parseApiResponse(decodedData, e) notices = parseApiResponse(decodedData, e)
} else { } 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 return nil, time.Time{}, err
} }
} else { } else {
@ -104,7 +104,7 @@ func parseApiResponse(data map[string]interface{}, apiEndpoint ApiEndpoint) []Wi
} }
published, err := time.Parse(PUBLISHED_TIME_FORMAT, d["published"].(string)) published, err := time.Parse(PUBLISHED_TIME_FORMAT, d["published"].(string))
if err != nil { if err != nil {
fmt.Println("ERROR\t", err) logger.error(err)
} }
notice.Published = published notice.Published = published
// optional fields // optional fields