From 63c429285ef865fe15364566b04a85fac825abec Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Tue, 27 Aug 2024 17:31:32 +0700 Subject: [PATCH] zlog: added notification support for log functions --- core/zerr/err.go | 2 - core/zlog/exported.go | 93 ++++++++++++++++++++++----------------- core/zlog/notification.go | 20 ++++++++- log.go | 36 +++++++++++++++ 4 files changed, 107 insertions(+), 44 deletions(-) diff --git a/core/zerr/err.go b/core/zerr/err.go index cfbf101..c1b4d7a 100644 --- a/core/zerr/err.go +++ b/core/zerr/err.go @@ -193,8 +193,6 @@ func (mu *Err) GetTime() time.Time { // ID sets the error ID. // // ID is used to identify the error. Used by Zen to consider if an error is the same as another. -// -// Current implementation func (mu *Err) ID(msg string, args ...any) Error { mu.id = fmt.Sprintf(msg, args...) return mu diff --git a/core/zlog/exported.go b/core/zlog/exported.go index 6b9aab7..5afed22 100644 --- a/core/zlog/exported.go +++ b/core/zlog/exported.go @@ -12,140 +12,153 @@ import ( var Logger = New(os.Stderr, defaultOpts) -func Info(ctx context.Context, msg string) { +func Info(ctx context.Context, msg string) Notifier { if !Logger.Enabled(ctx, slog.LevelInfo) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() - Log(ctx, msg, pc, t, slog.LevelInfo) + return Log(ctx, msg, pc, t, slog.LevelInfo) } -func Infof(ctx context.Context, msg string, args ...any) { +func Infof(ctx context.Context, msg string, args ...any) Notifier { if !Logger.Enabled(ctx, slog.LevelInfo) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() msg = fmt.Sprintf(msg, args...) - Log(ctx, msg, pc, t, slog.LevelInfo) + return Log(ctx, msg, pc, t, slog.LevelInfo) } -func Infow(ctx context.Context, msg string, fields ...any) { +func Infow(ctx context.Context, msg string, fields ...any) Notifier { if !Logger.Enabled(ctx, slog.LevelInfo) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() - Log(ctx, msg, pc, t, slog.LevelInfo, fields...) + return Log(ctx, msg, pc, t, slog.LevelInfo, fields...) } -func Debug(ctx context.Context, msg string) { +func Debug(ctx context.Context, msg string) Notifier { if !Logger.Enabled(ctx, slog.LevelDebug) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() - Log(ctx, msg, pc, t, slog.LevelDebug) + return Log(ctx, msg, pc, t, slog.LevelDebug) } -func Debugf(ctx context.Context, msg string, args ...any) { +func Debugf(ctx context.Context, msg string, args ...any) Notifier { if !Logger.Enabled(ctx, slog.LevelDebug) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() msg = fmt.Sprintf(msg, args...) - Log(ctx, msg, pc, t, slog.LevelDebug) + return Log(ctx, msg, pc, t, slog.LevelDebug) } -func Debugw(ctx context.Context, msg string, fields ...any) { +func Debugw(ctx context.Context, msg string, fields ...any) Notifier { if !Logger.Enabled(ctx, slog.LevelDebug) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() - Log(ctx, msg, pc, t, slog.LevelDebug, fields...) + return Log(ctx, msg, pc, t, slog.LevelDebug, fields...) } -func Warn(ctx context.Context, msg string) { +func Warn(ctx context.Context, msg string) Notifier { if !Logger.Enabled(ctx, slog.LevelWarn) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() - Log(ctx, msg, pc, t, slog.LevelWarn) + return Log(ctx, msg, pc, t, slog.LevelWarn) } -func Warnf(ctx context.Context, msg string, args ...any) { +func Warnf(ctx context.Context, msg string, args ...any) Notifier { if !Logger.Enabled(ctx, slog.LevelWarn) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() msg = fmt.Sprintf(msg, args...) - Log(ctx, msg, pc, t, slog.LevelWarn) + return Log(ctx, msg, pc, t, slog.LevelWarn) } -func Warnw(ctx context.Context, msg string, fields ...any) { +func Warnw(ctx context.Context, msg string, fields ...any) Notifier { if !Logger.Enabled(ctx, slog.LevelWarn) { - return + return notifier{} } pc := zcaller.Get(3) t := time.Now() - Log(ctx, msg, pc, t, slog.LevelWarn, fields...) + return Log(ctx, msg, pc, t, slog.LevelWarn, fields...) } -func Error(ctx context.Context, msg string) { +func Error(ctx context.Context, msg string) Notifier { if !Logger.Enabled(ctx, slog.LevelError) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() - Log(ctx, msg, pc, t, slog.LevelError) + return Log(ctx, msg, pc, t, slog.LevelError) } -func Errorf(ctx context.Context, msg string, args ...any) { +func Errorf(ctx context.Context, msg string, args ...any) Notifier { if !Logger.Enabled(ctx, slog.LevelError) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() msg = fmt.Sprintf(msg, args...) - Log(ctx, msg, pc, t, slog.LevelError) + return Log(ctx, msg, pc, t, slog.LevelError) } -func Errorw(ctx context.Context, msg string, fields ...any) { +func Errorw(ctx context.Context, msg string, fields ...any) Notifier { if !Logger.Enabled(ctx, slog.LevelError) { - return + return nullNotifier{} } pc := zcaller.Get(3) t := time.Now() - Log(ctx, msg, pc, t, slog.LevelError, fields...) + return Log(ctx, msg, pc, t, slog.LevelError, fields...) } -func LogRecord(ctx context.Context, record slog.Record) { +func LogRecord(ctx context.Context, record slog.Record) Notifier { if !Logger.Enabled(ctx, record.Level) { - return + return nullNotifier{} } - Logger.Handle(ctx, record) + if err := Logger.Handle(ctx, record); err != nil { + slog.ErrorContext(ctx, "failed to log", "error", err) + } + notifier := ¬ifier{ + ctx: ctx, + notifier: Logger.opts.NotificationHandler, + record: record, + } + return notifier } -func Log(ctx context.Context, msg string, pc uintptr, t time.Time, level slog.Level, fields ...any) { +func Log(ctx context.Context, msg string, pc uintptr, t time.Time, level slog.Level, fields ...any) Notifier { record := slog.NewRecord(t, level, msg, pc) record.Add(fields...) // error is only returned if the output writer fails. if err := Logger.Handle(ctx, record); err != nil { slog.ErrorContext(ctx, "failed to log", "error", err) } + return ¬ifier{ + ctx: ctx, + notifier: Logger.opts.NotificationHandler, + record: record, + } } func SetDefault(log *ZLog) { diff --git a/core/zlog/notification.go b/core/zlog/notification.go index 505f8b6..41714e9 100644 --- a/core/zlog/notification.go +++ b/core/zlog/notification.go @@ -8,11 +8,27 @@ import ( ) type Notifier interface { + // Notify sends a notification from this Log Entry. Notify(options ...zoptions.NotifyOption) } type NotificationHandler interface { - NotifyLog(ctx context.Context, record slog.Record) + NotifyLog(ctx context.Context, record slog.Record, options ...zoptions.NotifyOption) } -type notifyHandler struct{} +type notifier struct { + ctx context.Context + notifier NotificationHandler + record slog.Record +} + +func (no notifier) Notify(options ...zoptions.NotifyOption) { + if no.notifier == nil { + return + } + no.notifier.NotifyLog(no.ctx, no.record, options...) +} + +type nullNotifier struct{} + +func (nullNotifier) Notify(options ...zoptions.NotifyOption) {} diff --git a/log.go b/log.go index be94bc7..aae2975 100644 --- a/log.go +++ b/log.go @@ -13,6 +13,9 @@ var ( // Use Infof to log with a format. // // Use Infow to log with additional json fields. + // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. Info = zlog.Info // Infof logs a message at level Info with format. @@ -24,6 +27,9 @@ var ( // Use Infow to print structs and maps since they will // be rendered as json. // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. + // // Example: // // zen.Infof(ctx, "user '%s' logged in", user) @@ -50,6 +56,9 @@ var ( // // Note that primitive values are already optimized and will not use heavy reflection. // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. + // // Example: // // zen.Infow(ctx, "API called without error", @@ -68,6 +77,9 @@ var ( // Use [Debugf] to log with a format. // // Use [Debugw] to log with additional json fields. + // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. Debug = zlog.Debug // Debugf logs a message at level Debug with format. @@ -79,6 +91,9 @@ var ( // Use Debugw to print structs and maps since they will // be rendered as json. // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. + // // Example: // // zen.Debugf(ctx, "user '%s' logged in", user) @@ -103,6 +118,9 @@ var ( // use it to serialize. // Then for the last resort, use normal json.Marshal. // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. + // // Example: // // zen.Debugw(ctx, "database query", @@ -119,6 +137,9 @@ var ( // Use Warnf to log with a format. // // Use Warnw to log with additional json fields. + // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. Warn = zlog.Warn // Warnf logs a message at level Warn with format. @@ -130,6 +151,9 @@ var ( // Use Warnw to print structs and maps since they will // be rendered as json. // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. + // // Example: // // zen.Warnf(ctx, "user '%s' logged in", user) @@ -153,6 +177,9 @@ var ( // If does not, it will look if it implements [json.Marshaler] interface and // use it to serialize. // Then for the last resort, use normal json.Marshal. + // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. Warnw = zlog.Warnw // Error logs a message at level Error. @@ -163,6 +190,9 @@ var ( // Use Errorf to log with a format. // // Use Errorw to log with additional json fields. + // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. Error = zlog.Error // Errorf logs a message at level Error with format. @@ -174,6 +204,9 @@ var ( // Use Errorw to print structs and maps since they will // be rendered as json. // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. + // // Example: // // zen.Errorf(ctx, "user '%s' logged in", user) @@ -198,6 +231,9 @@ var ( // use it to serialize. // Then for the last resort, use normal json.Marshal. // + // You can call .Notify on the returned Notifier to send a notification + // for this log entry. + // // Example: // // zlog.Errorw(ctx, "RPC Error",