mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-15 08:41:16 -05:00
Bumps [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) from 1.1.0 to 1.1.1. - [Commits](https://github.com/olekukonko/tablewriter/compare/v1.1.0...v1.1.1) --- updated-dependencies: - dependency-name: github.com/olekukonko/tablewriter dependency-version: 1.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
376 lines
14 KiB
Go
376 lines
14 KiB
Go
package ll
|
||
|
||
import (
|
||
"fmt"
|
||
"github.com/olekukonko/cat"
|
||
"github.com/olekukonko/ll/lx"
|
||
"os"
|
||
"strings"
|
||
)
|
||
|
||
// FieldBuilder enables fluent addition of fields before logging.
|
||
// It acts as a builder pattern to attach key-value pairs (fields) to log entries,
|
||
// supporting structured logging with metadata. The builder allows chaining to add fields
|
||
// and log messages at various levels (Info, Debug, Warn, Error, etc.) in a single expression.
|
||
type FieldBuilder struct {
|
||
logger *Logger // Associated logger instance for logging operations
|
||
fields map[string]interface{} // Fields to include in the log entry as key-value pairs
|
||
}
|
||
|
||
// Logger creates a new logger with the builder’s fields embedded in its context.
|
||
// It clones the parent logger and copies the builder’s fields into the new logger’s context,
|
||
// enabling persistent field inclusion in subsequent logs. This method supports fluent chaining
|
||
// after Fields or Field calls.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// newLogger := logger.Fields("user", "alice").Logger()
|
||
// newLogger.Info("Action") // Output: [app] INFO: Action [user=alice]
|
||
func (fb *FieldBuilder) Logger() *Logger {
|
||
// Clone the parent logger to preserve its configuration
|
||
newLogger := fb.logger.Clone()
|
||
// Initialize a new context map to avoid modifying the parent’s context
|
||
newLogger.context = make(map[string]interface{})
|
||
// Copy builder’s fields into the new logger’s context
|
||
for k, v := range fb.fields {
|
||
newLogger.context[k] = v
|
||
}
|
||
return newLogger
|
||
}
|
||
|
||
// Info logs a message at Info level with the builder’s fields.
|
||
// It concatenates the arguments with spaces and delegates to the logger’s log method,
|
||
// returning early if fields are nil. This method is used for informational messages.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Info("Action", "started") // Output: [app] INFO: Action started [user=alice]
|
||
func (fb *FieldBuilder) Info(args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Log at Info level with the builder’s fields, no stack trace
|
||
fb.logger.log(lx.LevelInfo, lx.ClassText, cat.Space(args...), fb.fields, false)
|
||
}
|
||
|
||
// Infof logs a message at Info level with the builder’s fields.
|
||
// It formats the message using the provided format string and arguments, then delegates
|
||
// to the logger’s internal log method. If fields are nil, it returns early to avoid logging.
|
||
// This method is part of the fluent API, typically called after adding fields.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Infof("Action %s", "started") // Output: [app] INFO: Action started [user=alice]
|
||
func (fb *FieldBuilder) Infof(format string, args ...any) {
|
||
// Skip logging if fields are nil to prevent invalid log entries
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Format the message using the provided arguments
|
||
msg := fmt.Sprintf(format, args...)
|
||
// Log at Info level with the builder’s fields, no stack trace
|
||
fb.logger.log(lx.LevelInfo, lx.ClassText, msg, fb.fields, false)
|
||
}
|
||
|
||
// Debug logs a message at Debug level with the builder’s fields.
|
||
// It concatenates the arguments with spaces and delegates to the logger’s log method,
|
||
// returning early if fields are nil. This method is used for debugging information.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Debug("Debugging", "mode") // Output: [app] DEBUG: Debugging mode [user=alice]
|
||
func (fb *FieldBuilder) Debug(args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Log at Debug level with the builder’s fields, no stack trace
|
||
fb.logger.log(lx.LevelDebug, lx.ClassText, cat.Space(args...), fb.fields, false)
|
||
}
|
||
|
||
// Debugf logs a message at Debug level with the builder’s fields.
|
||
// It formats the message and delegates to the logger’s log method, returning early if
|
||
// fields are nil. This method is used for debugging information that may be disabled in
|
||
// production environments.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Debugf("Debug %s", "mode") // Output: [app] DEBUG: Debug mode [user=alice]
|
||
func (fb *FieldBuilder) Debugf(format string, args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Format the message
|
||
msg := fmt.Sprintf(format, args...)
|
||
// Log at Debug level with the builder’s fields, no stack trace
|
||
fb.logger.log(lx.LevelDebug, lx.ClassText, msg, fb.fields, false)
|
||
}
|
||
|
||
// Warn logs a message at Warn level with the builder’s fields.
|
||
// It concatenates the arguments with spaces and delegates to the logger’s log method,
|
||
// returning early if fields are nil. This method is used for warning conditions.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Warn("Warning", "issued") // Output: [app] WARN: Warning issued [user=alice]
|
||
func (fb *FieldBuilder) Warn(args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Log at Warn level with the builder’s fields, no stack trace
|
||
fb.logger.log(lx.LevelWarn, lx.ClassText, cat.Space(args...), fb.fields, false)
|
||
}
|
||
|
||
// Warnf logs a message at Warn level with the builder’s fields.
|
||
// It formats the message and delegates to the logger’s log method, returning early if
|
||
// fields are nil. This method is used for warning conditions that do not halt execution.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Warnf("Warning %s", "issued") // Output: [app] WARN: Warning issued [user=alice]
|
||
func (fb *FieldBuilder) Warnf(format string, args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Format the message
|
||
msg := fmt.Sprintf(format, args...)
|
||
// Log at Warn level with the builder’s fields, no stack trace
|
||
fb.logger.log(lx.LevelWarn, lx.ClassText, msg, fb.fields, false)
|
||
}
|
||
|
||
// Error logs a message at Error level with the builder’s fields.
|
||
// It concatenates the arguments with spaces and delegates to the logger’s log method,
|
||
// returning early if fields are nil. This method is used for error conditions.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Error("Error", "occurred") // Output: [app] ERROR: Error occurred [user=alice]
|
||
func (fb *FieldBuilder) Error(args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Log at Error level with the builder’s fields, no stack trace
|
||
fb.logger.log(lx.LevelError, lx.ClassText, cat.Space(args...), fb.fields, false)
|
||
}
|
||
|
||
// Errorf logs a message at Error level with the builder’s fields.
|
||
// It formats the message and delegates to the logger’s log method, returning early if
|
||
// fields are nil. This method is used for error conditions that may require attention.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Errorf("Error %s", "occurred") // Output: [app] ERROR: Error occurred [user=alice]
|
||
func (fb *FieldBuilder) Errorf(format string, args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Format the message
|
||
msg := fmt.Sprintf(format, args...)
|
||
// Log at Error level with the builder’s fields, no stack trace
|
||
fb.logger.log(lx.LevelError, lx.ClassText, msg, fb.fields, false)
|
||
}
|
||
|
||
// Stack logs a message at Error level with a stack trace and the builder’s fields.
|
||
// It concatenates the arguments with spaces and delegates to the logger’s log method,
|
||
// returning early if fields are nil. This method is useful for debugging critical errors.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Stack("Critical", "error") // Output: [app] ERROR: Critical error [user=alice stack=...]
|
||
func (fb *FieldBuilder) Stack(args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Log at Error level with the builder’s fields and a stack trace
|
||
fb.logger.log(lx.LevelError, lx.ClassText, cat.Space(args...), fb.fields, true)
|
||
}
|
||
|
||
// Stackf logs a message at Error level with a stack trace and the builder’s fields.
|
||
// It formats the message and delegates to the logger’s log method, returning early if
|
||
// fields are nil. This method is useful for debugging critical errors.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Stackf("Critical %s", "error") // Output: [app] ERROR: Critical error [user=alice stack=...]
|
||
func (fb *FieldBuilder) Stackf(format string, args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Format the message
|
||
msg := fmt.Sprintf(format, args...)
|
||
// Log at Error level with the builder’s fields and a stack trace
|
||
fb.logger.log(lx.LevelError, lx.ClassText, msg, fb.fields, true)
|
||
}
|
||
|
||
// Fatal logs a message at Error level with a stack trace and the builder’s fields, then exits.
|
||
// It constructs the message from variadic arguments, logs it with a stack trace, and terminates
|
||
// the program with exit code 1. Returns early if fields are nil. This method is used for
|
||
// unrecoverable errors.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Fatal("Fatal", "error") // Output: [app] ERROR: Fatal error [user=alice stack=...], then exits
|
||
func (fb *FieldBuilder) Fatal(args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Build the message by concatenating arguments with spaces
|
||
var builder strings.Builder
|
||
for i, arg := range args {
|
||
if i > 0 {
|
||
builder.WriteString(lx.Space)
|
||
}
|
||
builder.WriteString(fmt.Sprint(arg))
|
||
}
|
||
// Log at Error level with the builder’s fields and a stack trace
|
||
fb.logger.log(lx.LevelError, lx.ClassText, builder.String(), fb.fields, true)
|
||
// Exit the program with status code 1
|
||
os.Exit(1)
|
||
}
|
||
|
||
// Fatalf logs a formatted message at Error level with a stack trace and the builder’s fields,
|
||
// then exits. It delegates to Fatal and returns early if fields are nil. This method is used
|
||
// for unrecoverable errors.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Fatalf("Fatal %s", "error") // Output: [app] ERROR: Fatal error [user=alice stack=...], then exits
|
||
func (fb *FieldBuilder) Fatalf(format string, args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Format the message and pass to Fatal
|
||
fb.Fatal(fmt.Sprintf(format, args...))
|
||
}
|
||
|
||
// Panic logs a message at Error level with a stack trace and the builder’s fields, then panics.
|
||
// It constructs the message from variadic arguments, logs it with a stack trace, and triggers
|
||
// a panic with the message. Returns early if fields are nil. This method is used for critical
|
||
// errors that require immediate program termination with a panic.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Panic("Panic", "error") // Output: [app] ERROR: Panic error [user=alice stack=...], then panics
|
||
func (fb *FieldBuilder) Panic(args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Build the message by concatenating arguments with spaces
|
||
var builder strings.Builder
|
||
for i, arg := range args {
|
||
if i > 0 {
|
||
builder.WriteString(lx.Space)
|
||
}
|
||
builder.WriteString(fmt.Sprint(arg))
|
||
}
|
||
msg := builder.String()
|
||
// Log at Error level with the builder’s fields and a stack trace
|
||
fb.logger.log(lx.LevelError, lx.ClassText, msg, fb.fields, true)
|
||
// Trigger a panic with the formatted message
|
||
panic(msg)
|
||
}
|
||
|
||
// Panicf logs a formatted message at Error level with a stack trace and the builder’s fields,
|
||
// then panics. It delegates to Panic and returns early if fields are nil. This method is used
|
||
// for critical errors that require immediate program termination with a panic.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("user", "alice").Panicf("Panic %s", "error") // Output: [app] ERROR: Panic error [user=alice stack=...], then panics
|
||
func (fb *FieldBuilder) Panicf(format string, args ...any) {
|
||
// Skip logging if fields are nil
|
||
if fb.fields == nil {
|
||
return
|
||
}
|
||
// Format the message and pass to Panic
|
||
fb.Panic(fmt.Sprintf(format, args...))
|
||
}
|
||
|
||
// Err adds one or more errors to the FieldBuilder as a field and logs them.
|
||
// It stores non-nil errors in the "error" field: a single error if only one is non-nil,
|
||
// or a slice of errors if multiple are non-nil. It logs the concatenated string representations
|
||
// of non-nil errors (e.g., "failed 1; failed 2") at the Error level. Returns the FieldBuilder
|
||
// for chaining, allowing further field additions or logging. Thread-safe via the logger’s mutex.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// err1 := errors.New("failed 1")
|
||
// err2 := errors.New("failed 2")
|
||
// logger.Fields("k", "v").Err(err1, err2).Info("Error occurred")
|
||
// // Output: [app] ERROR: failed 1; failed 2
|
||
// // [app] INFO: Error occurred [error=[failed 1 failed 2] k=v]
|
||
func (fb *FieldBuilder) Err(errs ...error) *FieldBuilder {
|
||
// Initialize fields map if nil
|
||
if fb.fields == nil {
|
||
fb.fields = make(map[string]interface{})
|
||
}
|
||
|
||
// Collect non-nil errors and build log message
|
||
var nonNilErrors []error
|
||
var builder strings.Builder
|
||
count := 0
|
||
for i, err := range errs {
|
||
if err != nil {
|
||
if i > 0 && count > 0 {
|
||
builder.WriteString("; ")
|
||
}
|
||
builder.WriteString(err.Error())
|
||
nonNilErrors = append(nonNilErrors, err)
|
||
count++
|
||
}
|
||
}
|
||
|
||
// Set error field and log if there are non-nil errors
|
||
if count > 0 {
|
||
if count == 1 {
|
||
// Store single error directly
|
||
fb.fields["error"] = nonNilErrors[0]
|
||
} else {
|
||
// Store slice of errors
|
||
fb.fields["error"] = nonNilErrors
|
||
}
|
||
// Log concatenated error messages at Error level
|
||
fb.logger.log(lx.LevelError, lx.ClassText, builder.String(), nil, false)
|
||
}
|
||
|
||
// Return FieldBuilder for chaining
|
||
return fb
|
||
}
|
||
|
||
// Merge adds additional key-value pairs to the FieldBuilder.
|
||
// It processes variadic arguments as key-value pairs, expecting string keys. Non-string keys
|
||
// or uneven pairs generate an "error" field with a descriptive message. Returns the FieldBuilder
|
||
// for chaining to allow further field additions or logging.
|
||
// Example:
|
||
//
|
||
// logger := New("app").Enable()
|
||
// logger.Fields("k1", "v1").Merge("k2", "v2").Info("Action") // Output: [app] INFO: Action [k1=v1 k2=v2]
|
||
func (fb *FieldBuilder) Merge(pairs ...any) *FieldBuilder {
|
||
// Process pairs as key-value, advancing by 2
|
||
for i := 0; i < len(pairs)-1; i += 2 {
|
||
// Ensure the key is a string
|
||
if key, ok := pairs[i].(string); ok {
|
||
fb.fields[key] = pairs[i+1]
|
||
} else {
|
||
// Log an error field for non-string keys
|
||
fb.fields["error"] = fmt.Errorf("non-string key in Merge: %v", pairs[i])
|
||
}
|
||
}
|
||
// Check for uneven pairs (missing value)
|
||
if len(pairs)%2 != 0 {
|
||
fb.fields["error"] = fmt.Errorf("uneven key-value pairs in Merge: [%v]", pairs[len(pairs)-1])
|
||
}
|
||
return fb
|
||
}
|