### WriteAsyncer with Callback Example Source: https://github.com/shengyanli1982/law/blob/main/README.md Demonstrates creating a WriteAsyncer with a custom callback for handling write failures. Ensure the callback implementation is provided. ```go package main import ( "os" "time" "strconv" law "github.com/shengyanli1982/law" ) // callback 是一个实现了 law.Callback 接口的结构体 // callback is a struct that implements the law.Callback interface type callback struct{} // OnWriteFailed 是当数据写入失败时的回调函数 // OnWriteFailed is the callback function when data writing fails func (c *callback) OnWriteFailed(b []byte, err error) { fmt.Printf("write failed msg: %s, err: %v\n", string(b), err) // 输出写入失败的消息和错误 } func main() { // 创建一个新的配置,并设置回调函数 // Create a new configuration and set the callback function conf := NewConfig().WithCallback(&callback{}) // 使用 os.Stdout 和配置创建一个新的 WriteAsyncer 实例 // Create a new WriteAsyncer instance using os.Stdout and the configuration w := NewWriteAsyncer(os.Stdout, conf) // 使用 defer 语句确保在 main 函数退出时停止 WriteAsyncer // Use a defer statement to ensure that WriteAsyncer is stopped when the main function exits defer w.Stop() // 循环 10 次,每次都将一个数字写入 WriteAsyncer // Loop 10 times, each time write a number to WriteAsyncer for i := 0; i < 10; i++ { _, _ = w.Write([]byte(strconv.Itoa(i))) // 将当前的数字写入 WriteAsyncer } // 等待 1 秒,以便我们可以看到 WriteAsyncer 的输出 // Wait for 1 second so we can see the output of WriteAsyncer time.Sleep(time.Second) } ``` -------------------------------- ### Install LAW Package Source: https://github.com/shengyanli1982/law/blob/main/README.md Use this command to add the LAW package to your Go project. ```bash go get github.com/shengyanli1982/law ``` -------------------------------- ### Write logs to klog asynchronously using LAW Source: https://github.com/shengyanli1982/law/blob/main/README.md This example shows how to set up LAW's WriteAsyncer to capture klog output. Ensure LAW is imported and klog's output is redirected to the WriteAsyncer. A defer statement is used to stop the asyncer gracefully. ```go package main import ( "os" "time" law "github.com/shengyanli1982/law" "k8s.io/klog/v2" ) func main() { // 使用 os.Stdout 创建一个新的 WriteAsyncer 实例 // Create a new WriteAsyncer instance using os.Stdout aw := law.NewWriteAsyncer(os.Stdout, nil) // 使用 defer 语句确保在 main 函数退出时停止 WriteAsyncer // Use a defer statement to ensure that WriteAsyncer is stopped when the main function exits defer aw.Stop() // 将 klog 的输出设置为我们创建的 WriteAsyncer // Set the output of klog to the WriteAsyncer we created klog.SetOutput(aw) // 循环 10 次,每次都使用 klog 输出一个数字 // Loop 10 times, each time output a number using klog for i := 0; i < 10; i++ { klog.Info(i) // 输出当前的数字 } // 等待 3 秒,以便我们可以看到 klog 的输出 // Wait for 3 seconds so we can see the output of klog time.Sleep(3 * time.Second) } ``` -------------------------------- ### Run Go Benchmarks Source: https://github.com/shengyanli1982/law/blob/main/README_CN.md Execute benchmarks for LAW's logging implementations using `go test`. This command specifically targets benchmark functions starting with 'Benchmark' and includes memory profiling. ```bash $ go test -benchmem -run=^$ -bench ^Benchmark* github.com/shengyan1982/law/benchmark ``` -------------------------------- ### Write logs to zerolog asynchronously using LAW Source: https://github.com/shengyanli1982/law/blob/main/README.md This example demonstrates integrating LAW with zerolog for asynchronous logging. A WriteAsyncer is created and used to initialize a zerolog logger. The logger is configured to include timestamps. Ensure LAW and zerolog are imported. ```go package main import ( "os" "time" "github.com/rs/zerolog" law "github.com/shengyanli1982/law" ) func main() { // 使用 os.Stdout 创建一个新的 WriteAsyncer 实例 // Create a new WriteAsyncer instance using os.Stdout aw := law.NewWriteAsyncer(os.Stdout, nil) // 使用 defer 语句确保在 main 函数退出时停止 WriteAsyncer // Use a defer statement to ensure that WriteAsyncer is stopped when the main function exits defer aw.Stop() // 使用 WriteAsyncer 创建一个新的 zerolog.Logger 实例,并添加时间戳 // Create a new zerolog.Logger instance using WriteAsyncer and add a timestamp log := zerolog.New(aw).With().Timestamp().Logger() // 循环 10 次,每次都使用 log 输出一个数字和一条消息 // Loop 10 times, each time output a number and a message using log for i := 0; i < 10; i++ { log.Info().Int("i", i).Msg("hello") // 输出当前的数字和一条消息 } // 等待 3 秒,以便我们可以看到 log 的输出 // Wait for 3 seconds so we can see the output of log time.Sleep(3 * time.Second) } ``` -------------------------------- ### WriteAsyncer with Custom Buffer Size Source: https://github.com/shengyanli1982/law/blob/main/README.md Configures the WriteAsyncer with a specific buffer size using WithBufferSize. The default buffer size is 2k; this example sets it to 1024 bytes. ```go package main import ( "os" "time" "strconv" law "github.com/shengyanli1982/law" ) func main() { // 创建一个新的配置,并设置缓冲区大小为 1024 // Create a new configuration and set the buffer size to 1024 conf := NewConfig().WithBufferSize(1024) // 使用 os.Stdout 和配置创建一个新的 WriteAsyncer 实例 // Create a new WriteAsyncer instance using os.Stdout and the configuration w := NewWriteAsyncer(os.Stdout, conf) // 使用 defer 语句确保在 main 函数退出时停止 WriteAsyncer // Use a defer statement to ensure that WriteAsyncer is stopped when the main function exits defer w.Stop() // 循环 10 次,每次都将一个数字写入 WriteAsyncer // Loop 10 times, each time write a number to WriteAsyncer for i := 0; i < 10; i++ { _, _ = w.Write([]byte(strconv.Itoa(i))) // 将当前的数字写入 WriteAsyncer } // 等待 1 秒,以便我们可以看到 WriteAsyncer 的输出 // Wait for 1 second so we can see the output of WriteAsyncer time.Sleep(time.Second) } ``` -------------------------------- ### WriteAsyncer with Heartbeat and Idle Timeout Source: https://github.com/shengyanli1982/law/blob/main/README.md Customizes the heartbeat interval and idle timeout for the WriteAsyncer. The default heartbeat is 500ms and idle timeout is 5s; this example sets them to 200ms and 3s respectively. ```go package main import ( "os" "time" "strconv" law "github.com/shengyanli1982/law" ) func main() { // 创建一个新的配置,并设置心跳间隔和闲置超时 // Create a new configuration and set the heartbeat interval and idle timeout conf := NewConfig(). WithHeartbeatInterval(200 * time.Millisecond). WithIdleTimeout(3 * time.Second) // 使用 os.Stdout 和配置创建一个新的 WriteAsyncer 实例 // Create a new WriteAsyncer instance using os.Stdout and the configuration w := NewWriteAsyncer(os.Stdout, conf) // 使用 defer 语句确保在 main 函数退出时停止 WriteAsyncer // Use a defer statement to ensure that WriteAsyncer is stopped when the main function exits defer w.Stop() // 循环 10 次,每次都将一个数字写入 WriteAsyncer // Loop 10 times, each time write a number to WriteAsyncer for i := 0; i < 10; i++ { _, _ = w.Write([]byte(strconv.Itoa(i))) // 将当前的数字写入 WriteAsyncer } // 等待 1 秒,以便我们可以看到 WriteAsyncer 的输出 // Wait for 1 second so we can see the output of WriteAsyncer time.Sleep(time.Second) } ``` -------------------------------- ### HTTP Server with Zap and LAW Integration Source: https://github.com/shengyanli1982/law/blob/main/README_CN.md Integrates LAW with Zap logger to create an HTTP server. This example demonstrates how to configure Zap's encoder and writer, then registers a handler to log messages on incoming requests. The server listens on port 8080. ```go package main import ( "net/http" "os" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) func main() { // 创建一个zapcore.EncoderConfig,用于配置日志编码器 // Create a zapcore.EncoderConfig to configure the log encoder encoderCfg := zapcore.EncoderConfig{ MessageKey: "msg", // 消息的键名,Key name for the message LevelKey: "level", // 日志级别的键名,Key name for the log level NameKey: "logger", // 记录器名称的键名,Key name for the logger name EncodeLevel: zapcore.LowercaseLevelEncoder, // 日志级别的编码器,Encoder for the log level EncodeTime: zapcore.ISO8601TimeEncoder, // 时间的编码器,Encoder for the time EncodeDuration: zapcore.StringDurationEncoder, // 持续时间的编码器,Encoder for the duration } // 创建一个zapcore.WriteSyncer,将日志写入标准输出 // Create a zapcore.WriteSyncer that writes logs to the standard output zapSyncWriter := zapcore.AddSync(os.Stdout) // 创建一个zapcore.Core,使用JSON编码器和标准输出 // Create a zapcore.Core using the JSON encoder and the standard output zapCore := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), zapSyncWriter, zapcore.DebugLevel) // 创建一个zap.Logger,使用上面创建的zapcore.Core // Create a zap.Logger using the zapcore.Core created above zapLogger := zap.New(zapCore) // 注册一个HTTP处理函数,当访问"/"时,记录一条信息日志 // Register an HTTP handler function, when accessing "/", log an info message http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { zapLogger.Info("hello") }) // 启动HTTP服务器,监听8080端口 // Start the HTTP server, listen on port 8080 _ = http.ListenAndServe(":8080", nil) } ``` -------------------------------- ### NewWriteAsyncer Source: https://context7.com/shengyanli1982/law/llms.txt `NewWriteAsyncer` wraps any `io.Writer` and starts the background poller goroutine. Passing `nil` for either argument uses safe defaults (`os.Stdout` and `DefaultConfig()`). The returned `*WriteAsyncer` satisfies `io.Writer` and can be passed directly to any logger. ```APIDOC ## NewWriteAsyncer ### Description Wraps an `io.Writer` to enable asynchronous, buffered I/O operations. It starts a background poller goroutine to handle flushing. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **writer** (`io.Writer`) - The underlying writer to wrap. If nil, `os.Stdout` is used. - **config** (`*Config`) - Configuration for the asynchronous writer. If nil, `DefaultConfig()` is used. ### Returns - `*WriteAsyncer` - A new asynchronous writer that implements `io.Writer`. ### Request Example ```go package main import ( "os" "strconv" "time" law "github.com/shengyanli1982/law" ) func main() { // Wrap os.Stdout with the async writer using default config w := law.NewWriteAsyncer(os.Stdout, nil) defer w.Stop() for i := 0; i < 10; i++ { _, err := w.Write([]byte("line " + strconv.Itoa(i) + "\n")) if err != nil { panic(err) } } // Allow the background poller time to flush buffered data time.Sleep(time.Second) // Output: lines 0-9 written to stdout asynchronously } ``` ``` -------------------------------- ### Build a configuration with defaults Source: https://context7.com/shengyanli1982/law/llms.txt Returns a `*Config` with default settings. All fields are set via fluent `WithXXX` methods and the struct is validated on `NewWriteAsyncer`. Custom callbacks can be registered to log write failures. ```go package main import ( "fmt" "os" "time" law "github.com/shengyanli1982/law" ) // customCallback logs any write failure to stderr type customCallback struct{} func (c *customCallback) OnWriteFailed(content []byte, reason error) { fmt.Fprintf(os.Stderr, "write failed: content=%q err=%v\n", content, reason) } func main() { conf := law.NewConfig(). WithBufferSize(4096). // 4 KB I/O buffer WithHeartbeatInterval(200*time.Millisecond). // poll every 200 ms WithIdleTimeout(2*time.Second). // flush after 2 s idle WithCallback(&customCallback{}) // called on flush error w := law.NewWriteAsyncer(os.Stdout, conf) defer w.Stop() _, _ = w.Write([]byte("configured writer\n")) time.Sleep(500 * time.Millisecond) } ``` -------------------------------- ### Basic Usage of LAW Writer Source: https://github.com/shengyanli1982/law/blob/main/README.md Demonstrates creating a LAW writer with default configuration, writing data, and ensuring it's stopped. Waits for a second to observe output. ```go package main import ( "os" "time" "strconv" law "github.com/shengyanli1982/law" ) func main() { // 创建一个新的配置 // Create a new configuration conf := NewConfig() // 使用 os.Stdout 和配置创建一个新的 WriteAsyncer 实例 // Create a new WriteAsyncer instance using os.Stdout and the configuration w := NewWriteAsyncer(os.Stdout, conf) // 使用 defer 语句确保在 main 函数退出时停止 WriteAsyncer // Use a defer statement to ensure that WriteAsyncer is stopped when the main function exits defer w.Stop() // 循环 10 次,每次都将一个数字写入 WriteAsyncer // Loop 10 times, each time write a number to WriteAsyncer for i := 0; i < 10; i++ { _, _ = w.Write([]byte(strconv.Itoa(i))) // 将当前的数字写入 WriteAsyncer } // 等待 1 秒,以便我们可以看到 WriteAsyncer 的输出 // Wait for 1 second so we can see the output of WriteAsyncer time.Sleep(time.Second) } ``` -------------------------------- ### Create and Use Async Writer with Default Config Source: https://context7.com/shengyanli1982/law/llms.txt Wrap os.Stdout with the async writer using default configuration. Ensure to call Stop() to clean up resources. The background poller needs time to flush data. ```go package main import ( "os" "strconv" "time" law "github.com/shengyanli1982/law" ) func main() { // Wrap os.Stdout with the async writer using default config w := law.NewWriteAsyncer(os.Stdout, nil) defer w.Stop() for i := 0; i < 10; i++ { _, err := w.Write([]byte("line " + strconv.Itoa(i) + "\n")) if err != nil { panic(err) } } // Allow the background poller time to flush buffered data time.Sleep(time.Second) // Output: lines 0-9 written to stdout asynchronously } ``` -------------------------------- ### Integrate LAW with Klog Source: https://context7.com/shengyanli1982/law/llms.txt Redirect Kubernetes' klog through the async writer by calling `klog.SetOutput(aw)`. Remember to defer `Stop()` on the LAW writer. ```go package main import ( "os" "time" law "github.com/shengyanli1982/law" "k8s.io/klog/v2" ) func main() { aw := law.NewWriteAsyncer(os.Stdout, nil) defer aw.Stop() klog.SetOutput(aw) // redirect klog to LAW for i := 0; i < 10; i++ { klog.Infof("event index=%d", i) } time.Sleep(3 * time.Second) // Output: // I... demo.go:XX] event index=0 // ... } ``` -------------------------------- ### Asynchronous Logging with LAW and Zap Source: https://github.com/shengyanli1982/law/blob/main/README.md Demonstrates how to integrate LAW with Zap for asynchronous log writing. Ensure `aw.Stop()` is called to gracefully shut down the writer. ```go package main import ( "os" "strconv" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" law "github.com/shengyanli1982/law" ) func main() { // 使用 os.Stdout 创建一个新的 WriteAsyncer 实例 // Create a new WriteAsyncer instance using os.Stdout aw := law.NewWriteAsyncer(os.Stdout, nil) // 使用 defer 语句确保在 main 函数退出时停止 WriteAsyncer // Use a defer statement to ensure that WriteAsyncer is stopped when the main function exits defer aw.Stop() // 创建一个 zapcore.EncoderConfig 实例,用于配置 zap 的编码器 // Create a zapcore.EncoderConfig instance to configure the encoder of zap encoderCfg := zapcore.EncoderConfig{ MessageKey: "msg", // 消息的键名 LevelKey: "level", // 级别的键名 NameKey: "logger", // 记录器名的键名 EncodeLevel: zapcore.LowercaseLevelEncoder, // 级别的编码器 EncodeTime: zapcore.ISO8601TimeEncoder, // 时间的编码器 EncodeDuration: zapcore.StringDurationEncoder, // 持续时间的编码器 } // 使用 WriteAsyncer 创建一个 zapcore.WriteSyncer 实例 // Create a zapcore.WriteSyncer instance using WriteAsyncer zapAsyncWriter := zapcore.AddSync(aw) // 使用编码器配置和 WriteSyncer 创建一个 zapcore.Core 实例 // Create a zapcore.Core instance using the encoder configuration and WriteSyncer zapCore := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), zapAsyncWriter, zapcore.DebugLevel) // 使用 Core 创建一个 zap.Logger 实例 // Create a zap.Logger instance using Core zapLogger := zap.New(zapCore) // 循环 10 次,每次都使用 zapLogger 输出一个数字 // Loop 10 times, each time output a number using zapLogger for i := 0; i < 10; i++ { zapLogger.Info(strconv.Itoa(i)) // 输出当前的数字 } // 等待 3 秒,以便我们可以看到 zapLogger 的输出 // Wait for 3 seconds so we can see the output of zapLogger time.Sleep(3 * time.Second) } ``` -------------------------------- ### NewConfig / DefaultConfig Source: https://context7.com/shengyanli1982/law/llms.txt Initializes a new configuration with default settings for buffer size, heartbeat, and idle timeout. These defaults can be overridden using fluent `WithXXX` methods. ```APIDOC ## NewConfig / DefaultConfig — Build a configuration `NewConfig()` (alias `DefaultConfig()`) returns a `*Config` with defaults: buffer size 2 KB, heartbeat 500 ms, idle timeout 5 s, no callback, and the built-in MPSC queue. All fields are set via fluent `WithXXX` methods and the struct is validated on `NewWriteAsyncer`. ``` -------------------------------- ### Integrate LAW with Zerolog Source: https://context7.com/shengyanli1982/law/llms.txt Pass the `*WriteAsyncer` directly to `zerolog.New()` to enable asynchronous logging. Ensure `Stop()` is deferred to flush buffered logs. ```go package main import ( "os" "time" "github.com/rs/zerolog" law "github.com/shengyanli1982/law" ) func main() { aw := law.NewWriteAsyncer(os.Stdout, nil) defer aw.Stop() log := zerolog.New(aw).With().Timestamp().Logger() for i := 0; i < 10; i++ { log.Info().Int("i", i).Msg("hello") } time.Sleep(3 * time.Second) // Output: // {"level":"info","i":0,"time":"...","message":"hello"} // ... } ``` -------------------------------- ### SyncWriter HTTP Server with Zap Source: https://github.com/shengyanli1982/law/blob/main/README.md Sets up an HTTP server that logs messages to os.Stdout using Zap's JSON encoder and a synchronous WriteSyncer. Suitable for basic logging where immediate writes are acceptable. ```go package main import ( "net/http" "os" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) func main() { // 创建一个zapcore.EncoderConfig,用于配置日志编码器 // Create a zapcore.EncoderConfig to configure the log encoder encoderCfg := zapcore.EncoderConfig{ MessageKey: "msg", // 消息的键名,Key name for the message LevelKey: "level", // 日志级别的键名,Key name for the log level NameKey: "logger", // 记录器名称的键名,Key name for the logger name EncodeLevel: zapcore.LowercaseLevelEncoder, // 日志级别的编码器,Encoder for the log level EncodeTime: zapcore.ISO8601TimeEncoder, // 时间的编码器,Encoder for the time EncodeDuration: zapcore.StringDurationEncoder, // 持续时间的编码器,Encoder for the duration } // 创建一个zapcore.WriteSyncer,将日志写入标准输出 // Create a zapcore.WriteSyncer that writes logs to the standard output zapSyncWriter := zapcore.AddSync(os.Stdout) // 创建一个zapcore.Core,使用JSON编码器和标准输出 // Create a zapcore.Core using the JSON encoder and the standard output zapCore := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), zapSyncWriter, zapcore.DebugLevel) // 创建一个zap.Logger,使用上面创建的zapcore.Core // Create a zap.Logger using the zapcore.Core created above zapLogger := zap.New(zapCore) // 注册一个HTTP处理函数,当访问"/"时,记录一条信息日志 // Register an HTTP handler function, when accessing "/", log an info message http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { zapLogger.Info("hello") }) // 启动HTTP服务器,监听8080端口 // Start the HTTP server, listen on port 8080 _ = http.ListenAndServe(":8080", nil) } ``` ```bash #!/bin/bash times=0 while [ $times -lt 5 ] do wrk -c 500 -t 10 http://127.0.0.1:8080 times=$[$times+1] sleep 2 echo "--------------------------------------" done ``` -------------------------------- ### Asynchronous Logging with LAW and Logrus Source: https://github.com/shengyanli1982/law/blob/main/README.md Demonstrates how to integrate LAW with Logrus for asynchronous log writing. Ensure `aw.Stop()` is called to gracefully shut down the writer. ```go package main import ( "os" "time" law "github.com/shengyanli1982/law" "github.com/sirupsen/logrus" ) func main() { // 使用 os.Stdout 创建一个新的 WriteAsyncer 实例 // Create a new WriteAsyncer instance using os.Stdout aw := law.NewWriteAsyncer(os.Stdout, nil) // 使用 defer 语句确保在 main 函数退出时停止 WriteAsyncer // Use a defer statement to ensure that WriteAsyncer is stopped when the main function exits defer aw.Stop() // 将 logrus 的输出设置为我们创建的 WriteAsyncer // Set the output of logrus to the WriteAsyncer we created logrus.SetOutput(aw) // 循环 10 次,每次都使用 logrus 输出一个数字 // Loop 10 times, each time output a number using logrus for i := 0; i < 10; i++ { logrus.Info(i) // 输出当前的数字 } // 等待 3 秒,以便我们可以看到 logrus 的输出 // Wait for 3 seconds so we can see the output of logrus time.Sleep(3 * time.Second) } ``` -------------------------------- ### Implement Custom Queue for LAW Source: https://context7.com/shengyanli1982/law/llms.txt Implement the `Queue` interface with `Push(*bytes.Buffer)` and `Pop() *bytes.Buffer` methods. `Pop` should return `nil` when empty. The implementation must be safe for concurrent `Push` calls and sequential `Pop` calls. ```go package main import ( "bytes" "os" "sync" "time" law "github.com/shengyanli1982/law" ) // simpleQueue is a trivial mutex-protected slice queue type simpleQueue struct { mu sync.Mutex items []*bytes.Buffer } func (q *simpleQueue) Push(v *bytes.Buffer) { q.mu.Lock() q.items = append(q.items, v) q.mu.Unlock() } func (q *simpleQueue) Pop() *bytes.Buffer { q.mu.Lock() defer q.mu.Unlock() if len(q.items) == 0 { return nil } v := q.items[0] q.items = q.items[1:] return v } func main() { conf := law.NewConfig().WithQueue(&simpleQueue{}) w := law.NewWriteAsyncer(os.Stdout, conf) defer w.Stop() _, _ = w.Write([]byte("custom queue\n")) time.Sleep(time.Second) } ``` -------------------------------- ### MPSCQueue: Unbounded and Bounded Queues Source: https://context7.com/shengyanli1982/law/llms.txt Demonstrates the usage of `NewMPSCQueue` for an unbounded queue and `NewMPSCQueueWithLimits` for a queue with item-count and byte-size caps. `Push` blocks when limits are reached, while `Pop` is non-blocking. ```go package main import ( "bytes" "fmt" iq "github.com/shengyanli1982/law/internal/queue" ) func main() { // Unbounded queue q := iq.NewMPSCQueue[*bytes.Buffer]() q.Push(bytes.NewBufferString("hello")) q.Push(bytes.NewBufferString("world")) fmt.Println(q.Len()) // 2 fmt.Println(q.Pop().String()) // hello fmt.Println(q.Len()) // 1 // Bounded queue: max 2 items, max 1 KB bq := iq.NewMPSCQueueWithLimits[*bytes.Buffer](2, 1024) bq.Push(bytes.NewBufferString("a")) bq.Push(bytes.NewBufferString("b")) // third Push would block until one item is consumed v := bq.Pop() fmt.Println(v.String()) // a bq.Push(bytes.NewBufferString("c")) // now fits fmt.Println(bq.Len()) // 2 } ``` -------------------------------- ### HTTP Server with Async Logging using Zap Source: https://context7.com/shengyanli1982/law/llms.txt Replace the synchronous writer in an HTTP handler with a LAW writer to decouple log I/O from request processing. This reduces tail latency under high concurrency. Ensure `Stop()` is deferred. ```go package main import ( "net/http" "os" "go.uber.org/zap" "go.uber.org/zap/zapcore" law "github.com/shengyanli1982/law" ) func main() { aw := law.NewWriteAsyncer(os.Stdout, nil) defer aw.Stop() encoderCfg := zapcore.EncoderConfig{ MessageKey: "msg", LevelKey: "level", NameKey: "logger", EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, } core := zapcore.NewCore( zapcore.NewJSONEncoder(encoderCfg), zapcore.AddSync(aw), zapcore.DebugLevel, ) logger := zap.New(core) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { logger.Info("request", zap.String("method", r.Method), zap.String("path", r.URL.Path)) w.WriteHeader(http.StatusOK) }) // Benchmark with: wrk -c 500 -t 10 http://127.0.0.1:8080 _ = http.ListenAndServe(":8080", nil) } ``` -------------------------------- ### Use Custom Bounded Queue with LAW Source: https://context7.com/shengyanli1982/law/llms.txt Replaces the default MPSC queue with a custom bounded queue to manage queue size and memory usage. Ensure the custom queue implements the `Queue` interface. ```go package main import ( "bytes" "fmt" "os" "time" law "github.com/shengyanli1982/law" iq "github.com/shengyanli1982/law/internal/queue" ) func main() { // Built-in bounded MPSC queue: max 1000 items, max 512 KB bytes boundedQ := iq.NewMPSCQueueWithLimits[*bytes.Buffer](1000, 512*1024) conf := law.NewConfig().WithQueue(boundedQ) w := law.NewWriteAsyncer(os.Stdout, conf) defer w.Stop() _, _ = w.Write([]byte("bounded queue write\n")) time.Sleep(time.Second) fmt.Println("queue length:", boundedQ.Len()) } ``` -------------------------------- ### Benchmark results for LAW and other loggers Source: https://github.com/shengyanli1982/law/blob/main/README.md This output shows benchmark results for various logging configurations, including LAW's asynchronous writers and comparisons with zap. The results indicate performance characteristics under different parallel and synchronous write scenarios. ```bash $ go test -benchmem -run=^$ -bench ^Benchmark* github.com/shengyanli1982/law/benchmark goos: windows goarch: amd64 pkg: github.com/shengyanli1982/law/benchmark cpu: 12th Gen Intel(R) Core(TM) i5-12400F BenchmarkBlackHoleWriter-12 1000000000 0.1278 ns/op 0 B/op 0 allocs/op BenchmarkBlackHoleWriterParallel-12 1000000000 0.04558 ns/op 0 B/op 0 allocs/op BenchmarkLogAsyncWriter-12 6480730 212.0 ns/op 122 B/op 1 allocs/op BenchmarkLogAsyncWriterParallel-12 4032622 276.7 ns/op 260 B/op 2 allocs/op BenchmarkZapSyncWriter-12 9245127 128.7 ns/op 0 B/op 0 allocs/op BenchmarkZapSyncWriterParallel-12 52751426 26.14 ns/op 0 B/op 0 allocs/op BenchmarkZapAsyncWriter-12 3765366 311.0 ns/op 129 B/op 1 allocs/op BenchmarkZapAsyncWriterParallel-12 3039962 375.1 ns/op 234 B/op 2 allocs/op ``` -------------------------------- ### AsyncWriter HTTP Server with Zap Source: https://github.com/shengyanli1982/law/blob/main/README.md Configures an HTTP server to log messages asynchronously to os.Stdout using Zap and LAW's NewWriteAsyncer. This is beneficial for high-throughput scenarios to avoid blocking. ```go package main import ( "net/http" "os" "go.uber.org/zap" "go.uber.org/zap/zapcore" x "github.com/shengyanli1982/law" ) func main() { // 创建一个新的异步写入器,输出到标准输出 // Create a new asynchronous writer that outputs to standard output aw := x.NewWriteAsyncer(os.Stdout, nil) // 确保在程序结束时停止异步写入器 // Ensure the asynchronous writer is stopped when the program ends defer aw.Stop() // 创建一个zapcore.EncoderConfig,用于配置日志编码器 // Create a zapcore.EncoderConfig to configure the log encoder encoderCfg := zapcore.EncoderConfig{ MessageKey: "msg", // 消息的键名,Key name for the message LevelKey: "level", // 日志级别的键名,Key name for the log level NameKey: "logger", // 记录器名称的键名,Key name for the logger name EncodeLevel: zapcore.LowercaseLevelEncoder, // 日志级别的编码器,Encoder for the log level EncodeTime: zapcore.ISO8601TimeEncoder, // 时间的编码器,Encoder for the time EncodeDuration: zapcore.StringDurationEncoder, // 持续时间的编码器,Encoder for the duration } // 创建一个zapcore.WriteSyncer,将日志写入异步写入器 // Create a zapcore.WriteSyncer that writes logs to the asynchronous writer zapSyncWriter := zapcore.AddSync(aw) // 创建一个zapcore.Core,使用JSON编码器和异步写入器 // Create a zapcore.Core using the JSON encoder and the asynchronous writer zapCore := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), zapSyncWriter, zapcore.DebugLevel) // 创建一个zap.Logger,使用上面创建的zapcore.Core // Create a zap.Logger using the zapcore.Core created above zapLogger := zap.New(zapCore) // 注册一个HTTP处理函数,当访问"/"时,记录一条信息日志 // Register an HTTP handler function, when accessing "/", log an info message http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { zapLogger.Info("hello") }) // 启动HTTP服务器,监听8080端口 // Start the HTTP server, listen on port 8080 _ = http.ListenAndServe(":8080", nil) } ``` ```bash #!/bin/bash times=0 while [ $times -lt 5 ] do wrk -c 500 -t 10 http://127.0.0.1:8080 times=$[$times+1] sleep 2 echo "--------------------------------------" done ``` -------------------------------- ### Integrate LAW with Zap Logger Source: https://context7.com/shengyanli1982/law/llms.txt Wrap LAW's `WriteAsyncer` with `zapcore.AddSync` to use it as a `zapcore.WriteSyncer`. This allows Zap to send encoded JSON log lines through LAW's asynchronous pipeline. ```go package main import ( "os" "strconv" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" law "github.com/shengyanli1982/law" ) func main() { aw := law.NewWriteAsyncer(os.Stdout, nil) defer aw.Stop() encoderCfg := zapcore.EncoderConfig{ MessageKey: "msg", LevelKey: "level", NameKey: "logger", EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, } core := zapcore.NewCore( zapcore.NewJSONEncoder(encoderCfg), zapcore.AddSync(aw), // wrap LAW as WriteSyncer zapcore.DebugLevel, ) logger := zap.New(core) for i := 0; i < 10; i++ { logger.Info(strconv.Itoa(i)) } time.Sleep(3 * time.Second) // Output: // {"level":"info","msg":"0"} // {"level":"info","msg":"1"} ... {"level":"info","msg":"9"} } ``` -------------------------------- ### Handle Write Errors and Closed State Source: https://context7.com/shengyanli1982/law/llms.txt Demonstrates handling nil writes and writes after the asyncer has been stopped. Use errors.Is to check for specific error types. ```go package main import ( "bytes" "errors" "fmt" law "github.com/shengyanli1982/law" ) func main() { buf := &bytes.Buffer{} w := law.NewWriteAsyncer(buf, nil) // Normal write — enqueued immediately, flushed asynchronously n, err := w.Write([]byte("hello world\n")) fmt.Printf("enqueued %d bytes, err=%v\n", n, err) // enqueued 12 bytes, err= // Write nil — returns a typed error _, err = w.Write(nil) fmt.Println(errors.Is(err, law.ErrorWriteContentIsNil)) // true w.Stop() // Write after Stop — returns a typed error _, err = w.Write([]byte("too late")) fmt.Println(errors.Is(err, law.ErrorWriteAsyncerIsClosed)) // true } ``` -------------------------------- ### HTTP Server Performance Test Script Source: https://github.com/shengyanli1982/law/blob/main/README_CN.md A bash script to test the performance of an HTTP server using the `wrk` tool. It runs the test 5 times with a 2-second delay between each run, targeting a local server on port 8080. ```bash #!/bin/bash times=0 while [ $times -lt 5 ] do wrk -c 500 -t 10 http://127.0.0.1:8080 times=$[$times+1] sleep 2 echo "--------------------------------------" done ``` -------------------------------- ### Integrate LAW with Logrus Logger Source: https://context7.com/shengyanli1982/law/llms.txt Set Logrus output to LAW's `WriteAsyncer` using `logrus.SetOutput(aw)`. This redirects all Logrus output through LAW's asynchronous writing mechanism. ```go package main import ( "os" "time" law "github.com/shengyanli1982/law" "github.com/sirupsen/logrus" ) func main() { aw := law.NewWriteAsyncer(os.Stdout, nil) defer aw.Stop() logrus.SetOutput(aw) // all logrus output goes through LAW for i := 0; i < 10; i++ { logrus.WithField("index", i).Info("processing") } time.Sleep(3 * time.Second) // Output: // time=\"...\" level=info msg=processing index=0 // ... } ``` -------------------------------- ### Handle write failures with a callback Source: https://context7.com/shengyanli1982/law/llms.txt Registers a `Callback` whose `OnWriteFailed` method is invoked when the underlying `io.Writer` returns an error. `content` is `nil` for flush errors. Passing `nil` disables error callbacks. A small buffer size can trigger flush/write errors quickly. ```go package main import ( "errors" "fmt" "time" law "github.com/shengyanli1982/law" ) var errSink = errors.New("disk full") // brokenWriter always returns an error type brokenWriter struct{} func (b *brokenWriter) Write(p []byte) (int, error) { return 0, errSink } type myCallback struct{} func (c *myCallback) OnWriteFailed(content []byte, reason error) { fmt.Printf("FAILED: %q reason=%v\n", content, reason) } func main() { conf := law.NewConfig(). WithCallback(&myCallback{}). WithBufferSize(10) // small buffer triggers flush/write errors quickly w := law.NewWriteAsyncer(&brokenWriter{}, conf) defer w.Stop() for i := 0; i < 3; i++ { _, _ = w.Write([]byte("important log entry")) } time.Sleep(time.Second) // Output (one line per failed write): // FAILED: "important log entry" reason=disk full } ``` -------------------------------- ### Define Custom Queue Interface for LAW Source: https://github.com/shengyanli1982/law/blob/main/README.md Defines the `Queue` interface with `Push` and `Pop` methods for custom queue implementations in LAW. This interface is used to manage log data storage. ```go package main import ( "bytes" ) // Queue 是一个接口,定义了队列的基本操作:Push 和 Pop。 // Queue is an interface that defines the basic operations of a queue: Push and Pop. type Queue interface { // Push 方法用于将值添加到队列中。 // The Push method is used to add a value to the queue. Push(value *bytes.Buffer) // Pop 方法用于从队列中取出一个值。 // The Pop method is used to take a value out of the queue. Pop() *bytes.Buffer } ``` -------------------------------- ### Config.WithBufferSize Source: https://context7.com/shengyanli1982/law/llms.txt Tunes the capacity of the internal `bufio.Writer`. A larger buffer reduces the frequency of flushes, while a smaller buffer forces more frequent flushes, especially under load. The default buffer size is 2048 bytes. ```APIDOC ## Config.WithBufferSize — Tune the I/O buffer Sets the capacity of the internal `bufio.Writer`. When the buffer is full the poller flushes immediately; otherwise it waits for the idle timeout. Default is `2048` bytes. ``` -------------------------------- ### Write Source: https://context7.com/shengyanli1982/law/llms.txt `Write` implements `io.Writer`. It copies `p` into a pooled `*bytes.Buffer` and pushes it onto the internal MPSC queue. The call is non-blocking for the caller; the background poller writes to the real destination. Returns `ErrorWriteAsyncerIsClosed` if `Stop` has already been called, and `ErrorWriteContentIsNil` if `p` is `nil`. ```APIDOC ## Write ### Description Enqueues data for asynchronous flushing. This method implements the `io.Writer` interface. Data is copied into a buffer and added to an internal queue for background processing. ### Method `Write` ### Endpoint N/A (This is a method on the `WriteAsyncer` object) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **p** ([]byte) - The data to write. If nil, `ErrorWriteContentIsNil` is returned. ### Returns - `int` - The number of bytes written (enqueued). - `error` - An error if the operation fails. Returns `ErrorWriteAsyncerIsClosed` if the asyncer has been stopped, or `ErrorWriteContentIsNil` if `p` is nil. ### Request Example ```go package main import ( "bytes" "errors" "fmt" law "github.com/shengyanli1982/law" ) func main() { buf := &bytes.Buffer{} w := law.NewWriteAsyncer(buf, nil) // Normal write — enqueued immediately, flushed asynchronously n, err := w.Write([]byte("hello world\n")) fmt.Printf("enqueued %d bytes, err=%v\n", n, err) // enqueued 12 bytes, err= // Write nil — returns a typed error _, err = w.Write(nil) fmt.Println(errors.Is(err, law.ErrorWriteContentIsNil)) // true w.Stop() // Write after Stop — returns a typed error _, err = w.Write([]byte("too late")) fmt.Println(errors.Is(err, law.ErrorWriteAsyncerIsClosed)) // true } ``` ``` -------------------------------- ### Tune the I/O buffer size Source: https://context7.com/shengyanli1982/law/llms.txt Sets the capacity of the internal `bufio.Writer`. When the buffer is full, the poller flushes immediately; otherwise, it waits for the idle timeout. A very small buffer forces frequent flushes under load. ```go package main import ( "os" "strings" "time" law "github.com/shengyanli1982/law" ) func main() { // Very small buffer forces frequent flushes under load conf := law.NewConfig().WithBufferSize(64) w := law.NewWriteAsyncer(os.Stdout, conf) defer w.Stop() line := strings.Repeat("x", 80) + "\n" for i := 0; i < 5; i++ { _, _ = w.Write([]byte(line)) // each write overflows the 64-byte buffer } time.Sleep(time.Second) } ```