Skip to content

Conversation

@onelapahead
Copy link
Contributor

Kubernetes controllers love logr as an interface for logging.

FireFly loves logrus (log levels are so much better than -vvvvv).

🍅 🥔

In our Kubernetes operators - we'd prefer to provide the controller-runtime / K8s libraries with an ff-common like logger, so that we can then use log.L(ctx) semantics w/ proper log levels to create a smoother logging experience for our developers vs our users.

These wrapper implementations would help achieve that.

…patibility

Signed-off-by: hfuss <hayden.fuss@kaleido.io>
@onelapahead onelapahead requested a review from a team as a code owner November 19, 2025 20:37
@onelapahead
Copy link
Contributor Author

onelapahead commented Nov 19, 2025

In a controller-runtime / kubebuilder base operator you can then easily do:

func main() {
    // ... flag parsing ...
    
    // Initialize ff-common logging instead of zapr
    ctx := context.Background()
    log.SetLevel(opts.LogLevel) // or read from flag
    
    // Set the controller-runtime logger to use our ff-common adapter
    ctrl.SetLogger(log.NewLogr(ctx, "firefly-operator"))
    
    // ... rest of setup ...
}

and use log.L(ctx) in the actual reconcilers - all w/ a consistent log output this way.

…for logging

Signed-off-by: hfuss <hayden.fuss@kaleido.io>
…lementation

Signed-off-by: hfuss <hayden.fuss@kaleido.io>
Signed-off-by: hfuss <hayden.fuss@kaleido.io>
Copy link
Contributor

@EnriqueL8 EnriqueL8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @onelapahead - this is awesome 🙇🏼

Left a few comments/things I don't understand

}

// WithFields adds the specified, structured fields to the logger in the context
func WithFields(ctx context.Context, fields map[string]string) context.Context {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep the interface consistent with above and instead of a map have keyValuePairs as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well so this one is meant to feel more logrus native, but did struggle with the names to indicate that...

pkg/log/logr.go Outdated

// Init initializes the sink
func (l *Sink) Init(_ logr.RuntimeInfo) {
// Optional: store callDepth if needed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah don't think we need to implement this for now, will leave a different comment

pkg/log/logr.go Outdated
func (l *Sink) Enabled(level int) bool {
// Map logr V-levels to ff-common levels
// logr: V(0)=info, V(1)=debug, V(2+)=trace
switch level {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for negative level?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't be possible for logr but it is is int so will be defensive

pkg/log/logr.go Outdated
}

// Info logs an info message with the given keys and values. keysAndValues is not efficiently implemented, use WithValues instead
func (l *Sink) Info(level int, msg string, keysAndValues ...interface{}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confused how with Sink we still allow passing level int ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see Debug etc... so maybe this function is actually called Logf ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aaah is this implementing just the interface ?? that makes sense then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this is all the logr interface - which doesn't have log levels just ints lol - its quite terse.

This is just so k8s libs log through our logging, so we can use our logging in k8s controllers w/o having two different loggers going

@@ -0,0 +1,37 @@
// Copyright © 2022 - 2025 Kaleido, Inc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright © 2022 - 2025 Kaleido, Inc.
// Copyright © 2025 Kaleido, Inc.

pkg/log/logr.go Outdated
@@ -0,0 +1,114 @@
// Copyright © 2022 - 2025 Kaleido, Inc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright © 2022 - 2025 Kaleido, Inc.
// Copyright © 2025 Kaleido, Inc.

pkg/log/logr.go Outdated
func (l *Sink) buildContext(keysAndValues []interface{}) context.Context {
fields := make(map[string]string)
for i := 0; i < len(keysAndValues); i += 2 {
fields[keysAndValues[i].(string)] = keysAndValues[i+1].(string)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to be a bit safer with this cast keysAndValues[i].(string) and throw some error?

pkg/log/logr.go Outdated
return logr.New(&Sink{
name: name,
ctx: ctx,
logger: L(ctx),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This L( is so miss leading, it's actually getting the logger from context - it was already there so no worries just annoying

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe I should be using the private impl that L is pointed to ? That would read better

Signed-off-by: hfuss <hayden.fuss@kaleido.io>
…amically via monitoring server; all dbsql logs to trace

Signed-off-by: hfuss <hayden.fuss@kaleido.io>
Signed-off-by: hfuss <hayden.fuss@kaleido.io>
Signed-off-by: hfuss <hayden.fuss@kaleido.io>
@onelapahead
Copy link
Contributor Author

Likely need to break out the dynamic logging changes into a separate PR... will do that once I'm done testing

Signed-off-by: hfuss <hayden.fuss@kaleido.io>
Signed-off-by: hfuss <hayden.fuss@kaleido.io>
Signed-off-by: hfuss <hayden.fuss@kaleido.io>
@onelapahead
Copy link
Contributor Author

After testing - this PR is working great for a few different use cases, but logr is yet another dependency for ff-common to support.

I think until ff-common migrates to a newer framework like zap instead of logrus - its best to avoid logr support. All the other logging enhancements will be pulled out to a smaller PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants