Thanks to visit codestin.com
Credit goes to pkg.go.dev

log

package module
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: Dec 5, 2025 License: BSD-2-Clause Imports: 11 Imported by: 14

README

Go Log Utilities

CI Go Report Card Go Reference License

A Go library providing advanced logging utilities focused on log sampling and dynamic log level management, designed to integrate seamlessly with Google's glog library.

Features

  • Log Sampling: Reduce log volume with intelligent sampling mechanisms
  • Dynamic Log Level Management: Change log levels at runtime via HTTP endpoints
  • Multiple Sampler Types: Counter-based, time-based, glog-level-based, and custom samplers
  • Thread-Safe: All components are designed for concurrent use
  • Extensible: Factory pattern and interface-based design for easy customization

Installation

go get github.com/bborbe/log

Quick Start

Basic Log Sampling
package main

import (
    "time"
    "github.com/bborbe/log"
    "github.com/golang/glog"
)

func main() {
    // Sample every 10th log entry
    modSampler := log.NewSampleMod(10)
    
    // Sample once every 10 seconds
    timeSampler := log.NewSampleTime(10 * time.Second)
    
    // Use in your logging code
    if modSampler.IsSample() {
        glog.V(2).Infof("This will be logged every 10th time")
    }
    
    if timeSampler.IsSample() {
        glog.V(2).Infof("This will be logged at most once every 10 seconds")
    }
}
Dynamic Log Level Management
package main

import (
    "context"
    "net/http"
    "time"
    
    "github.com/bborbe/log"
    "github.com/golang/glog"
    "github.com/gorilla/mux"
)

func main() {
    ctx := context.Background()
    
    // Create log level setter that auto-resets after 5 minutes
    logLevelSetter := log.NewLogLevelSetter(
        glog.Level(1), // default level
        5*time.Minute, // auto-reset duration
    )
    
    // Set up HTTP handler for dynamic log level changes
    router := mux.NewRouter()
    router.Handle("/debug/loglevel/{level}", 
        log.NewSetLoglevelHandler(ctx, logLevelSetter))
    
    http.ListenAndServe(":8080", router)
}

Now you can change log levels at runtime:

curl http://localhost:8080/debug/loglevel/4

Sampler Types

ModSampler

Samples every Nth log entry based on a counter:

sampler := log.NewSampleMod(100) // Sample every 100th log
TimeSampler

Samples based on time intervals:

sampler := log.NewSampleTime(30 * time.Second) // Sample at most once per 30 seconds
GlogLevelSampler

Samples based on glog verbosity levels:

sampler := log.NewSamplerGlogLevel(3) // Sample when glog level >= 3
ListSampler

Combines multiple samplers with OR logic:

sampler := log.SamplerList{
    log.NewSampleTime(10 * time.Second),
    log.NewSamplerGlogLevel(4),
}
FuncSampler

Create custom sampling logic:

sampler := log.SamplerFunc(func() bool {
    // Your custom sampling logic
    return shouldSample()
})
TrueSampler

Always samples (useful for testing or special cases):

sampler := log.SamplerTrue{}

Factory Pattern

Use the factory pattern for dependency injection:

// Use the default factory
factory := log.DefaultSamplerFactory
sampler := factory.Sampler()

// Or create a custom factory
customFactory := log.SamplerFactoryFunc(func() log.Sampler {
    return log.NewSampleMod(50)
})

Full Example

Here's a complete, runnable example demonstrating multiple features working together in a production-like scenario:

package main

import (
    "context"
    "net/http"
    "time"

    "github.com/bborbe/log"
    "github.com/golang/glog"
    "github.com/gorilla/mux"
)

