golog

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 18, 2026 License: MIT Imports: 11 Imported by: 0

README

🔪 Golog

Go Report Card GoDoc License

Golog Logo

A high-performance, structured logging library for Go services. Golog provides standardized logging with automatic log rotation, context-aware tracing, and sensitive data masking.

Features

  • 🚀 High Performance: Asynchronous buffered I/O for optimal performance
  • 📝 Structured Logging: JSON-formatted logs with context-aware fields
  • 🔄 Automatic Rotation: Configurable log file rotation based on size and age
  • 🔒 Security: Automatic masking of sensitive data (passwords, tokens, etc.)
  • 📊 TDR Logging: Transaction Detail Request logging for API requests/responses
  • 🎯 Type-Safe Context: Typed context keys for better code safety
  • 🔌 Flexible Usage: Singleton pattern or direct logger instances
  • 📦 Production Ready: Built on top of zap logger

Installation

Install the library using Go modules:

go get -u github.com/tommynurwantoro/golog

Quick Start

package main

import (
    "context"
    "github.com/tommynurwantoro/golog"
)

func main() {
    // Initialize logger
    config := golog.Config{
        App:          "myapp",
        AppVer:       "1.0.0",
        Env:          "development",
        FileLocation: "/var/log/myapp",
        FileMaxSize:  100, // megabytes
        FileMaxBackup: 5,
        FileMaxAge:   30, // days
        Stdout:       true,
    }

    logger := golog.Load(config)
    defer golog.Sync()

    // Create context with trace information
    ctx := context.Background()
    ctx = golog.WithTraceID(ctx, "trace-123")

    // Log messages (bind context via WithContext)
    logger.WithContext(ctx).Info("Application started")
    logger.WithContext(ctx).Debug("Debug information")
    logger.WithContext(ctx).Warn("Warning message")
}

Configuration

The Config struct allows you to customize logging behavior. All fields are validated and defaults are applied automatically.

Configuration Fields
Field Type Required Default Description
App string Yes - Application name (e.g., "myapp")
AppVer string Yes - Application version (e.g., "1.0.0")
Env string Yes - Environment: "development" or "production"
FileLocation string Yes - Directory where system logs will be saved
FileTDRLocation string No FileLocation Directory for TDR logs (writes to {FileTDRLocation}/tdr.log)
FileMaxSize int Yes - Maximum log file size in megabytes before rotation
FileMaxBackup int Yes - Maximum number of backup files to keep
FileMaxAge int Yes - Maximum age of backup files in days
Stdout bool No false Enable console output (useful for development)
LogLevel zapcore.Level No InfoLevel Minimum log level (Debug, Info, Warn, Error)
VersionFilePath string No "version.txt" Path to version file (overrides AppVer if exists)
Example Configuration
config := golog.Config{
    App:            "myapp",
    AppVer:         "1.0.0",
    Env:            "production",
    FileLocation:   "/var/log/myapp",
    FileTDRLocation: "/var/log/myapp", // Optional (defaults to FileLocation)
    FileMaxSize:    500,                        // 500 MB
    FileMaxBackup:  10,                         // Keep 10 backups
    FileMaxAge:     90,                         // Keep for 90 days
    Stdout:         false,                      // Disable in production
    LogLevel:       zapcore.InfoLevel,         // Optional
    VersionFilePath: "version.txt",             // Optional
}

Usage

The singleton pattern allows you to use the logger from anywhere in your application without passing it around:

package main

import (
    "context"
    "github.com/tommynurwantoro/golog"
    "go.uber.org/zap"
)

