Files
opencloud/vendor/github.com/olekukonko/ll/field.go
dependabot[bot] 28148d02bd build(deps): bump github.com/olekukonko/tablewriter from 1.1.0 to 1.1.1
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>
2025-11-20 11:50:20 +01:00

376 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 builders fields embedded in its context.
// It clones the parent logger and copies the builders fields into the new loggers 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 parents context
newLogger.context = make(map[string]interface{})
// Copy builders fields into the new loggers context
for k, v := range fb.fields {
newLogger.context[k] = v
}
return newLogger
}
// Info logs a message at Info level with the builders fields.
// It concatenates the arguments with spaces and delegates to the loggers 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 builders 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 builders fields.
// It formats the message using the provided format string and arguments, then delegates
// to the loggers 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 builders fields, no stack trace
fb.logger.log(lx.LevelInfo, lx.ClassText, msg, fb.fields, false)
}
// Debug logs a message at Debug level with the builders fields.
// It concatenates the arguments with spaces and delegates to the loggers 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 builders 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 builders fields.
// It formats the message and delegates to the loggers 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 builders fields, no stack trace
fb.logger.log(lx.LevelDebug, lx.ClassText, msg, fb.fields, false)
}
// Warn logs a message at Warn level with the builders fields.
// It concatenates the arguments with spaces and delegates to the loggers 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 builders 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 builders fields.
// It formats the message and delegates to the loggers 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 builders fields, no stack trace
fb.logger.log(lx.LevelWarn, lx.ClassText, msg, fb.fields, false)
}
// Error logs a message at Error level with the builders fields.
// It concatenates the arguments with spaces and delegates to the loggers 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 builders 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 builders fields.
// It formats the message and delegates to the loggers 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 builders 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 builders fields.
// It concatenates the arguments with spaces and delegates to the loggers 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 builders 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 builders fields.
// It formats the message and delegates to the loggers 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 builders 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 builders 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 builders 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 builders 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 builders 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 builders 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 builders 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 loggers 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
}