func main() {
    ctx := context.Background()

    // Combine time-based and level-based sampling
    // This will sample if EITHER condition is met (OR logic):
    // - At most once every 10 seconds, OR
    // - When glog verbosity is >= 4
    sampler := log.SamplerList{
        log.NewSampleTime(10 * time.Second),
        log.NewSamplerGlogLevel(4),
    }

    // Set up dynamic log level management
    // Default level: 1, auto-resets after 5 minutes
    logLevelSetter := log.NewLogLevelSetter(glog.Level(1), 5*time.Minute)

    // Create HTTP server with debug endpoint
    router := mux.NewRouter()
    router.Handle("/debug/loglevel/{level}",
        log.NewSetLoglevelHandler(ctx, logLevelSetter))

    // Start HTTP server in background
    go func() {
        if err := http.ListenAndServe(":8080", router); err != nil {
            glog.Fatalf("HTTP server failed: %v", err)
        }
    }()

    // Example application loop with sampled logging
    for i := 0; i < 1000; i++ {
        // High-frequency operation
        processItem(i)

        // Sampled logging to avoid log spam
        if sampler.IsSample() {
            glog.V(2).Infof("Processed item %d", i)
        }

        time.Sleep(100 * time.Millisecond)
    }
}

func processItem(i int) {
    // Your application logic here
    _ = i
}

You can change log levels at runtime:

# Increase verbosity to see more logs
curl http://localhost:8080/debug/loglevel/4

# Response: set loglevel to 4 completed
# Log level will auto-reset to 1 after 5 minutes

HTTP Log Level Management

The library provides built-in HTTP handlers for runtime log level changes:

  • Endpoint: GET/POST /debug/loglevel/{level}
  • Auto-reset: Automatically reverts to default level after specified duration
  • Thread-safe: Safe for concurrent access

Example integration with gorilla/mux:

router := mux.NewRouter()
logLevelSetter := log.NewLogLevelSetter(glog.Level(1), 5*time.Minute)
router.Handle("/debug/loglevel/{level}",
    log.NewSetLoglevelHandler(context.Background(), logLevelSetter))

Development

Running Tests
make test
Code Generation (Mocks)
make generate
Full Development Workflow
make precommit  # Format, test, lint, and check

Testing Framework

The library uses:

  • Ginkgo v2 for BDD-style testing
  • Gomega for assertions
  • Counterfeiter for mock generation

Testing

Testing Code That Uses Log Samplers

When testing code that uses this library, you have several options for controlling sampling behavior:

Option 1: Use TrueSampler for Always Sampling
import (
    "testing"
    "github.com/bborbe/log"
    "github.com/golang/glog"
)

func TestYourCode(t *testing.T) {
    // Use TrueSampler to ensure logs always sample during tests
    sampler := log.NewSamplerTrue()

    // Your test code here
    if sampler.IsSample() {
        glog.V(2).Infof("This will always log in tests")
    }
}
Option 2: Use SamplerFunc for Controlled Testing
func TestWithControlledSampling(t *testing.T) {
    shouldSample := true
    sampler := log.SamplerFunc(func() bool {
        return shouldSample
    })

    // Test when sampling is enabled
    if sampler.IsSample() {
        glog.V(2).Infof("Sampled log")
    }

    // Test when sampling is disabled
    shouldSample = false
    if sampler.IsSample() {
        t.Error("Should not sample")
    }
}
Option 3: Use Mock Samplers (Counterfeiter)
import (
    "testing"
    "github.com/bborbe/log/mocks"
)

func TestWithMockSampler(t *testing.T) {
    mockSampler := &mocks.LogSampler{}

    // Configure mock behavior
    mockSampler.IsSampleReturns(true)

    // Your test code using the mock
    result := mockSampler.IsSample()
    if !result {
        t.Error("Expected sampling to be enabled")
    }

    // Verify mock was called
    if mockSampler.IsSampleCallCount() != 1 {
        t.Error("Expected IsSample to be called once")
    }
}
Testing with Ginkgo/Gomega
import (
    . "github.com/onsi/ginkgo/v2"
    . "github.com/onsi/gomega"
    "github.com/bborbe/log"
)

var _ = Describe("YourComponent", func() {
    var sampler log.Sampler

    BeforeEach(func() {
        sampler = log.NewSamplerTrue()
    })

    It("should sample logs", func() {
        Expect(sampler.IsSample()).To(BeTrue())
    })
})

License

This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create your feature branch
  3. Add tests for your changes
  4. Run make precommit to ensure code quality
  5. Submit a pull request

