Skip to content

Navigation Menu

Sign in
Sign up

logger: replace WithTap with WithTee #1790

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
paulwe merged 8 commits into main from logger-with-tee
Sep 10, 2026
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/logger-with-tee.md
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"github.com/livekit/protocol": minor
"@livekit/protocol": patch
---

Replace logger.WithTap with logger.WithTee, which duplicates every log entry to a caller-supplied zaputil.Tee. The tee's core is built from the level each derived logger resolves, so the copy follows component levels rather than carrying a level of its own.

Replace ZapLogger.WithMinLevel with WithComponentLeveler, which attaches a zaputil.ComponentLeveler to a branch of the logger tree. A leveler owns the per-component level and write-enabler cache for one configuration source and can only widen its parent, so a caller can resolve levels per (tenant, component) without rebuilding loggers on a config change. ZapLogger.Leveler exposes the leveler a branch resolves through, for use as the parent of a derived one.
3 changes: 1 addition & 2 deletions livekit/egress_test.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"testing"

"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/logger/testutil"
Expand All @@ -32,7 +31,7 @@ type TestEgressLogOutput struct {

func TestLoggerProto(t *testing.T) {
ws := &testutil.BufferedWriteSyncer{}
l, err := logger.NewZapLogger(&logger.Config{}, logger.WithTap(zaputil.NewWriteEnabler(ws, zapcore.DebugLevel)))
l, err := logger.NewZapLogger(&logger.Config{Level: "debug"}, logger.WithTee(zaputil.NewTee(testutil.NewJSONCoreFactory(ws))))
require.NoError(t, err)

s3 := &S3Upload{
Expand Down
22 changes: 21 additions & 1 deletion logger/config.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@

package logger

import "sync"
import (
"strings"
"sync"

"go.uber.org/zap/zapcore"
)

type Config struct {
JSON bool `yaml:"json,omitempty"`
Expand Down Expand Up @@ -72,3 +77,18 @@ func (c *Config) AddUpdateObserver(cb ConfigObserver) {
defer c.lock.Unlock()
c.onUpdatedCallbacks = append(c.onUpdatedCallbacks, cb)
}

// ResolveComponentLevel always resolves: an unconfigured component takes Level.
func (c *Config) ResolveComponentLevel(component string) (zapcore.Level, bool) {
c.lock.Lock()
defer c.lock.Unlock()

parts := strings.Split(component, ".")
for len(parts) > 0 {
if lvl, ok := c.ComponentLevels[strings.Join(parts, ".")]; ok {
return ParseZapLevel(lvl), true
}
parts = parts[:len(parts)-1]
}
return ParseZapLevel(c.Level), true
}
25 changes: 25 additions & 0 deletions logger/config_test.go
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package logger

import (
"io"
"testing"

"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

"github.com/livekit/protocol/logger/zaputil"
)

func TestConfigResolveComponentLevel(t *testing.T) {
conf := &Config{Level: "info", ComponentLevels: map[string]string{"rtc.room": "debug"}}
root := zaputil.NewRootComponentLeveler(zapcore.AddSync(io.Discard), conf)

require.True(t, root.ComponentLevel("rtc.room").Enabled(zapcore.DebugLevel))
require.True(t, root.ComponentLevel("rtc.room.track").Enabled(zapcore.DebugLevel))
require.False(t, root.ComponentLevel("rtc").Enabled(zapcore.DebugLevel))
require.True(t, root.ComponentLevel("rtc").Enabled(zapcore.InfoLevel))

lvl, ok := (&Config{}).ResolveComponentLevel("anything")
require.True(t, ok)
require.Equal(t, zapcore.InfoLevel, lvl)
}
Loading
Loading

AltStyle によって変換されたページ (->オリジナル) /