feat: logging (#20)
All checks were successful
CD / test (push) Successful in 52s
CD / Build and push (push) Successful in 15m10s

Reviewed-on: #20
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
This commit was merged in pull request #20.
This commit is contained in:
2024-12-27 19:52:21 +01:00
committed by t.behrendt
parent 1ea43ac4cf
commit 3ad5b1ec0e
4 changed files with 119 additions and 8 deletions

59
main.go
View File

@@ -2,6 +2,9 @@ package main
import (
"fmt"
"log/slog"
"os"
"strings"
"realdnydns/pkg/config"
"realdnydns/pkg/dnsProvider"
@@ -15,48 +18,84 @@ import (
)
func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
configClient := config.Config{}
err := configClient.Load("config.yaml")
if err != nil {
logger.Error("Failed to load config file", slog.String("error", err.Error()))
panic(err)
}
if configClient.LogLevel != "" {
logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.Level(config.LogLevelMap[strings.ToLower(configClient.LogLevel)]),
}))
}
var externalIpProvider externalIpProvider.ExternalIpProvider
switch configClient.ExternalIPProvider.Type {
case "plain":
logger.Info("Using plain external IP provider", slog.String("external_ip_provider", "plain"))
var plainConfig plainExternalIpProvider.PlainExternalIpProviderConfig
err := configClient.ExternalIPProvider.ProviderConfig.Decode(&plainConfig)
if err != nil {
logger.Error("Failed to create config",
slog.String("external_ip_provider", "plain"),
slog.String("error", err.Error()),
)
panic(err)
}
externalIpProvider, err = plainExternalIpProvider.New(plainConfig)
if err != nil {
logger.Error("Failed to create plain external IP provider",
slog.String("external_ip_provider", "plain"),
slog.String("error", err.Error()),
)
panic(err)
}
default:
logger.Error("Unknown external IP provider", slog.String("external_ip_provider", configClient.ExternalIPProvider.Type))
panic(fmt.Errorf("unknown external IP provider: %s", configClient.ExternalIPProvider.Type))
}
var dnsProvider dnsProvider.DNSProvider
switch configClient.DNSProvider.Type {
case "ionos":
logger.Info("Using IONOS DNS provider", slog.String("dns_provider", "ionos"))
var ionosConfig ionos.IONOSConfig
err := configClient.DNSProvider.ProviderConfig.Decode(&ionosConfig)
if err != nil {
logger.Error("Failed to create IONOS DNS provider",
slog.String("dns_provider", "ionos"),
slog.String("error", err.Error()),
)
panic(err)
}
dnsProvider, err = ionos.NewIonos(&ionosConfig)
if err != nil {
logger.Error("Failed to create IONOS DNS provider",
slog.String("dns_provider", "ionos"),
slog.String("error", err.Error()),
)
panic(err)
}
default:
logger.Error("Unknown DNS provider", slog.String("dns_provider", configClient.DNSProvider.Type))
panic(fmt.Errorf("unknown DNS provider: %s", configClient.DNSProvider.Type))
}
var notificationProvider notificationProvider.NotificationProvider
switch configClient.NotificationProvider.Type {
case "gotify":
logger.Info("Using Gotify notification provider", slog.String("notification_provider", "gotify"))
var gotifyConfig gotify.NotificationProviderImplGotifyConfig
err := configClient.NotificationProvider.ProviderConfig.Decode(&gotifyConfig)
if err != nil {
@@ -65,29 +104,39 @@ func main() {
notificationProvider, err = gotify.New(gotifyConfig)
if err != nil {
logger.Error("Failed to create Gotify notification provider",
slog.String("notification_provider", "gotify"),
slog.String("error", err.Error()),
)
panic(err)
}
default:
logger.Info("Using console notification provider", slog.String("notification_provider", "console"))
notificationProvider = notificationProviderConsole.New()
}
rdd := realDynDns.New(externalIpProvider, dnsProvider, notificationProvider, configClient.Domains)
rdd := realDynDns.New(externalIpProvider, dnsProvider, notificationProvider, configClient.Domains, logger.With(slog.String("service", "realDynDns")))
switch configClient.Mode {
case config.ScheduledMode:
logger.Info("Running in scheduled mode", slog.String("interval", configClient.CheckInterval))
schedule, job, err := rdd.RunWithSchedule(configClient.CheckInterval)
if err != nil {
logger.Error("Failed to create scheduler", slog.String("error", err.Error()))
panic(err)
}
fmt.Println("Starting scheduler")
fmt.Println("Next run:", job.NextRun())
logger.Info("Next run:", slog.String("time", job.NextRun().String()))
schedule.StartBlocking()
case config.RunOnceMode:
numberOfChanges, err := rdd.RunOnce()
logger.Info("Running in run once mode")
_, err := rdd.RunOnce()
if err != nil {
logger.Error("Failed to run once", slog.String("error", err.Error()))
panic(err)
}
fmt.Println("Number of changes:", numberOfChanges)
}
}

View File

@@ -3,7 +3,9 @@ package config
import (
"errors"
"fmt"
"log/slog"
"os"
"strings"
"gopkg.in/yaml.v3"
)
@@ -15,6 +17,7 @@ type Config struct {
NotificationProvider NotificationProviderConfig `yaml:"notification_provider,omitempty"`
Domains []DomainConfig `yaml:"domains"`
CheckInterval string `yaml:"check_interval"`
LogLevel string `yaml:"log_level"`
}
const (
@@ -22,6 +25,18 @@ const (
ScheduledMode = "Scheduled"
)
var LogLevelMap = map[string]slog.Level{
"debug": slog.LevelDebug,
"info": slog.LevelInfo,
"warn": slog.LevelWarn,
"error": slog.LevelError,
}
func isValidLogLevel(level string) bool {
_, ok := LogLevelMap[strings.ToLower(level)]
return ok
}
type DomainConfig struct {
TLD string `yaml:"tld"`
Subdomains []string `yaml:"subdomains"`
@@ -68,5 +83,9 @@ func (c *Config) validate() error {
return errors.New("check interval must be set when mode is 'Scheduled'")
}
if c.LogLevel != "" && !isValidLogLevel(c.LogLevel) {
return fmt.Errorf("log level must be one of 'debug', 'info', 'warn', 'error', but got %s", c.LogLevel)
}
return nil
}

View File

@@ -2,6 +2,7 @@ package realDynDns
import (
"fmt"
"log/slog"
"time"
"realdnydns/pkg/config"
@@ -17,6 +18,7 @@ type ChangeDetector struct {
dnsProvider dnsProvider.DNSProvider
notificationProvider notificationProvider.NotificationProvider
domains []config.DomainConfig
logger *slog.Logger
}
func New(
@@ -24,12 +26,14 @@ func New(
dnsProvider dnsProvider.DNSProvider,
notificationProvider notificationProvider.NotificationProvider,
domains []config.DomainConfig,
logger *slog.Logger,
) ChangeDetector {
return ChangeDetector{
externalIpProvider: externalIpProvider,
dnsProvider: dnsProvider,
notificationProvider: notificationProvider,
domains: domains,
logger: logger,
}
}
@@ -58,8 +62,11 @@ func (c *ChangeDetector) RunOnce() (int, error) {
}
func (c *ChangeDetector) detectAndApplyChanges() (int, error) {
c.logger.Info("Detecting and applying changes")
externalIp, err := c.externalIpProvider.GetExternalIp()
if err != nil {
c.logger.Error("Failed to retrieve external IP", slog.String("error", err.Error()))
return 0, err
}
@@ -67,28 +74,59 @@ func (c *ChangeDetector) detectAndApplyChanges() (int, error) {
for _, domain := range c.domains {
for _, subdomain := range domain.Subdomains {
c.logger.Info("Checking record",
slog.String("tld", domain.TLD),
slog.String("subdomain", subdomain),
)
currentRecord, err := c.dnsProvider.GetRecord(domain.TLD, subdomain)
if err != nil {
c.logger.Error("Failed to retrieve record",
slog.String("error", err.Error()),
slog.String("tld", domain.TLD),
slog.String("subdomain", subdomain),
)
return numberUpdated, err
}
if currentRecord.IP != externalIp.String() {
c.logger.Info("Record has changed",
slog.String("tld", domain.TLD),
slog.String("subdomain", subdomain),
slog.String("current_ip", currentRecord.IP),
slog.String("external_ip", externalIp.String()),
)
err = c.notificationProvider.SendNotification(
fmt.Sprintf("Update %s.%s", subdomain, domain.TLD),
fmt.Sprintf("The IP of %s has changed from %s to %s", domain.TLD, currentRecord.IP, externalIp.String()),
)
if err != nil {
c.logger.Warn("Failed to send notification",
slog.String("error", err.Error()),
)
return numberUpdated, err
}
c.logger.Info("Updating record",
slog.String("tld", domain.TLD),
slog.String("subdomain", subdomain),
slog.String("current_ip", currentRecord.IP),
slog.String("external_ip", externalIp.String()),
)
_, err = c.dnsProvider.UpdateRecord(domain.TLD, subdomain, externalIp, currentRecord.TTL, currentRecord.Prio, currentRecord.Disabled)
numberUpdated++
if err != nil {
c.logger.Error("Failed to update record",
slog.String("error", err.Error()),
slog.String("tld", domain.TLD),
slog.String("subdomain", subdomain),
)
return numberUpdated, err
}
numberUpdated++
}
}
}
c.logger.Info("Run completed", slog.Int("number_of_changes", numberUpdated))
return numberUpdated, nil
}

View File

@@ -1,6 +1,7 @@
package realDynDns
import (
"log/slog"
"net"
"realdnydns/model/common"
"realdnydns/pkg/config"
@@ -72,7 +73,9 @@ func testDetectAndApplyChangesWithChanges() func(t *testing.T) {
"@",
},
},
})
},
slog.Default(),
)
numberUpdated, err := changeDetector.RunOnce()
if err != nil {
@@ -101,7 +104,9 @@ func testDetectAndApplyChangesWithoutChanges() func(t *testing.T) {
"@",
},
},
})
},
slog.Default(),
)
numberUpdated, err := changeDetector.RunOnce()
if err != nil {