func main() {
    config := golog.Config{
        App:          "myapp",
        AppVer:       "1.0.0",
        Env:          "development",
        FileLocation: "/var/log/myapp",
        FileMaxSize:  100,
        FileMaxBackup: 5,
        FileMaxAge:   30,
        Stdout:       true,
    }

    // Initialize singleton logger
    golog.Load(config)
    defer golog.Sync() // Important: flush logs on exit

    // Use from anywhere - bind context via WithContext, then call log methods
    ctx := context.Background()
    ctx = golog.WithTraceID(ctx, "trace-123")

    golog.WithContext(ctx).Info("Application started")
    golog.WithContext(ctx).Debug("Debug message", zap.String("key", "value"))
    golog.WithContext(ctx).Warn("Warning message")
}
Direct Logger Instance

For more control or when you need multiple logger instances:

package main

import (
    "context"
    "github.com/tommynurwantoro/golog"
)

func main() {
    config := golog.Config{
        App:          "myapp",
        AppVer:       "1.0.0",
        Env:          "production",
        FileLocation: "/var/log/myapp",
        FileMaxSize:  500,
        FileMaxBackup: 10,
        FileMaxAge:   90,
        Stdout:       false,
    }

    logger := golog.NewLogger(config)
    defer logger.Sync() // Important: flush logs before exit

    ctx := context.Background()
    ctx = golog.WithTraceID(ctx, "trace-123")
    logger.WithContext(ctx).Info("Application started")
}
Logging Methods

Bind context via WithContext(ctx), then call logging methods with message and optional fields:

ctx := context.Background()
ctx = golog.WithTraceID(ctx, "trace-123")
log := golog.WithContext(ctx)

// Info level logging
log.Info("User logged in", zap.String("userID", "123"))

// Debug level logging
log.Debug("Processing request", zap.String("method", "GET"))

// Warning level logging
log.Warn("Rate limit approaching", zap.Int("requests", 95))

// Error level logging
err := errors.New("database connection failed")
log.Error("Failed to connect", err, zap.String("host", "db.example.com"))

// Fatal level logging (exits application)
// log.Fatal("Critical error", err)

// Panic level logging (panics)
// log.Panic("Unexpected error", err)
Context-Aware Logging

Golog automatically extracts trace information from the context. This makes it easy to track requests across your application.

Type-safe context keys prevent typos and provide better IDE support:

import (
    "context"
    "github.com/tommynurwantoro/golog"
)

func handleRequest(ctx context.Context) {
    // Add trace information to context
    ctx = golog.WithTraceID(ctx, "trace-123")
    ctx = golog.WithSrcIP(ctx, "192.168.1.1")
    ctx = golog.WithPort(ctx, "8080")
    ctx = golog.WithPath(ctx, "/api/users")

    // All logs will automatically include these fields
    golog.WithContext(ctx).Info("Request received")
    
    // Retrieve values if needed
    traceID, ok := golog.GetTraceID(ctx)
    if ok {
        // Use traceID
    }
}
Using String Keys (Backward Compatible)

For backward compatibility, you can still use string keys:

ctx = context.WithValue(ctx, "traceId", "trace-123")
ctx = context.WithValue(ctx, "srcIP", "192.168.1.1")
ctx = context.WithValue(ctx, "port", "8080")
ctx = context.WithValue(ctx, "path", "/api/users")
Available Context Keys
  • traceId / golog.WithTraceID() - Request trace ID
  • srcIP / golog.WithSrcIP() - Source IP address
  • port / golog.WithPort() - Port number
  • path / golog.WithPath() - Request path

All context values are automatically included in log entries.

Transaction Detail Request (TDR) Logging

TDR logging captures complete request/response information for API calls, including headers, request/response bodies, status codes, and response times. Sensitive data is automatically masked.

import (
    "context"
    "time"
    "github.com/tommynurwantoro/golog"
)

func logAPIRequest(ctx context.Context, req, resp interface{}) {
    tdr := golog.LogModel{
        CorrelationID: "corr-456",
        Method:        "POST",
        Path:          "/api/users",
        StatusCode:    "200",
        HttpStatus:    200,
        Header:        requestHeaders,      // http.Header or fasthttp.RequestHeader
        Request:       req,                 // Request body (will be masked)
        Response:      resp,                // Response body (will be masked)
        ResponseTime:  150 * time.Millisecond,
        Error:         nil,                 // Error if any
        OtherData:     map[string]interface{}{"custom": "data"},
    }

    golog.WithContext(ctx).TDR(tdr)
}

