Skip to content

Commit 960e43d

Browse files
committed
feat: add verbose logging option to controller-gen
1 parent 1ad88b0 commit 960e43d

File tree

4 files changed

+117
-1
lines changed

4 files changed

+117
-1
lines changed

pkg/crd/gen.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"go/ast"
2222
"go/types"
23+
"log/slog"
2324
"sort"
2425
"strings"
2526

@@ -124,6 +125,12 @@ func transformPreserveUnknownFields(value bool) func(map[string]interface{}) err
124125
}
125126

126127
func (g Generator) Generate(ctx *genall.GenerationContext) error {
128+
// Extract logger and use a discard logger if nil to avoid repeated nil checks
129+
logger := ctx.Logger
130+
if logger == nil {
131+
logger = slog.New(slog.DiscardHandler)
132+
}
133+
127134
parser := &Parser{
128135
Collector: ctx.Collector,
129136
Checker: ctx.Checker,
@@ -134,30 +141,44 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
134141
GenerateEmbeddedObjectMeta: g.GenerateEmbeddedObjectMeta != nil && *g.GenerateEmbeddedObjectMeta,
135142
}
136143

144+
logger.Debug("starting CRD generation", "ignoreUnexported", parser.IgnoreUnexportedFields, "allowDangerous", parser.AllowDangerousTypes)
145+
137146
AddKnownTypes(parser)
138147
for _, root := range ctx.Roots {
139148
parser.NeedPackage(root)
149+
logger.Debug("processing package", "package", root.PkgPath)
140150
}
141151

142152
metav1Pkg := FindMetav1(ctx.Roots)
143153
if metav1Pkg == nil {
144154
// no objects in the roots, since nothing imported metav1
155+
logger.Debug("no metav1 package found in roots, no CRDs to generate")
145156
return nil
146157
}
147158

159+
logger.Debug("found metav1 package", "package", metav1Pkg.PkgPath)
160+
148161
// TODO: allow selecting a specific object
149162
kubeKinds := FindKubeKinds(parser, metav1Pkg)
150163
if len(kubeKinds) == 0 {
151164
// no objects in the roots
165+
logger.Debug("no Kubernetes kinds found in packages")
152166
return nil
153167
}
154168

169+
logger.Info("found Kubernetes kinds for CRD generation", "count", len(kubeKinds))
170+
for _, kind := range kubeKinds {
171+
logger.Debug("processing Kubernetes kind", "group", kind.Group, "kind", kind.Kind)
172+
}
173+
155174
crdVersions := g.CRDVersions
156175

157176
if len(crdVersions) == 0 {
158177
crdVersions = []string{defaultVersion}
159178
}
160179

180+
logger.Debug("using CRD versions", "versions", crdVersions)
181+
161182
var headerText string
162183

163184
if g.HeaderFile != "" {
@@ -166,6 +187,7 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
166187
return err
167188
}
168189
headerText = string(headerBytes)
190+
logger.Debug("loaded header file", "file", g.HeaderFile, "size", len(headerBytes))
169191
}
170192
headerText = strings.ReplaceAll(headerText, " YEAR", " "+g.Year)
171193

@@ -178,6 +200,8 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
178200
}
179201

180202
for _, groupKind := range kubeKinds {
203+
logger.Debug("generating CRD", "group", groupKind.Group, "kind", groupKind.Kind)
204+
181205
parser.NeedCRDFor(groupKind, g.MaxDescLen)
182206
crdRaw := parser.CustomResourceDefinitions[groupKind]
183207
addAttribution(&crdRaw)
@@ -202,9 +226,14 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
202226
} else {
203227
fileName = fmt.Sprintf("%s_%s.%s.yaml", crdRaw.Spec.Group, crdRaw.Spec.Names.Plural, crdVersions[i])
204228
}
229+
230+
logger.Debug("writing CRD file", "filename", fileName, "group", groupKind.Group, "kind", groupKind.Kind)
231+
205232
if err := ctx.WriteYAML(fileName, headerText, []interface{}{crd}, yamlOpts...); err != nil {
206233
return err
207234
}
235+
236+
logger.Info("generated CRD", "filename", fileName, "group", groupKind.Group, "kind", groupKind.Kind)
208237
}
209238
}
210239

