Skip to content

Commit 9bfc849

Browse files
authored
chore: structured logging (#28)
1 parent 235e363 commit 9bfc849

13 files changed

Lines changed: 216 additions & 68 deletions

File tree

.github/workflows/reusable-unittests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- name: Set up Go
1414
uses: actions/setup-go@v5
1515
with:
16-
go-version: '1.24.5'
16+
go-version: "1.25.4"
1717

1818
- name: Run tests with coverage
1919
run: go test -coverprofile=coverage.out ./...

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.24.5-alpine AS builder
1+
FROM golang:1.25.4-alpine AS builder
22

33
LABEL org.opencontainers.image.source=https://github.com/DeepSpace2/plugnpin
44

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ See [Per Container Configuration ➔ Pi-Hole](#targetDomainLabel).
5252

5353
| Variable {: style="width:35%" } | Description | Default {: style="width:10%" } |
5454
|---|---|---|
55+
| `DEBUG` | Set to `true` to enable DEBUG level logs | `false` |
5556
| `DOCKER_HOST` | The URL of a docker socket proxy. If set, you don't need to mount the docker socket as a volume. Querying containers must be allowed (typically done by setting the `CONTAINERS` environment variable to `1`). | *None* |
5657
| <div id="piHoleDisabledEnvVar"><a name="piholeDisabledEnvVar"></a>`PIHOLE_DISABLED`</div> | Set to `true` to disable Pi-Hole functionality | `false` |
5758
| `RUN_INTERVAL` | The interval at which to scan for new containers, in Go's [`time.ParseDuration`](<https://go.dev/pkg/time/#ParseDuration>){: target="_blank" } format. Set to `0` to run once and exit. | `1h` |

go.mod

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,62 @@
11
module github.com/deepspace2/plugnpin
22

3-
go 1.23.11
3+
go 1.25.4
44

55
require (
66
github.com/caarlos0/env/v11 v11.3.1
77
github.com/docker/docker v28.3.2+incompatible
8+
github.com/docker/go-connections v0.5.0
89
github.com/docker/go-sdk/client v0.1.0-alpha009
910
github.com/joho/godotenv v1.5.1
11+
github.com/moby/moby/api v1.52.0
1012
github.com/spf13/pflag v1.0.7
1113
github.com/stretchr/testify v1.10.0
1214
)
1315

1416
require (
1517
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
1618
github.com/Microsoft/go-winio v0.6.2 // indirect
19+
github.com/beorn7/perks v1.0.1 // indirect
1720
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
1821
github.com/containerd/errdefs v1.0.0 // indirect
1922
github.com/containerd/errdefs/pkg v0.3.0 // indirect
23+
github.com/containerd/fifo v1.1.0 // indirect
24+
github.com/containerd/log v0.1.0 // indirect
2025
github.com/davecgh/go-spew v1.1.1 // indirect
2126
github.com/distribution/reference v0.6.0 // indirect
22-
github.com/docker/go-connections v0.5.0 // indirect
27+
github.com/docker/go-metrics v0.0.1 // indirect
2328
github.com/docker/go-sdk/config v0.1.0-alpha009 // indirect
2429
github.com/docker/go-sdk/context v0.1.0-alpha009 // indirect
2530
github.com/docker/go-units v0.5.0 // indirect
2631
github.com/felixge/httpsnoop v1.0.4 // indirect
2732
github.com/go-logr/logr v1.4.3 // indirect
2833
github.com/go-logr/stdr v1.2.2 // indirect
2934
github.com/gogo/protobuf v1.3.2 // indirect
35+
github.com/golang/protobuf v1.5.0 // indirect
36+
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
3037
github.com/moby/docker-image-spec v1.3.1 // indirect
38+
github.com/moby/sys/atomicwriter v0.1.0 // indirect
3139
github.com/moby/sys/sequential v0.6.0 // indirect
40+
github.com/moby/sys/userns v0.1.0 // indirect
41+
github.com/moby/term v0.5.2 // indirect
42+
github.com/morikuni/aec v1.0.0 // indirect
3243
github.com/opencontainers/go-digest v1.0.0 // indirect
3344
github.com/opencontainers/image-spec v1.1.1 // indirect
3445
github.com/pkg/errors v0.9.1 // indirect
3546
github.com/pmezard/go-difflib v1.0.0 // indirect
47+
github.com/prometheus/client_golang v1.1.0 // indirect
48+
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
49+
github.com/prometheus/common v0.6.0 // indirect
50+
github.com/prometheus/procfs v0.0.3 // indirect
51+
github.com/sirupsen/logrus v1.9.3 // indirect
3652
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
3753
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
3854
go.opentelemetry.io/otel v1.37.0 // indirect
3955
go.opentelemetry.io/otel/metric v1.37.0 // indirect
4056
go.opentelemetry.io/otel/trace v1.37.0 // indirect
4157
golang.org/x/net v0.37.0 // indirect
4258
golang.org/x/sys v0.33.0 // indirect
59+
golang.org/x/time v0.11.0 // indirect
4360
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
4461
google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect
4562
google.golang.org/protobuf v1.36.7 // indirect

go.sum

Lines changed: 76 additions & 0 deletions
Large diffs are not rendered by default.

main.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package main
22

33
import (
44
"context"
5-
"log"
5+
"fmt"
66
"os"
77
"os/signal"
88
"sync"
@@ -13,31 +13,41 @@ import (
1313
"github.com/deepspace2/plugnpin/pkg/clients/npm"
1414
"github.com/deepspace2/plugnpin/pkg/clients/pihole"
1515
"github.com/deepspace2/plugnpin/pkg/config"
16+
"github.com/deepspace2/plugnpin/pkg/logging"
1617
"github.com/deepspace2/plugnpin/pkg/processor"
1718
)
1819

20+
var log = logging.GetLogger()
21+
1922
func shutdown(cancelCtx context.CancelFunc, wg *sync.WaitGroup) {
2023
shutdownChan := make(chan os.Signal, 1)
2124
signal.Notify(shutdownChan, syscall.SIGINT, syscall.SIGTERM)
2225

2326
<-shutdownChan
2427

25-
log.Println("Shutdown signal received, exiting gracefully.")
28+
log.Info("Shutdown signal received, exiting gracefully.")
2629
cancelCtx()
2730
wg.Wait()
28-
log.Println("Shutdown complete.")
31+
log.Info("Shutdown complete.")
2932
}
3033

3134
func main() {
3235
cliFlags := cli.ParseFlags()
3336

3437
conf, err := config.GetEnvVars()
3538
if err != nil {
36-
log.Fatal(err)
39+
log.Error("Failed to parse environment variables", "error", err)
40+
os.Exit(1)
41+
}
42+
43+
if conf.Debug {
44+
logging.SetLevel(logging.DEBUG)
45+
} else {
46+
logging.SetLevel(logging.INFO)
3747
}
3848

3949
if conf.RunInterval > 0 {
40-
log.Printf("Will run every %v", conf.RunInterval)
50+
log.Info(fmt.Sprintf("Will run every %v", conf.RunInterval))
4151
}
4252

4353
var piholeClient *pihole.Client
@@ -48,27 +58,30 @@ func main() {
4858
piholeClient = pihole.NewClient(conf.PiholeHost)
4959
err = piholeClient.Login(conf.PiholePassword)
5060
if err != nil {
51-
log.Fatalf("ERROR failed to login to Pi-Hole: %v", err)
61+
log.Error("Failed to login to Pi-Hole", "error", err)
62+
os.Exit(1)
5263
}
5364
}
5465

5566
npmClient = npm.NewClient(conf.NpmHost, conf.NpmUsername, conf.NpmPassword)
5667
err = npmClient.Login()
5768
if err != nil {
58-
log.Fatalf("ERROR failed to login to Nginx Proxy Manager: %v", err)
69+
log.Error("Failed to login to Nginx Proxy Manager", "error", err)
70+
os.Exit(1)
5971
}
6072
}
6173

6274
dockerClient, err := docker.NewClient()
6375
if err != nil {
64-
log.Fatalf("ERROR creating docker client: %v", err)
76+
log.Error("Failed to create docker client", "error", err)
77+
os.Exit(1)
6578
}
6679
defer dockerClient.Close()
6780

6881
proc := processor.New(dockerClient, piholeClient, npmClient, cliFlags.DryRun)
6982

7083
if conf.RunInterval == 0 {
71-
log.Println("RUN_INTERVAL is 0, will run once")
84+
log.Info("RUN_INTERVAL is 0, will run once")
7285
proc.RunOnce()
7386
return
7487
}

pkg/clients/docker/docker.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@ package docker
33
import (
44
"context"
55
"fmt"
6-
"log"
76
"slices"
87
"strconv"
98
"strings"
109

11-
"github.com/deepspace2/plugnpin/pkg/clients/npm"
12-
"github.com/deepspace2/plugnpin/pkg/clients/pihole"
13-
"github.com/deepspace2/plugnpin/pkg/errors"
1410
"github.com/docker/docker/api/types/container"
1511
"github.com/docker/docker/api/types/filters"
1612
dockerSdk "github.com/docker/go-sdk/client"
13+
14+
"github.com/deepspace2/plugnpin/pkg/clients/npm"
15+
"github.com/deepspace2/plugnpin/pkg/clients/pihole"
16+
"github.com/deepspace2/plugnpin/pkg/errors"
17+
"github.com/deepspace2/plugnpin/pkg/logging"
1718
)
1819

20+
var log = logging.GetLogger()
21+
1922
const (
2023
ipLabel = "plugNPiN.ip"
2124
urlLabel = "plugNPiN.url"
@@ -49,7 +52,7 @@ func (d *Client) GetRelevantContainers() ([]container.Summary, error) {
4952
f.Add("label", label)
5053
}
5154

52-
log.Printf("Getting containers with labels: %v", strings.Join(labels, ", "))
55+
log.Info(fmt.Sprintf("Getting containers with labels: %v", strings.Join(labels, ", ")))
5356

5457
return d.ContainerList(
5558
context.Background(),

pkg/clients/docker/events.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package docker
22

33
import (
44
"context"
5-
"log"
65

76
"github.com/docker/docker/api/types/events"
87
"github.com/docker/docker/api/types/filters"
@@ -22,7 +21,7 @@ func Listen(ctx context.Context, handler func(events.Message)) error {
2221
f.Add("event", ContainerEvent.Stop.String())
2322
f.Add("event", ContainerEvent.Kill.String())
2423

25-
log.Println("Listening for Docker events...")
24+
log.Info("Listening for Docker events...")
2625

2726
messages, errs := c.Events(ctx, events.ListOptions{
2827
Filters: f,
@@ -31,13 +30,13 @@ func Listen(ctx context.Context, handler func(events.Message)) error {
3130
for {
3231
select {
3332
case <-ctx.Done():
34-
log.Printf("Stopping stream of Docker events")
33+
log.Info("Stopping stream of Docker events")
3534
return ctx.Err()
3635
case event := <-messages:
3736
handler(event)
3837
case err := <-errs:
3938
if err != nil {
40-
log.Printf("Error receiving event: %v", err)
39+
log.Error("Failed to receive event", "error", err)
4140
}
4241
return err
4342
}

pkg/clients/npm/npm.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7-
"log"
87
"net/http"
98
"net/url"
109

1110
"github.com/deepspace2/plugnpin/pkg/clients"
11+
"github.com/deepspace2/plugnpin/pkg/logging"
1212
)
1313

14+
var log = logging.GetLogger()
15+
1416
type Client struct {
1517
http.Client
1618
baseURL string
@@ -86,7 +88,7 @@ func (n *Client) getProxyHosts() (map[string]int, error) {
8688
}
8789

8890
func (n *Client) refreshToken() error {
89-
log.Println("Refreshing Nginx Proxy Manager token")
91+
log.Info("Refreshing Nginx Proxy Manager token")
9092
return n.Login()
9193
}
9294

pkg/clients/pihole/pihole.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ package pihole
22

33
import (
44
"encoding/json"
5+
"errors"
56
"fmt"
6-
"log"
77
"net/http"
8+
"os"
89
"strings"
910

1011
"github.com/deepspace2/plugnpin/pkg/clients"
12+
"github.com/deepspace2/plugnpin/pkg/logging"
1113
)
1214

15+
var log = logging.GetLogger()
16+
1317
type Client struct {
1418
http.Client
1519
baseURL string
@@ -39,7 +43,7 @@ func (p *Client) Login(password string) error {
3943
json.Unmarshal([]byte(loginResponseString), &resp)
4044

4145
if statusCode >= 400 || resp.Session.Sid == "" {
42-
return fmt.Errorf(resp.Session.Message)
46+
return errors.New(resp.Session.Message)
4347
}
4448

4549
p.sid = resp.Session.Sid
@@ -63,8 +67,8 @@ func dnsRecordToRaw(domain DomainName, ip IP) string {
6367

6468
func (p *Client) getDnsRecords() (DnsRecords, error) {
6569
if p.sid == "" {
66-
// TODO:
67-
log.Fatal("no SID")
70+
log.Error("Missing Pi-Hole session ID")
71+
os.Exit(1)
6872
}
6973
headers["X-FTL-SID"] = p.sid
7074
configResponseString, _, err := clients.Get(&p.Client, p.baseURL+"/config", headers)
@@ -113,8 +117,8 @@ func (p *Client) AddDnsRecord(domain, ip string) error {
113117
}
114118

115119
if p.sid == "" {
116-
// TODO:
117-
log.Fatal("no SID")
120+
log.Error("Missing Pi-Hole session ID")
121+
os.Exit(1)
118122
}
119123
headers["X-FTL-SID"] = p.sid
120124
resp, statusCode, err := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
@@ -158,8 +162,8 @@ func (p *Client) DeleteDnsRecord(domain string) error {
158162
}
159163

160164
if p.sid == "" {
161-
// TODO:
162-
log.Fatal("no SID")
165+
log.Error("Missing Pi-Hole session ID")
166+
os.Exit(1)
163167
}
164168
headers["X-FTL-SID"] = p.sid
165169
resp, statusCode, err := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
@@ -192,8 +196,8 @@ func cNameRecordToRaw(domain DomainName, target Target) string {
192196

193197
func (p *Client) getCNameRecords() (CNameRecords, error) {
194198
if p.sid == "" {
195-
// TODO:
196-
log.Fatal("no SID")
199+
log.Error("Missing Pi-Hole session ID")
200+
os.Exit(1)
197201
}
198202
headers["X-FTL-SID"] = p.sid
199203
configResponseString, _, err := clients.Get(&p.Client, p.baseURL+"/config", headers)
@@ -242,8 +246,8 @@ func (p *Client) AddCNameRecord(domain, target string) error {
242246
}
243247

244248
if p.sid == "" {
245-
// TODO:
246-
log.Fatal("no SID")
249+
log.Error("Missing Pi-Hole session ID")
250+
os.Exit(1)
247251
}
248252
headers["X-FTL-SID"] = p.sid
249253
resp, statusCode, err := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
@@ -287,8 +291,8 @@ func (p *Client) DeleteCNameRecord(domain, target string) error {
287291
}
288292

289293
if p.sid == "" {
290-
// TODO:
291-
log.Fatal("no SID")
294+
log.Error("Missing Pi-Hole session ID")
295+
os.Exit(1)
292296
}
293297
headers["X-FTL-SID"] = p.sid
294298
resp, statusCode, err := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))

0 commit comments

Comments
 (0)