Note: TDR logs are written to a separate file (FileTDRLocation) for easier analysis and monitoring.

Sensitive Data Masking

Golog automatically masks sensitive fields in request/response bodies:

  • password
  • license, license_code
  • token, access_token, refresh_token

Sensitive headers are also removed:

  • Authorization
  • Signature
  • Apikey

Masked values are replaced with ***** in logs.

Log Output

File Output

By default, logs are written to files in the FileLocation directory:

  • System logs: {FileLocation}/system.log
  • TDR logs: {FileTDRLocation}/tdr.log (defaults to {FileLocation}/tdr.log)
Console Output

Enable console output for development by setting Stdout: true:

config := golog.Config{
    // ... other config
    Stdout: true, // Logs will appear in console
}

In production, set Stdout: false for better performance.

Log Rotation

Log files are automatically rotated when they reach FileMaxSize:

  • Old files are renamed with a timestamp suffix
  • Only FileMaxBackup backup files are kept
  • Files older than FileMaxAge days are automatically deleted

Example rotation:

system.log
system.log.2024-01-15
system.log.2024-01-14
Log Format

Logs are written in JSON format for easy parsing:

{
  "timestamp": "2024-01-15T10:30:45Z",
  "logLevel": "INFO",
  "message": "User logged in",
  "app": "myapp",
  "appVer": "1.0.0",
  "env": "production",
  "traceId": "trace-123",
  "srcIP": "192.168.1.1",
  "path": "/api/users",
  "userID": "123"
}

Performance Considerations

Best Practices
  1. Always call Sync() on shutdown: Ensures all buffered logs are written

    defer golog.Sync() // For singleton
    defer logger.Sync() // For direct logger
    
  2. Use appropriate log levels: Set LogLevel to filter out unnecessary logs in production

    config.LogLevel = zapcore.InfoLevel // Only log Info and above
    
  3. Disable stdout in production: Console output adds overhead

    config.Stdout = false // Production
    config.Stdout = true  // Development
    
  4. Use context efficiently: Pre-populate context at request entry point and bind once

    // At HTTP handler entry
    ctx = golog.WithTraceID(ctx, generateTraceID())
    ctx = golog.WithPath(ctx, r.URL.Path)
    log := golog.WithContext(ctx)
    // Use log.Info(), log.Error(), etc. throughout the handler
    
Performance Characteristics
  • Asynchronous I/O: Logs are buffered and written asynchronously
  • Zero-allocation context extraction: Context fields are extracted efficiently
  • Optimized JSON operations: Sensitive data masking is optimized for performance

Advanced Usage

Custom Log Levels
import "go.uber.org/zap/zapcore"

config := golog.Config{
    // ... other config
    LogLevel: zapcore.DebugLevel, // Enable debug logs
}
Version File Override

If a version file exists, it will override AppVer:

config := golog.Config{
    AppVer:         "1.0.0",        // Default version
    VersionFilePath: "version.txt", // File containing "1.2.3"
    // AppVer will be "1.2.3" if file exists
}
Testing

Reset the singleton logger between tests:

func TestSomething(t *testing.T) {
    golog.Reset() // Reset singleton
    
    config := golog.Config{/* test config */}
    logger := golog.Load(config)
    defer golog.Sync()
    
    // Your test code
}

Troubleshooting

Logs not appearing
  • Ensure Sync() is called before application exit
  • Check file permissions for FileLocation directory
  • Verify LogLevel allows the log level you're using
Performance issues
  • Disable Stdout in production
  • Increase FileMaxSize to reduce rotation frequency
  • Use appropriate LogLevel to filter unnecessary logs
