mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
- Add NoticeLevel constant using zapcore.WarnLevel value (1)
- Implement custom level encoders (noticeLevelEncoder, noticeColorLevelEncoder)
- Update Notice() method to use custom level
- Add "notice" case to log level parsing in main.go
- Update encoder configurations to handle NOTICE level properly
- Add comprehensive test (TestNoticeLevel) to verify behavior
The NOTICE level provides visual distinction from INFO and ERROR levels,
with cyan color in development and proper level filtering. When log level
is set to NOTICE, it shows NOTICE and above (WARN, ERROR) while filtering
out DEBUG and INFO messages.
Note: NOTICE and WARN share the same numeric value (1) due to zap's
integer-based level system, so both display as "NOTICE" in logs for
visual consistency.
Usage:
- logger.Notice().Msg("message")
- log_level = "notice" in config
- Supports structured logging with fields
145 lines
4.4 KiB
Go
145 lines
4.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zapcore"
|
|
|
|
"github.com/Control-D-Inc/ctrld"
|
|
)
|
|
|
|
func Test_logWriter_Write(t *testing.T) {
|
|
size := 64 * 1024
|
|
lw := &logWriter{size: size}
|
|
lw.buf.Grow(lw.size)
|
|
data := strings.Repeat("A", size)
|
|
lw.Write([]byte(data))
|
|
if lw.buf.String() != data {
|
|
t.Fatalf("unexpected buf content: %v", lw.buf.String())
|
|
}
|
|
newData := "B"
|
|
halfData := strings.Repeat("A", len(data)/2) + logWriterInitEndMarker
|
|
lw.Write([]byte(newData))
|
|
if lw.buf.String() != halfData+newData {
|
|
t.Fatalf("unexpected new buf content: %v", lw.buf.String())
|
|
}
|
|
|
|
bigData := strings.Repeat("B", 256*1024)
|
|
expected := halfData + strings.Repeat("B", 16*1024)
|
|
lw.Write([]byte(bigData))
|
|
if lw.buf.String() != expected {
|
|
t.Fatalf("unexpected big buf content: %v", lw.buf.String())
|
|
}
|
|
}
|
|
|
|
func Test_logWriter_ConcurrentWrite(t *testing.T) {
|
|
size := 64 * 1024
|
|
lw := &logWriter{size: size}
|
|
n := 10
|
|
var wg sync.WaitGroup
|
|
wg.Add(n)
|
|
for i := 0; i < n; i++ {
|
|
go func() {
|
|
defer wg.Done()
|
|
lw.Write([]byte(strings.Repeat("A", i)))
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
if lw.buf.Len() > lw.size {
|
|
t.Fatalf("unexpected buf size: %v, content: %q", lw.buf.Len(), lw.buf.String())
|
|
}
|
|
}
|
|
|
|
func Test_logWriter_MarkerInitEnd(t *testing.T) {
|
|
size := 64 * 1024
|
|
lw := &logWriter{size: size}
|
|
lw.buf.Grow(lw.size)
|
|
|
|
paddingSize := 10
|
|
// Writing half of the size, minus len(end marker) and padding size.
|
|
dataSize := size/2 - len(logWriterInitEndMarker) - paddingSize
|
|
data := strings.Repeat("A", dataSize)
|
|
// Inserting newline for making partial init data
|
|
data += "\n"
|
|
// Filling left over buffer to make the log full.
|
|
// The data length: len(end marker) + padding size - 1 (for newline above) + size/2
|
|
data += strings.Repeat("A", len(logWriterInitEndMarker)+paddingSize-1+(size/2))
|
|
lw.Write([]byte(data))
|
|
if lw.buf.String() != data {
|
|
t.Fatalf("unexpected buf content: %v", lw.buf.String())
|
|
}
|
|
lw.Write([]byte("B"))
|
|
lw.Write([]byte(strings.Repeat("B", 256*1024)))
|
|
firstIdx := strings.Index(lw.buf.String(), logWriterInitEndMarker)
|
|
lastIdx := strings.LastIndex(lw.buf.String(), logWriterInitEndMarker)
|
|
// Check if init end marker present.
|
|
if firstIdx == -1 || lastIdx == -1 {
|
|
t.Fatalf("missing init end marker: %s", lw.buf.String())
|
|
}
|
|
// Check if init end marker appears only once.
|
|
if firstIdx != lastIdx {
|
|
t.Fatalf("log init end marker appears more than once: %s", lw.buf.String())
|
|
}
|
|
// Ensure that we have the correct init log data.
|
|
if !strings.Contains(lw.buf.String(), strings.Repeat("A", dataSize)+logWriterInitEndMarker) {
|
|
t.Fatalf("unexpected log content: %s", lw.buf.String())
|
|
}
|
|
}
|
|
|
|
// TestNoticeLevel tests that the custom NOTICE level works correctly
|
|
func TestNoticeLevel(t *testing.T) {
|
|
// Create a buffer to capture log output
|
|
var buf bytes.Buffer
|
|
|
|
// Create encoder config with custom NOTICE level support
|
|
encoderConfig := zap.NewDevelopmentEncoderConfig()
|
|
encoderConfig.TimeKey = "time"
|
|
encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("15:04:05.000")
|
|
encoderConfig.EncodeLevel = noticeLevelEncoder
|
|
|
|
// Test with NOTICE level
|
|
encoder := zapcore.NewConsoleEncoder(encoderConfig)
|
|
core := zapcore.NewCore(encoder, zapcore.AddSync(&buf), ctrld.NoticeLevel)
|
|
logger := zap.New(core)
|
|
ctrldLogger := &ctrld.Logger{Logger: logger}
|
|
|
|
// Log messages at different levels
|
|
ctrldLogger.Debug().Msg("This is a DEBUG message")
|
|
ctrldLogger.Info().Msg("This is an INFO message")
|
|
ctrldLogger.Notice().Msg("This is a NOTICE message")
|
|
ctrldLogger.Warn().Msg("This is a WARN message")
|
|
ctrldLogger.Error().Msg("This is an ERROR message")
|
|
|
|
output := buf.String()
|
|
|
|
// Verify that DEBUG and INFO messages are NOT logged (filtered out)
|
|
if strings.Contains(output, "DEBUG") {
|
|
t.Error("DEBUG message should not be logged when level is NOTICE")
|
|
}
|
|
if strings.Contains(output, "INFO") {
|
|
t.Error("INFO message should not be logged when level is NOTICE")
|
|
}
|
|
|
|
// Verify that NOTICE, WARN, and ERROR messages ARE logged
|
|
if !strings.Contains(output, "NOTICE") {
|
|
t.Error("NOTICE message should be logged when level is NOTICE")
|
|
}
|
|
if !strings.Contains(output, "WARN") {
|
|
t.Error("WARN message should be logged when level is NOTICE")
|
|
}
|
|
if !strings.Contains(output, "ERROR") {
|
|
t.Error("ERROR message should be logged when level is NOTICE")
|
|
}
|
|
|
|
// Verify the NOTICE message content
|
|
if !strings.Contains(output, "This is a NOTICE message") {
|
|
t.Error("NOTICE message content should be present")
|
|
}
|
|
|
|
t.Logf("Log output with NOTICE level:\n%s", output)
|
|
}
|