Dependencies

Documentation

Overview

Package log provides logging utilities focused on log sampling and dynamic log level management.

This library extends Google's glog with sampling mechanisms to reduce log volume in production while maintaining debuggability through runtime log level changes.

Core Features

- Log Sampling: Reduce log volume using time-based, counter-based, or level-based sampling - Dynamic Log Levels: Change log verbosity at runtime via HTTP endpoints - Multiple Sampler Strategies: ModSampler, TimeSampler, GlogLevelSampler, TrueSampler - Sampler Composition: Combine multiple samplers using OR logic with SamplerList - Memory Monitoring: Track memory usage with periodic logging

Quick Start

Sample every 10th log entry:

sampler := log.NewSampleMod(10)
if sampler.IsSample() {
    glog.V(2).Infof("Sampled message")
}

Combine samplers to log if high verbosity OR every 10 seconds:

sampler := log.SamplerList{
    log.NewSamplerGlogLevel(4),
    log.NewSampleTime(10 * time.Second),
}
if sampler.IsSample() {
    glog.V(2).Infof("This logs if verbosity >= 4 OR at most once per 10 seconds")
}

Dynamic Log Level Management

Set up an HTTP endpoint to change log levels at runtime:

logLevelSetter := log.NewLogLevelSetter(glog.Level(1), 5*time.Minute)
router.Handle("/debug/loglevel/{level}", log.NewSetLoglevelHandler(ctx, logLevelSetter))

Change log level via HTTP:

curl http://localhost:8080/debug/loglevel/4

The log level will automatically reset after 5 minutes.

Sampler Types

ModSampler - Sample every Nth occurrence:

sampler := log.NewSampleMod(100)  // Sample every 100th call

TimeSampler - Sample at most once per time interval:

sampler := log.NewSampleTime(30 * time.Second)  // At most once per 30 seconds

GlogLevelSampler - Sample based on glog verbosity:

sampler := log.NewSamplerGlogLevel(3)  // Sample if -v=3 or higher

TrueSampler - Always sample (useful for debugging):

sampler := log.NewSamplerTrue()  // Always returns true

SamplerList - Combine samplers with OR logic:

sampler := log.SamplerList{
    log.NewSampleMod(1000),
    log.NewSampleTime(5 * time.Minute),
}  // Sample every 1000th call OR at most once per 5 minutes

Memory Monitoring

Monitor memory usage periodically:

monitor := log.NewMemoryMonitor()
monitor.LogMemoryUsage("startup")
// ... do work ...
monitor.LogMemoryUsage("after-processing")

Or use the convenience function:

log.MemoryStats("checkpoint-1")

Thread Safety

All sampler implementations are thread-safe and can be used concurrently from multiple goroutines without additional synchronization.

Testing

Use TrueSampler for testing to ensure consistent behavior:

func TestYourCode(t *testing.T) {
    sampler := log.NewSamplerTrue()
    // Test code that always samples
}

Or use SamplerFunc for controlled testing:

shouldSample := true
sampler := log.SamplerFunc(func() bool {
    return shouldSample
})

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MemoryStats added in v1.4.1

func MemoryStats(prefix string)

MemoryStats is a helper that reads memory stats and logs them with the given prefix

func NewSetLoglevelHandler

func NewSetLoglevelHandler(ctx context.Context, logLevelSetter LogLevelSetter) http.Handler

NewSetLoglevelHandler creates an HTTP handler for dynamically changing log levels via REST API. The handler expects a URL path variable named "level" containing the desired log level.

Usage with gorilla/mux:

router := mux.NewRouter()
logLevelSetter := log.NewLogLevelSetter(glog.Level(1), 5*time.Minute)
router.Handle("/debug/loglevel/{level}", log.NewSetLoglevelHandler(ctx, logLevelSetter))

Example HTTP requests:

GET  /debug/loglevel/4  - Set log level to 4
POST /debug/loglevel/2  - Set log level to 2

Parameters:

  • ctx: Context for the log level setter operations
  • logLevelSetter: The LogLevelSetter implementation to use for changing levels

Returns an http.Handler that can be registered with any HTTP router.

Types

type LogLevelSetter

type LogLevelSetter interface {
	// Set changes the current log level to the specified value.
	// The implementation may automatically reset to a default level after a timeout.
	Set(ctx context.Context, logLevel glog.Level) error
}

LogLevelSetter provides an interface for dynamically changing log levels at runtime. This is particularly useful for debugging production systems without restarts.

func NewLogLevelSetter

func NewLogLevelSetter(
	defaultLoglevel glog.Level,
	autoResetDuration time.Duration,
) LogLevelSetter

NewLogLevelSetter creates a new LogLevelSetter that automatically resets to the default log level after the specified duration.

Parameters:

  • defaultLoglevel: The log level to reset to after the auto-reset duration
  • autoResetDuration: How long to wait before automatically resetting the log level

The setter is thread-safe and can handle concurrent log level changes.

type LogLevelSetterFunc

type LogLevelSetterFunc func(ctx context.Context, logLevel glog.Level) error

LogLevelSetterFunc is a function type that implements the LogLevelSetter interface. It allows regular functions to be used as log level setters.

func (LogLevelSetterFunc) Set

func (l LogLevelSetterFunc) Set(ctx context.Context, logLevel glog.Level) error

Set implements the LogLevelSetter interface by calling the underlying function.

type MemoryMonitor added in v1.4.0

type MemoryMonitor interface {
	LogMemoryUsage(name string)
	LogMemoryUsagef(format string, args ...interface{})
	LogMemoryUsageOnStart()
	LogMemoryUsageOnEnd()
}

MemoryMonitor provides memory usage monitoring functionality

func NewMemoryMonitor added in v1.4.0

func NewMemoryMonitor(logInterval time.Duration) MemoryMonitor

NewMemoryMonitor creates a new memory monitor that logs memory usage at specified intervals

type Sampler

type Sampler interface {
	// IsSample returns true if the current log entry should be emitted.
	// Implementations may use various strategies such as counters, time intervals,
	// or log levels to determine sampling behavior.
	IsSample() bool
}

Sampler defines an interface for log sampling decisions. It allows reducing log volume by selectively determining which log entries should be emitted.

Example usage:

sampler := log.NewSampleMod(10)
if sampler.IsSample() {
    glog.V(2).Infof("This message is sampled")
}

func NewSampleMod

func NewSampleMod(mod uint64) Sampler

NewSampleMod creates a counter-based sampler that samples every Nth log entry. It maintains an internal counter that increments on each IsSample() call, returning true when the counter is divisible by the modulus value.

Example:

sampler := log.NewSampleMod(10) // Sample every 10th log entry
for i := 0; i < 100; i++ {
    if sampler.IsSample() {
        glog.V(2).Infof("Log entry %d", i) // This will be called 10 times
    }
}

Parameters:

  • mod: The modulus value for sampling (must be > 0). Every mod-th call will return true.

The sampler is thread-safe and can be used concurrently from multiple goroutines.

func NewSampleTime

func NewSampleTime(duration stdtime.Duration) Sampler

NewSampleTime creates a time-based sampler that limits log sampling to a maximum frequency. It ensures that IsSample() returns true at most once per the specified duration.

Example:

sampler := log.NewSampleTime(5 * time.Second) // Sample at most once every 5 seconds
for {
    if sampler.IsSample() {
        glog.V(2).Infof("This message appears at most once every 5 seconds")
    }
    time.Sleep(100 * time.Millisecond)
}

Parameters:

  • duration: The minimum time interval between samples. Must be > 0.

The sampler is thread-safe and can be used concurrently from multiple goroutines. It uses github.com/bborbe/time for consistent time handling across the library.

func NewSamplerGlogLevel

func NewSamplerGlogLevel(level glog.Level) Sampler

NewSamplerGlogLevel creates a sampler that samples based on the current glog verbosity level. It returns true when the current glog verbosity is at or above the specified level. This allows for dynamic sampling based on the runtime log level configuration.

Example:

// Sample when glog verbosity is 3 or higher
sampler := log.NewSamplerGlogLevel(3)
if sampler.IsSample() {
    glog.V(2).Infof("This logs when -v=3 or higher is set")
}

// Combine with other samplers for conditional high-verbosity logging
conditionalSampler := log.SamplerList{
    log.NewSamplerGlogLevel(4),           // Always sample at high verbosity
    log.NewSampleTime(30 * time.Second),  // Otherwise, sample every 30 seconds
}

Parameters:

  • level: The minimum glog verbosity level required for sampling

This sampler has no internal state and queries glog's current verbosity setting on each call, making it inherently thread-safe and responsive to runtime changes.

func NewSamplerTrue

func NewSamplerTrue() Sampler

NewSamplerTrue creates a sampler that always returns true. This is useful for testing, debugging, or situations where you want to temporarily disable sampling and log everything.

Example:

// For debugging, log everything regardless of other sampling rules
debugSampler := log.NewSamplerTrue()
if debugSampler.IsSample() {
    glog.V(4).Infof("Debug message - always logged")
}

// Use in tests to ensure all log paths are exercised
testSampler := log.NewSamplerTrue()

This sampler has no internal state and is inherently thread-safe.

type SamplerFactory

type SamplerFactory interface {
	// Sampler creates and returns a new Sampler instance.
	Sampler() Sampler
}

SamplerFactory provides a factory pattern for creating Sampler instances. This interface enables dependency injection and makes testing easier.

For testing, use log.DefaultSamplerFactory instead of mocking this interface.

var DefaultSamplerFactory SamplerFactory = SamplerFactoryFunc(func() Sampler {
	return SamplerList{
		NewSampleTime(10 * time.Second),
		NewSamplerGlogLevel(4),
	}
})

DefaultSamplerFactory provides a default sampler configuration that combines time-based sampling (every 10 seconds) with glog level sampling (level 4+).

type SamplerFactoryFunc

type SamplerFactoryFunc func() Sampler

SamplerFactoryFunc is a function type that implements the SamplerFactory interface. It allows regular functions to be used as sampler factories.

func (SamplerFactoryFunc) Sampler

func (s SamplerFactoryFunc) Sampler() Sampler

Sampler implements the SamplerFactory interface by calling the underlying function.

type SamplerFunc

type SamplerFunc func() bool

SamplerFunc is a function type that implements the Sampler interface. It allows regular functions to be used as samplers, enabling custom sampling logic.

Example:

// Custom sampler that samples based on random chance
randomSampler := log.SamplerFunc(func() bool {
    return rand.Float64() < 0.1 // 10% chance
})

// Custom sampler with external state
counter := 0
customSampler := log.SamplerFunc(func() bool {
    counter++
    return counter%3 == 0 // Every 3rd call
})

func (SamplerFunc) IsSample

func (s SamplerFunc) IsSample() bool

IsSample implements the Sampler interface by calling the underlying function.

type SamplerList

type SamplerList []Sampler

SamplerList combines multiple samplers using OR logic. It returns true if ANY of the contained samplers returns true. This allows for complex sampling strategies by combining different sampling methods.

Example:

// Sample if either time-based OR mod-based condition is met
sampler := log.SamplerList{
    log.NewSampleTime(30 * time.Second),  // At most once per 30 seconds
    log.NewSampleMod(100),                // Every 100th call
}

// Sample if high log level OR time interval OR random chance
complexSampler := log.SamplerList{
    log.NewSamplerGlogLevel(4),           // When glog level >= 4
    log.NewSampleTime(10 * time.Second),  // At most once per 10 seconds
    log.SamplerFunc(func() bool {         // 5% random chance
        return rand.Float64() < 0.05
    }),
}

The samplers are evaluated in order and the first one to return true causes the entire list to return true (short-circuit evaluation).

func (SamplerList) IsSample

func (s SamplerList) IsSample() bool

IsSample implements the Sampler interface using OR logic across all contained samplers. It returns true if any sampler in the list returns true.

Directories

Path Synopsis
Code generated by counterfeiter.
Code generated by counterfeiter.

Jump to

Keyboard shortcuts

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