Missing context fields
  • Ensure context is passed through your call chain
  • Bind context before logging: golog.WithContext(ctx).Info(...) or logger.WithContext(ctx).Info(...)
  • Use typed context keys (golog.WithTraceID()) for type safety
  • Check that context values are set before logging

Examples

See the logger_test.go file for usage examples including:

  • Basic logging
  • Context binding with WithContext
  • Error handling
  • TDR logging
  • Configuration and validation

Contributing

Contributions are welcome! Please follow the Contribution Guidelines.

Development Setup
git clone https://github.com/tommynurwantoro/golog.git
cd golog
go mod download
go test ./...

License

This project is licensed under the MIT License. See the LICENSE file for details.

Acknowledgments

  • Built on top of zap - A blazing fast, structured, leveled logging library
  • Uses lumberjack for log rotation

Documentation

Index

Constants

View Source
const (
	// TraceIDKey is the context key for trace ID
	TraceIDKey contextKey = "traceId"
	// SrcIPKey is the context key for source IP
	SrcIPKey contextKey = "srcIP"
	// PortKey is the context key for port
	PortKey contextKey = "port"
	// PathKey is the context key for path
	PathKey contextKey = "path"
)

Variables

View Source
var SENSITIVE_ATTR = map[string]bool{
	"password":      true,
	"license":       true,
	"license_code":  true,
	"token":         true,
	"access_token":  true,
	"refresh_token": true,
}
View Source
var SENSITIVE_HEADER = []string{
	"Authorization",
	"Signature",
	"Apikey",
}

Functions

func Debug

func Debug(msg string, fields ...zap.Field)

Debug logs a message at DebugLevel.

func Error

func Error(msg string, err error, fields ...zap.Field)

Error logs a message at ErrorLevel.

func Fatal

func Fatal(msg string, err error, fields ...zap.Field)

Fatal logs a message at FatalLevel.

The logger then calls os.Exit(1), even if logging at FatalLevel is disabled.

func GetPath

func GetPath(ctx context.Context) (string, bool)

GetPath retrieves path from context

func GetPort

func GetPort(ctx context.Context) (string, bool)

GetPort retrieves port from context

func GetSrcIP

func GetSrcIP(ctx context.Context) (string, bool)

GetSrcIP retrieves source IP from context

func GetTraceID

func GetTraceID(ctx context.Context) (string, bool)

GetTraceID retrieves trace ID from context

func Info

func Info(msg string, fields ...zap.Field)

Info logs a message at InfoLevel.

func NewArrayStringField

func NewArrayStringField(key string, value []string) zap.Field

func NewBooleanField

func NewBooleanField(key string, value bool) zap.Field

func NewInt64Field

func NewInt64Field(key string, value int64) zap.Field

func NewIntField

func NewIntField(key string, value int) zap.Field

func NewObjectField

func NewObjectField(key string, value interface{}) zap.Field

func NewStringField

func NewStringField(key string, value string) zap.Field

func Panic

func Panic(msg string, err error, fields ...zap.Field)

Panic logs a message at PanicLevel.

The logger then panics, even if logging at PanicLevel is disabled.

func Reset

func Reset()

Reset resets the singleton logger. This is primarily useful for testing. It should not be called in production code.

func Sync

func Sync() error

Sync flushes any buffered log entries. Applications should take care to call Sync before exiting to ensure all log entries are written.

func TDR

func TDR(model LogModel)

TDR (Transaction Detail Request) consist of request and response log

func Warn

func Warn(msg string, fields ...zap.Field)

Warn logs a message at WarnLevel.

func WithPath

func WithPath(ctx context.Context, path string) context.Context

WithPath adds path to the context

func WithPort

func WithPort(ctx context.Context, port string) context.Context

WithPort adds port to the context

func WithSrcIP

func WithSrcIP(ctx context.Context, srcIP string) context.Context

WithSrcIP adds source IP to the context

