phuslog Documentation

repository·master·Indexed 21 days ago

https://github.com/phuslu/log

A high-performance, dependency-free structured logging library for Go designed for fast JSON output. It features a variety of writers including AsyncWriter for low-latency logging, FileWriter for size-based rotation, and ConsoleWriter for human-friendly colorized output. The library includes an OpenTelemetry Logs Adapter for compatibility with the OpenTelemetry Logs API and provides integration options for the standard slog API.

Tokens
17.7K
Snippets
74
Records
84
Agent score
74%

What's inside phuslog

  1. Use the phuslog OpenTelemetry Logs Adapter

    master

    The phuslog/otel submodule provides compatibility with the OpenTelemetry Logs API while maintaining phuslog's high-performance flat JSON output format. It bridges OpenTelemetry log.Record objects to github.com/phuslu/log, preserving metadata like severity, trace context, scope, and attributes as JSON fields.

    Note: This is not an OTLP exporter and does not implement the full OpenTelemetry SDK pipeline. To send logs to an OpenTelemetry Collector, write the phuslog JSON to stdout/file and use a Collector receiver, or use a separate OTLP exporter.

    package main
    
    import (
    	"context"
    	phuslog "github.com/phuslu/log"
    	phuslogotel "github.com/phuslu/log/otel"
    	otellog "go.opentelemetry.io/otel/log"
    )
    
    func main() {
    	var logger otellog.Logger = phuslogotel.Logger{
    		Log: phuslog.Logger{
    			Level: phuslog.InfoLevel,
    		},
    	}
    
    	var record otellog.Record
    	record.SetSeverity(otellog.SeverityInfo)
    	record.SetBody(otellog.StringValue("hello from otel"))
    	record.AddAttributes(otellogog.String("component", "worker"))
    
    	logger.Emit(context.Background(), record)
    }
  2. Integrate phuslog with OpenTelemetry LoggerProvider

    master

    When working with OpenTelemetry code that expects a log.LoggerProvider, you can use the phuslogotel.Logger implementation to satisfy the otellog.Logger interface. This allows you to use standard OpenTelemetry logging calls while benefiting from phuslog's performance and JSON format.

    // Implementation of otellog.Logger using phuslog
    var logger otellog.Logger = phuslogotel.Logger{
    	Log: phuslog.Logger{
    		Level: phuslog.InfoLevel,
    	},
    }
  3. Use StdoutExporter for standard OpenTelemetry stdout JSON

    master

    If you require standard OpenTelemetry stdout log JSON instead of the phuslog flat JSON format, use phuslogotel.StdoutExporter. This is used within the OpenTelemetry SDK pipeline (e.g., with a BatchProcessor).

    package main
    
    import (
    	"context"
    	"os"
    
    	phuslogotel "github.com/phuslu/log/otel"
    	"go.opentelemetry.io/otel/log/global"
    	otellog "go.opentelemetry.io/otel/log"
    	sdklog "go.opentelemetry.io/otel/sdk/log"
    )
    
    func main() {
    	ctx := context.Background()
    	exporter := &phuslogotel.StdoutExporter{Writer: os.Stdout}
    	provider := sdklog.NewLoggerProvider(
    		sdklog.WithProcessor(sdklog.NewBatchProcessor(exporter)),
    	)
    	defer provider.Shutdown(ctx)
    	global.SetLoggerProvider(provider)
    
    	logger := global.Logger("example")
    	var record otellog.Record
    	record.SetSeverity(otellog.SeverityInfo)
    	record.SetBody(otellog.StringValue("hello from otel stdout"))
    	record.AddAttributes(otellog.String("component", "worker"))
    
    	logger.Emit(ctx, record)
    }
  4. Customize Logger Fields and Format

    master

    You can customize the global log.DefaultLogger or create local log.Logger instances to control field names, time formats, and log levels.

    Key configuration fields:

    • Level: The minimum log level (e.g., log.InfoLevel).
    • Caller: Depth for capturing the file/line number.
    • TimeField: The name of the timestamp field in the output.
    • TimeFormat: The format string for the timestamp (supports standard Go layouts or constants like log.TimeFormatUnixMs).
    • Writer: The destination for logs.
    package main
    
    import (
    	"os"
    	"github.com/phuslu/log"
    )
    
    func main() {
    	// Customizing the default global logger
    	log.DefaultLogger = log.Logger{
    		Level:      log.InfoLevel,
    		Caller:     1,
    		TimeField:  "date",
    		TimeFormat: "2006-01-02",
    		Writer:     &log.IOWriter{os.Stdout},
    	}
    
    	log.Info().Str("foo", "bar").Msgf("hello %s", "world")
    }
  5. Use FileWriter for rotating file-based logging

    master

    The FileWriter struct provides a mechanism for writing logs to a file with automatic rotation based on file size. When a file reaches MaxSize, it is rotated, a new file is created, and the old file is renamed with a timestamp.

    Key features include:

    • Automatic Rotation: Triggered when the file exceeds MaxSize bytes.
    • Backup Management: Retains a specific number of old log files defined by MaxBackups (0 retains all).
    • Symlinking: By default, the Filename acts as a symlink to the current active log file, making it easier to track the 'latest' log.
    • Directory Management: If EnsureFolder is true, the writer will automatically create the necessary directory structure.
    import "github.com/phuslu/phuslu/log"
    
    writer := &log.FileWriter{
    	Filename:     "/var/log/app/server.log",
    	MaxSize:      100 * 1024 * 1024, // 100 MB
    	MaxBackups:   5,
    	EnsureFolder: true,
    	FileMode:     0644,
    }
    
    // Use as an io.Writer
    writer.Write([]byte("log message\n"))
  6. Use Context for pre-encoded JSON fragments

    master

    The Context type allows you to work with pre-encoded JSON byte slices to avoid re-encoding overhead. This is useful for high-performance scenarios where parts of the log context are reused.

    1. Create a context using NewContext(dst []byte).
    2. Build fields into that context using Entry methods.
    3. Retrieve the bytes via Value().
    4. Attach it to a new log entry using Dict(key string, ctx Context) or Context(ctx Context).

    Dict wraps the context in a JSON object {...} under the specified key, while Context appends the raw bytes directly to the entry buffer.

    // 1. Create a context
    ctx := NewContext(nil)
    ctx.Str("session_id", "abc-123").Int("retry", 1)
    
    // 2. Use it in a log entry
    logger.Info().
        Dict("session_context", ctx.Value()).
        Msg("user action")
  7. Implement OpenTelemetry logging with phuslog

    master

    The otel package provides an implementation of the OpenTelemetry (OTel) Logger and LoggerProvider interfaces, using phuslog as the underlying structured logging engine. This allows you to use standard OpenTelemetry logging APIs while benefiting from the performance and structured output of phuslog.

    To use it, you typically create a LoggerProvider configured with a phuslog.Logger, and then use that provider to create OTel-compliant Logger instances.

    // Example conceptual usage
    provider := otel.LoggerProvider{
        Log: phuslogLogger, // An existing phuslog.Logger
    }
    
    // Create an OTel logger
    otelLogger := provider.Logger("my-service")
    
    // Use the OTel logger
    ctx := context.Background()
    otelLogger.Emit(ctx, otellog.NewRecord(otellog.SeverityInfo, otellog.NewValue(otellog.KindString, "hello world")))
  8. Use the global DefaultLogger

    master

    The package provides a DefaultLogger which is pre-configured to log to os.Stderr at DebugLevel. You can use it directly via package-level functions like log.Info() or log.Error().

    import "github.com/phuslu/phuslu/log"
    
    func main() {
        log.Info().Msg("hello world")
        log.Error().Str("error_code", "500").Msg("something went wrong")
    }
  9. Simple Logging Example

    master

    By default, phuslog writes to os.Stderr. You can use structured logging by chaining methods like .Str(), .Int(), and .Err() before calling a terminal method like .Msg() or .Msgf().

    package main
    
    import (
    	"github.com/phuslu/log"
    )
    
    func main() {
    	log.Info().Str("foo", "bar").Int("number", 42).Msg("hi, phuslog")
    	log.Info().Msgf("foo=%s number=%d error=%+v", "bar", 42, "an error")
    }
  10. Integrate with slog

    master

    You can use phuslog as a high-performance replacement for slog.JSONHandler or wrap it to work with the standard slog API.

    To use it as a drop-in replacement for slog.JSONHandler:

    slog.SetDefault(slog.New(phuslog.SlogNewJSONHandler(os.Stderr, &slog.HandlerOptions{AddSource: true})))
    import (
    	"log/slog"
    	"os"
    	phuslog "github.com/phuslu/log"
    )
    
    func main() {
    	slog.SetDefault(slog.New(phuslog.SlogNewJSONHandler(os.Stderr, &slog.HandlerOptions{AddSource: true})))
    	slog.Info("hello from phuslog", "a", 1, "b", 2)
    }
  11. Add contextual fields with NewContext

    master

    Use log.NewContext to create a context containing key-value pairs that will be automatically included in every subsequent log entry from that logger instance.

    logger := log.Logger{
    	Level:   log.InfoLevel,
    	Context: log.NewContext(nil).Str("ctx", "some_ctx").Value(),
    }
    
    logger.Info().Msg("this log will have the ctx field")
  12. Configure FileWriter for rotating log files

    master

    The FileWriter writes logs to a specified file and handles rotation based on file size.

    Key features:

    • Rotation: Logs rotate when the file reaches MaxSize. It uses a symlink to point to the current log file with a timestamp instead of renaming, which is highly efficient but may require administrator privileges on Windows.
    • Retention: Use MaxBackups to limit the number of old log files kept.
    • Directory Management: Set EnsureFolder: true to automatically create the log directory if it doesn't exist.
    • Performance Tip: For maximum throughput on Linux, combine FileWriter with an AsyncWriter.

    Configuration options:

    • Filename: Path to the log file.
    • FileMode: File permissions (default 0644).
    • MaxSize: Rotation threshold in bytes.
    • MaxBackups: Number of old files to retain (default: retain all).
    • TimeFormat: Format for filenames (default 2006-01-02T15-04-05). Supports TimeFormatUnix and TimeFormatUnixMs.
    • LocalTime: Use local time instead of UTC for filenames.
    • HostName, ProcessID: Include these in the filename.
    • Header: Optional function to add a header to a new log file after rotation.
    • Cleaner: Optional function to customize how log backups are cleaned up.
    writer := &log.FileWriter{
        Filename:     "/var/log/app.log",
        MaxSize:      100 * 1024 * 1024, // 100MB
        MaxBackups:   5,
        EnsureFolder: true,
    }