Skip to content

Commit 9b34bcb

Browse files
committed
feat: wire structured logger (slog) throughout detection pipeline
1 parent 550f2fb commit 9b34bcb

18 files changed

Lines changed: 129 additions & 54 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Binaries
22
bin/
3+
server
34
*.exe
45
*.dll
56
*.so

cmd/server/main.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"context"
55
"fmt"
6+
"log/slog"
67
"os"
78
"os/signal"
89
"strings"
@@ -99,6 +100,16 @@ func (s *ServerCLI) buildTagConfig() *wiz.TagConfig {
99100
}
100101

101102
func (s *ServerCLI) Run(_ *kong.Context) error {
103+
// Initialize structured logger
104+
logLevel := slog.LevelInfo
105+
if s.Verbose {
106+
logLevel = slog.LevelDebug
107+
}
108+
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
109+
Level: logLevel,
110+
}))
111+
slog.SetDefault(logger)
112+
102113
fmt.Println("Starting Version Guard Detector Service (Open Source)")
103114
fmt.Printf(" Version: %s\n", version)
104115
fmt.Printf(" Temporal Endpoint: %s\n", s.TemporalEndpoint)
@@ -184,17 +195,17 @@ func (s *ServerCLI) Run(_ *kong.Context) error {
184195
wizClient := wiz.NewClient(wizHTTPClient, time.Duration(s.WizCacheTTLHours)*time.Hour)
185196

186197
if s.WizAuroraReportID != "" {
187-
invSources[types.ResourceTypeAurora] = wiz.NewAuroraInventorySource(wizClient, s.WizAuroraReportID).
198+
invSources[types.ResourceTypeAurora] = wiz.NewAuroraInventorySource(wizClient, s.WizAuroraReportID, logger).
188199
WithTagConfig(tagConfig)
189200
fmt.Println("✓ Aurora inventory source configured (Wiz)")
190201
}
191202
if s.WizElastiCacheReportID != "" {
192-
invSources[types.ResourceTypeElastiCache] = wiz.NewElastiCacheInventorySource(wizClient, s.WizElastiCacheReportID).
203+
invSources[types.ResourceTypeElastiCache] = wiz.NewElastiCacheInventorySource(wizClient, s.WizElastiCacheReportID, logger).
193204
WithTagConfig(tagConfig)
194205
fmt.Println("✓ ElastiCache inventory source configured (Wiz)")
195206
}
196207
if s.WizEKSReportID != "" {
197-
invSources[types.ResourceTypeEKS] = wiz.NewEKSInventorySource(wizClient, s.WizEKSReportID).
208+
invSources[types.ResourceTypeEKS] = wiz.NewEKSInventorySource(wizClient, s.WizEKSReportID, logger).
198209
WithTagConfig(tagConfig)
199210
fmt.Println("✓ EKS inventory source configured (Wiz)")
200211
}
@@ -240,15 +251,15 @@ func (s *ServerCLI) Run(_ *kong.Context) error {
240251
cacheTTL := 24 * time.Hour
241252

242253
// Aurora EOL provider (using endoflife.date for PostgreSQL versions)
243-
eolProviders[types.ResourceTypeAurora] = eolendoflife.NewProvider(eolHTTPClient, cacheTTL)
254+
eolProviders[types.ResourceTypeAurora] = eolendoflife.NewProvider(eolHTTPClient, cacheTTL, logger)
244255
fmt.Println("✓ Aurora EOL provider configured (endoflife.date API)")
245256

246257
// EKS EOL provider (using endoflife.date for Kubernetes versions)
247-
eolProviders[types.ResourceTypeEKS] = eolendoflife.NewProvider(eolHTTPClient, cacheTTL)
258+
eolProviders[types.ResourceTypeEKS] = eolendoflife.NewProvider(eolHTTPClient, cacheTTL, logger)
248259
fmt.Println("✓ EKS EOL provider configured (endoflife.date API)")
249260

250261
// ElastiCache EOL provider
251-
eolProviders[types.ResourceTypeElastiCache] = eolendoflife.NewProvider(eolHTTPClient, cacheTTL)
262+
eolProviders[types.ResourceTypeElastiCache] = eolendoflife.NewProvider(eolHTTPClient, cacheTTL, logger)
252263
fmt.Println("✓ ElastiCache EOL provider configured (endoflife.date API)")
253264

254265
// Initialize policy engine
@@ -261,6 +272,7 @@ func (s *ServerCLI) Run(_ *kong.Context) error {
261272
invSources[types.ResourceTypeAurora],
262273
eolProviders[types.ResourceTypeAurora],
263274
policyEngine,
275+
logger,
264276
)
265277
fmt.Println("✓ Aurora detector initialized")
266278
}
@@ -271,6 +283,7 @@ func (s *ServerCLI) Run(_ *kong.Context) error {
271283
invSources[types.ResourceTypeEKS],
272284
eolProviders[types.ResourceTypeEKS],
273285
policyEngine,
286+
logger,
274287
)
275288
fmt.Println("✓ EKS detector initialized")
276289
}

pkg/detector/aurora/detector.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package aurora
33
import (
44
"context"
55
"fmt"
6+
"log/slog"
67
"time"
78

89
"github.com/pkg/errors"
@@ -18,18 +19,24 @@ type Detector struct {
1819
inventory inventory.InventorySource
1920
eol eol.Provider
2021
policy policy.VersionPolicy
22+
logger *slog.Logger
2123
}
2224

2325
// NewDetector creates a new Aurora detector
2426
func NewDetector(
2527
inventory inventory.InventorySource,
2628
eol eol.Provider,
2729
policy policy.VersionPolicy,
30+
logger *slog.Logger,
2831
) *Detector {
32+
if logger == nil {
33+
logger = slog.Default()
34+
}
2935
return &Detector{
3036
inventory: inventory,
3137
eol: eol,
3238
policy: policy,
39+
logger: logger,
3340
}
3441
}
3542

pkg/detector/aurora/detector_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func TestDetector_Detect_EOLVersion(t *testing.T) {
4949
mockInventory,
5050
mockEOL,
5151
policy.NewDefaultPolicy(),
52+
nil, // logger
5253
)
5354

5455
// Run detection
@@ -119,6 +120,7 @@ func TestDetector_Detect_CurrentVersion(t *testing.T) {
119120
mockInventory,
120121
mockEOL,
121122
policy.NewDefaultPolicy(),
123+
nil, // logger
122124
)
123125

124126
// Run detection
@@ -176,6 +178,7 @@ func TestDetector_Detect_ExtendedSupport(t *testing.T) {
176178
mockInventory,
177179
mockEOL,
178180
policy.NewDefaultPolicy(),
181+
nil, // logger
179182
)
180183

181184
// Run detection
@@ -257,6 +260,7 @@ func TestDetector_Detect_MultipleResources(t *testing.T) {
257260
mockInventory,
258261
mockEOL,
259262
policy.NewDefaultPolicy(),
263+
nil, // logger
260264
)
261265

262266
// Run detection
@@ -311,6 +315,7 @@ func TestDetector_Detect_EmptyInventory(t *testing.T) {
311315
mockInventory,
312316
mockEOL,
313317
policy.NewDefaultPolicy(),
318+
nil, // logger
314319
)
315320

316321
// Run detection
@@ -327,7 +332,7 @@ func TestDetector_Detect_EmptyInventory(t *testing.T) {
327332
}
328333

329334
func TestDetector_Name(t *testing.T) {
330-
detector := NewDetector(nil, nil, nil)
335+
detector := NewDetector(nil, nil, nil, nil)
331336

332337
name := detector.Name()
333338
expected := "aurora-detector"
@@ -338,7 +343,7 @@ func TestDetector_Name(t *testing.T) {
338343
}
339344

340345
func TestDetector_ResourceType(t *testing.T) {
341-
detector := NewDetector(nil, nil, nil)
346+
detector := NewDetector(nil, nil, nil, nil)
342347

343348
resourceType := detector.ResourceType()
344349

pkg/detector/aurora/integration_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ func TestFullFlow_MultipleResourcesWithDifferentStatuses(t *testing.T) {
154154
mockInventory,
155155
mockEOL,
156156
policy.NewDefaultPolicy(),
157+
nil, // logger
157158
)
158159

159160
// Execute: Run the full detection flow
@@ -368,7 +369,7 @@ func TestFullFlow_SummaryStatistics(t *testing.T) {
368369
},
369370
}
370371

371-
detector := NewDetector(mockInventory, mockEOL, policy.NewDefaultPolicy())
372+
detector := NewDetector(mockInventory, mockEOL, policy.NewDefaultPolicy(), nil)
372373
findings, err := detector.Detect(context.Background())
373374

374375
if err != nil {

pkg/detector/eks/detector.go

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

33
import (
44
"context"
5-
"log"
5+
"log/slog"
66
"time"
77

88
"github.com/pkg/errors"
@@ -18,18 +18,24 @@ type Detector struct {
1818
inventory inventory.InventorySource
1919
eol eol.Provider
2020
policy policy.VersionPolicy
21+
logger *slog.Logger
2122
}
2223

2324
// NewDetector creates a new EKS detector
2425
func NewDetector(
2526
inventory inventory.InventorySource,
2627
eol eol.Provider,
2728
policy policy.VersionPolicy,
29+
logger *slog.Logger,
2830
) *Detector {
31+
if logger == nil {
32+
logger = slog.Default()
33+
}
2934
return &Detector{
3035
inventory: inventory,
3136
eol: eol,
3237
policy: policy,
38+
logger: logger,
3339
}
3440
}
3541

@@ -62,8 +68,9 @@ func (d *Detector) Detect(ctx context.Context) ([]*types.Finding, error) {
6268
finding, err := d.detectResource(ctx, resource)
6369
if err != nil {
6470
// Log error but continue with other resources
65-
// TODO: wire through proper structured logger (e.g., *slog.Logger)
66-
log.Printf("WARN: failed to detect drift for %s: %v", resource.ID, err)
71+
d.logger.WarnContext(ctx, "failed to detect drift for resource",
72+
"resource_id", resource.ID,
73+
"error", err)
6774
continue
6875
}
6976

pkg/detector/eks/detector_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func TestDetector_Detect_EOLVersion(t *testing.T) {
4949
mockInventory,
5050
mockEOL,
5151
policy.NewDefaultPolicy(),
52+
nil, // logger
5253
)
5354

5455
// Run detection
@@ -119,6 +120,7 @@ func TestDetector_Detect_CurrentVersion(t *testing.T) {
119120
mockInventory,
120121
mockEOL,
121122
policy.NewDefaultPolicy(),
123+
nil, // logger
122124
)
123125

124126
// Run detection
@@ -176,6 +178,7 @@ func TestDetector_Detect_ExtendedSupport(t *testing.T) {
176178
mockInventory,
177179
mockEOL,
178180
policy.NewDefaultPolicy(),
181+
nil, // logger
179182
)
180183

181184
// Run detection
@@ -257,6 +260,7 @@ func TestDetector_Detect_MultipleResources(t *testing.T) {
257260
mockInventory,
258261
mockEOL,
259262
policy.NewDefaultPolicy(),
263+
nil, // logger
260264
)
261265

262266
// Run detection
@@ -311,6 +315,7 @@ func TestDetector_Detect_EmptyInventory(t *testing.T) {
311315
mockInventory,
312316
mockEOL,
313317
policy.NewDefaultPolicy(),
318+
nil, // logger
314319
)
315320

316321
// Run detection
@@ -327,7 +332,7 @@ func TestDetector_Detect_EmptyInventory(t *testing.T) {
327332
}
328333

329334
func TestDetector_Name(t *testing.T) {
330-
detector := NewDetector(nil, nil, nil)
335+
detector := NewDetector(nil, nil, nil, nil)
331336

332337
name := detector.Name()
333338
expected := "eks-detector"
@@ -338,7 +343,7 @@ func TestDetector_Name(t *testing.T) {
338343
}
339344

340345
func TestDetector_ResourceType(t *testing.T) {
341-
detector := NewDetector(nil, nil, nil)
346+
detector := NewDetector(nil, nil, nil, nil)
342347

343348
resourceType := detector.ResourceType()
344349

pkg/detector/eks/integration_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ func TestFullFlow_MultipleResourcesWithDifferentStatuses(t *testing.T) {
146146
mockInventory,
147147
mockEOL,
148148
policy.NewDefaultPolicy(),
149+
nil, // logger
149150
)
150151

151152
// Execute: Run detection

pkg/eol/endoflife/integration_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func TestProviderRealAPIIntegration(t *testing.T) {
6666

6767
// Create provider with real client
6868
client := NewRealHTTPClient()
69-
provider := NewProvider(client, 1*time.Hour)
69+
provider := NewProvider(client, 1*time.Hour, nil)
7070

7171
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
7272
defer cancel()
@@ -124,7 +124,7 @@ func TestCachingRealAPI(t *testing.T) {
124124
}
125125

126126
client := NewRealHTTPClient()
127-
provider := NewProvider(client, 1*time.Hour)
127+
provider := NewProvider(client, 1*time.Hour, nil)
128128

129129
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
130130
defer cancel()

pkg/eol/endoflife/provider.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package endoflife
33
import (
44
"context"
55
"fmt"
6+
"log/slog"
67
"strings"
78
"sync"
89
"time"
@@ -62,6 +63,7 @@ type Provider struct {
6263
client Client
6364
cacheTTL time.Duration
6465
group singleflight.Group // Prevents thundering herd on API calls
66+
logger *slog.Logger
6567
}
6668

6769
//nolint:govet // field alignment sacrificed for readability
@@ -71,15 +73,19 @@ type cachedVersions struct {
7173
}
7274

7375
// NewProvider creates a new endoflife.date EOL provider
74-
func NewProvider(client Client, cacheTTL time.Duration) *Provider {
76+
func NewProvider(client Client, cacheTTL time.Duration, logger *slog.Logger) *Provider {
7577
if cacheTTL == 0 {
7678
cacheTTL = 24 * time.Hour // Default: cache for 24 hours
7779
}
80+
if logger == nil {
81+
logger = slog.Default()
82+
}
7883

7984
return &Provider{
8085
client: client,
8186
cacheTTL: cacheTTL,
8287
cache: make(map[string]*cachedVersions),
88+
logger: logger,
8389
}
8490
}
8591

@@ -191,7 +197,10 @@ func (p *Provider) ListAllVersions(ctx context.Context, engine string) ([]*types
191197
lifecycle, err := p.convertCycle(engine, product, cycle)
192198
if err != nil {
193199
// Skip cycles we can't parse, but log a warning
194-
// TODO: wire through proper structured logger
200+
p.logger.WarnContext(ctx, "failed to convert EOL cycle, skipping",
201+
"engine", engine,
202+
"product", product,
203+
"error", err)
195204
continue
196205
}
197206
versions = append(versions, lifecycle)

0 commit comments

Comments
 (0)