func WithTraceID

func WithTraceID(ctx context.Context, traceID string) context.Context

WithTraceID adds trace ID to the context

Types

type Config

type Config struct {
	// App name
	App string `json:"app"`

	// App Version
	AppVer string `json:"appVer"`

	// Log environment (development or production)
	Env string `json:"env"`

	// Location where the system log will be saved
	FileLocation string `json:"fileLocation"`

	// Location where the tdr log will be saved
	// If empty, defaults to FileLocation + "/tdr.log"
	FileTDRLocation string `json:"fileTDRLocation"`

	// Maximum size of a single log file.
	// If the capacity reach, file will be saved but it will be renamed
	// with suffix the current date
	FileMaxSize int `json:"fileMaxSize"`

	// Maximum number of backup file that will not be deleted
	FileMaxBackup int `json:"fileMaxBackup"`

	// Number of days where the backup log will not be deleted
	FileMaxAge int `json:"fileMaxAge"`

	// Log will be printed in console if the value is true
	Stdout bool `json:"stdout"`

	// Log level (debug, info, warn, error). Defaults to info if not set.
	LogLevel zapcore.Level `json:"logLevel"`

	// Path to version file. If empty, defaults to "version.txt".
	// If set and file exists, will override AppVer.
	VersionFilePath string `json:"versionFilePath"`
}

func (*Config) Validate

func (c *Config) Validate()

Validate validates the Config and sets defaults.

type Log

type Log struct {
	// contains filtered or unexported fields
}

func (*Log) Debug

func (l *Log) Debug(msg string, fields ...zap.Field)

func (*Log) Error

func (l *Log) Error(msg string, err error, fields ...zap.Field)

func (*Log) Fatal

func (l *Log) Fatal(msg string, err error, fields ...zap.Field)

func (*Log) Info

func (l *Log) Info(msg string, fields ...zap.Field)

func (*Log) Panic

func (l *Log) Panic(msg string, err error, fields ...zap.Field)

func (*Log) Sync

func (l *Log) Sync() error

Sync flushes any buffered log entries. Applications should take care to call Sync before exiting to ensure all log entries are written.

func (*Log) TDR

func (l *Log) TDR(log LogModel)

func (*Log) Warn

func (l *Log) Warn(msg string, fields ...zap.Field)

func (*Log) WithContext added in v1.1.0

func (l *Log) WithContext(ctx context.Context) LoggerInterface

type LogModel

type LogModel struct {
	TraceID       string        `json:"traceId"`
	CorrelationID string        `json:"correlationId"`
	SrcIP         string        `json:"srcIp"`
	IP            string        `json:"ip"`
	Port          string        `json:"port"`
	Path          string        `json:"path"`
	Method        string        `json:"method"`
	Header        interface{}   `json:"header"`
	Request       interface{}   `json:"request"`
	StatusCode    string        `json:"statusCode"`
	HttpStatus    uint64        `json:"httpStatus"`
	Response      interface{}   `json:"response"`
	ResponseTime  time.Duration `json:"rt"`
	Error         interface{}   `json:"error"`
	OtherData     interface{}   `json:"otherData"`
}

type LoggerInterface

type LoggerInterface interface {
	WithContext(ctx context.Context) LoggerInterface
	Debug(message string, fields ...zap.Field)
	Info(message string, fields ...zap.Field)
	Warn(message string, fields ...zap.Field)
	Error(message string, err error, fields ...zap.Field)
	Fatal(message string, err error, fields ...zap.Field)
	Panic(message string, err error, fields ...zap.Field)
	TDR(tdr LogModel)
	Sync() error
}

func Load

func Load(config Config) LoggerInterface

Load constructs and returns a singleton logger instance. The logger is initialized only once on the first call.

func NewLogger

func NewLogger(conf Config) LoggerInterface

func WithContext added in v1.1.0

func WithContext(ctx context.Context) LoggerInterface

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL