Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft: logging implementation design #2010

Closed
wants to merge 6 commits into from

Conversation

bhautikpip
Copy link
Contributor

Started with initial implementation design based on this doc: https://docs.google.com/document/d/1WIHyMyA8ZnKaobJlqvvfzyVBD3HHfU0OC3elOrWKUJs/edit

Feel free to provide feedback/suggestions on what type of interface or methods we should add and if the current directory structure would be okay for SDK.

@bhautikpip
Copy link
Contributor Author

bhautikpip commented Jun 23, 2021

@Aneurysm9 @MrAlias Feel free to provide feedback.

)

type Logger interface {
Log(level LogLevel, msg fmt.Stringer)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Log(level LogLevel, msg fmt.Stringer)
Log(ctx context.Context, level LogLevel, msg fmt.Stringer, attrs ...attribute.KeyValue)

What about this interface? Including the Context enables the logger to correlate entries with active spans or other context data. Including attributes allows for something more like structured logging.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. I think we can add context and attribute to the interface method.

Copy link

@xaionaro xaionaro Sep 3, 2021

Choose a reason for hiding this comment

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

@Aneurysm9 , @bhautikpip : In structured logging I used to see an hierarchy of values. Like WithValue in logrus and With in zap: in some function we added a value and called another function, then in that function we added a one value more and called a logger.

And I do not understand how it supposed to work in this interface. Is the hierarchy of values stored in the context?


const (
// LogLevelDebug is usually only enabled when debugging.
LogLevelDebug LogLevel = iota + 1
Copy link
Member

Choose a reason for hiding this comment

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

Should we try to align these with the levels currently defined in the logging spec draft?


l.mu.Lock()
defer l.mu.Unlock()
_, _ = fmt.Fprintf(l.w, "%s [%s] %s\n", time.Now().Format(time.RFC3339), ll, msg)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
_, _ = fmt.Fprintf(l.w, "%s [%s] %s\n", time.Now().Format(time.RFC3339), ll, msg)
_, _ = fmt.Fprintf(l.w, "%s\t[%s]\t%s\n", time.Now().Format(time.RFC3339), ll, msg)

Tab-delimited records are easier to parse.

"go.opentelemetry.io/otel/otellog"
)

var Logger otellog.Logger = otellog.NewDefaultLogger(os.Stdout, otellog.LogLevelInfo)
Copy link
Member

Choose a reason for hiding this comment

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

I think we need to provide a mechanism somewhere for the application author to provide an alternate implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed. I would like it if this closer followed what we do with the ErrorHandler interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes I was thinking to add SetLogger API to add custom logging implementation but I will try to follow @MrAlias's suggestion and lookup ErrorHandler interface to maintain implementation parity within SDK.

Copy link
Contributor

Choose a reason for hiding this comment

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

What I was suggesting, in a very terse manner, was that we include a SetLogger and GetLogger function in the otel package and move the Logger interface to the otel package as well. This would mirror what is done with the ErrorHandler. From there, the default implementation could be abstracted away into an internal package.

Comment on lines +66 to +68
if ll < l.minLevel {
return
}
Copy link
Member

@pellared pellared Jun 29, 2021

Choose a reason for hiding this comment

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

nit but I think that important:
we need some API to check the current log level so that we can prevent unnecessary printf when big objects are being formated

at this point, it is a little too late: if one does logger.Tracef("dump my big object %v", iAmHuge) then iAmHuge would be formatted in the memory

I suggest adding SetLogLevel function to the logger package (side-by-side to the SetLogger function). So that the functions like Trace and Tracef in this package could take advantage of the global log level.

@xaionaro
Copy link

xaionaro commented Sep 6, 2021

I'm working on a very similar thing. And I would appreciate your feedback on: https://github.com/6543/log/tree/observability_api_draft . And it would be really great to unite forces if possible.

Specifically for example my understanding of OpenTelemetry may be wrong :)

)

type Logger interface {
Log(level LogLevel, msg fmt.Stringer)
Copy link

Choose a reason for hiding this comment

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

Suggested change
Log(level LogLevel, msg fmt.Stringer)
Log(ctx context.Context, level LogLevel, msg fmt.Stringer, attrs ...attribute.KeyValue)
ContextDecoder(ctx context.Context) []attribute.KeyValue

What about this interface? I think just including the Context is not enough. It should provide an way to "decode" the Context, which means getting values from the Context and logging these values that we exactly need.
For example, In a http server, the Context invocation chain always begins from the Request.Context(). Therefore, there are many redundant values in the passed Context. It is expensive to log all these values. What we need in such a Context may just the tracing related values. In such a situation, including a ContextDecoder can solve it easily, remaining scalability and flexibility.
In default case, it is enough maintaining a NoopDecoder that ignoring the Context and returning nil.

@MrAlias
Copy link
Contributor

MrAlias commented Nov 23, 2021

Closing in favor of #2343

@MrAlias MrAlias closed this Nov 23, 2021
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.

None yet

6 participants