pkg/genall/genall.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"encoding/json"
2121
"fmt"
2222
"io"
23+
"log/slog"
2324
"os"
2425

2526
"golang.org/x/tools/go/packages"
@@ -101,6 +102,9 @@ type Runtime struct {
101102
OutputRules OutputRules
102103
// ErrorWriter defines where to write error messages.
103104
ErrorWriter io.Writer
105+
// LogLevel sets the logging level for generator operations.
106+
// Defaults to slog.LevelInfo if not specified.
107+
LogLevel slog.Level
104108
}
105109

106110
// GenerationContext defines the common information needed for each Generator
@@ -117,6 +121,8 @@ type GenerationContext struct {
117121
// InputRule describes how to load associated boilerplate artifacts.
118122
// It should *not* be used to load source files.
119123
InputRule
124+
// Logger is the logger for verbose output. If nil, logging is disabled.
125+
Logger *slog.Logger
120126
}
121127

122128
// WriteYAMLOptions implements the Options Pattern for WriteYAML.
@@ -261,17 +267,37 @@ func (r *Runtime) Run() bool {
261267
return true
262268
}
263269

270+
// Set up logging based on log level setting
271+
var logger *slog.Logger
272+
273+
// Use the specified log level, defaulting to Info if not set
274+
logLevel := r.LogLevel
275+
if logLevel == 0 {
276+
logLevel = slog.LevelInfo
277+
}
278+
279+
logger = slog.New(slog.NewTextHandler(r.ErrorWriter, &slog.HandlerOptions{
280+
Level: logLevel,
281+
}))
282+
283+
if logLevel <= slog.LevelDebug {
284+
logger.Info("debug logging enabled")
285+
}
286+
264287
hadErrs := false
265288
for _, gen := range r.Generators {
266289
ctx := r.GenerationContext // make a shallow copy
267290
ctx.OutputRule = r.OutputRules.ForGenerator(gen)
291+
ctx.Logger = logger
268292

269293
// don't pass a typechecker to generators that don't provide a filter
270294
// to avoid accidents
271295
if _, needsChecking := (*gen).(NeedsTypeChecking); !needsChecking {
272296
ctx.Checker = nil
273297
}
274298

299+
logger.Debug("running generator", "generator", fmt.Sprintf("%T", *gen))
300+
275301
if err := (*gen).Generate(&ctx); err != nil {
276302
fmt.Fprintln(r.ErrorWriter, err)
277303
hadErrs = true

pkg/genall/options.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package genall
1818

1919
import (
2020
"fmt"
21+
"log/slog"
2122
"strings"
2223

2324
"golang.org/x/tools/go/packages"
@@ -26,6 +27,7 @@ import (
2627

2728
var (
2829
InputPathsMarker = markers.Must(markers.MakeDefinition("paths", markers.DescribesPackage, InputPaths(nil)))
30+
LogLevelMarker = markers.Must(markers.MakeDefinition("loglevel", markers.DescribesPackage, LogLevel("")))
2931
)
3032

3133
// +controllertools:marker:generateHelp:category=""
@@ -35,16 +37,52 @@ var (
3537
// Multiple paths can be specified using "{path1, path2, path3}".
3638
type InputPaths []string
3739

40+
// +controllertools:marker:generateHelp:category=""
41+
42+
// LogLevel sets the logging level for generator operations.
43+
// Valid values are "debug", "info", "warn", "error".
44+
// Defaults to "info" if not specified.
45+
type LogLevel string
46+
47+
const (
48+
LogLevelDebug LogLevel = "debug"
49+
LogLevelInfo LogLevel = "info"
50+
LogLevelWarn LogLevel = "warn"
51+
LogLevelError LogLevel = "error"
52+
)
53+
54+
// ToSlogLevel converts the LogLevel to slog.Level
55+
func (l LogLevel) ToSlogLevel() slog.Level {
56+
switch l {
57+
case LogLevelDebug:
58+
return slog.LevelDebug
59+
case LogLevelInfo:
60+
return slog.LevelInfo
61+
case LogLevelWarn:
62+
return slog.LevelWarn
63+
case LogLevelError:
64+
return slog.LevelError
65+
default:
66+
return slog.LevelInfo // default fallback
67+
}
68+
}
69+
3870
// RegisterOptionsMarkers registers "mandatory" options markers for FromOptions into the given registry.
39-
// At this point, that's just InputPaths.
71+
// At this point, that's just InputPaths and LogLevel.
4072
func RegisterOptionsMarkers(into *markers.Registry) error {
4173
if err := into.Register(InputPathsMarker); err != nil {
4274
return err
4375
}
76+
if err := into.Register(LogLevelMarker); err != nil {
77+
return err
78+
}
4479
// NB(directxman12): we make this optional so we don't have a bootstrap problem with helpgen
4580
if helpGiver, hasHelp := ((interface{})(InputPaths(nil))).(HasHelp); hasHelp {
4681
into.AddHelp(InputPathsMarker, helpGiver.Help())
4782
}
83+
if helpGiver, hasHelp := ((interface{})(LogLevel(""))).(HasHelp); hasHelp {
84+
into.AddHelp(LogLevelMarker, helpGiver.Help())
85+
}
4886
return nil
4987
}
5088

@@ -90,6 +128,13 @@ func FromOptionsWithConfig(cfg *packages.Config, optionsRegistry *markers.Regist
90128
return nil, err
91129
}
92130

131+
// Set log level from the parsed options
132+
if protoRt.LogLevel != "" {
133+
genRuntime.LogLevel = protoRt.LogLevel.ToSlogLevel()
134+
} else {
135+
genRuntime.LogLevel = slog.LevelInfo // default
136+
}
137+
93138
// attempt to figure out what the user wants without a lot of verbose specificity:
94139
// if the user specifies a default rule, assume that they probably want to fall back
95140
// to that. Otherwise, assume that they just wanted to customize one option from the
@@ -117,6 +162,7 @@ func protoFromOptions(optionsRegistry *markers.Registry, options []string) (prot
117162
ByGenerator: make(map[*Generator]OutputRule),
118163
}
119164
var paths []string
165+
var logLevel LogLevel
120166

121167
// collect the generators first, so that we can key the output on the actual
122168
// generator, which matters if there's settings in the gen object and it's not a pointer.
@@ -156,6 +202,8 @@ func protoFromOptions(optionsRegistry *markers.Registry, options []string) (prot
156202
continue
157203
case InputPaths:
158204
paths = append(paths, val...)
205+
case LogLevel:
206+
logLevel = val
159207
default:
160208
return protoRuntime{}, fmt.Errorf("unknown option marker %q", defn.Name)
161209
}
@@ -176,6 +224,7 @@ func protoFromOptions(optionsRegistry *markers.Registry, options []string) (prot
176224
Generators: gens,
177225
OutputRules: rules,
178226
GeneratorsByName: gensByName,
227+
LogLevel: logLevel,
179228
}, nil
180229
}
181230

@@ -186,6 +235,7 @@ type protoRuntime struct {
186235
Generators Generators
187236
OutputRules OutputRules
188237
GeneratorsByName map[string]*Generator
238+
LogLevel LogLevel
189239
}
190240

191241
// splitOutputRuleOption splits a marker name of "output:rule:gen" or "output:rule"

pkg/genall/zz_generated.markerhelp.go

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)