fix(go-log@2.9/go-libp2p@0.45): dynamic log level control and tail (#11039)

This fix restores dynamic log level control and tail for go-libp2p loggers

Updated to:
https://github.com/libp2p/go-libp2p/releases/tag/v0.45.0
https://github.com/ipfs/go-log/releases/tag/v2.9.0

these changes restore dynamic log level control and tail for go-libp2p
subsystems after the migration to slog, fixing the regression introduced
in https://github.com/libp2p/go-libp2p/pull/3364

Fixes https://github.com/ipfs/kubo/issues/11035

For details why and how, see explainer in
https://github.com/ipfs/go-log/releases/tag/v2.9.0
This commit is contained in:
Marcin Rataj 2025-11-06 16:00:56 +01:00 committed by GitHub
parent be7bf60355
commit f067a86170
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 196 additions and 19 deletions

View File

@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"os"
@ -33,6 +34,7 @@ import (
"github.com/ipfs/kubo/repo"
"github.com/ipfs/kubo/repo/fsrepo"
"github.com/ipfs/kubo/tracing"
"github.com/libp2p/go-libp2p/gologshim"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
manet "github.com/multiformats/go-multiaddr/net"
@ -50,6 +52,17 @@ var (
tracer trace.Tracer
)
func init() {
// Set go-log's slog handler as the application-wide default.
// This ensures all slog-based logging uses go-log's formatting.
slog.SetDefault(slog.New(logging.SlogHandler()))
// Wire go-log's slog bridge to go-libp2p's gologshim.
// This provides go-libp2p loggers with the "logger" attribute
// for per-subsystem level control (e.g., `ipfs log level libp2p-swarm debug`).
gologshim.SetDefaultHandler(logging.SlogHandler())
}
// declared as a var for testing purposes.
var dnsResolver = madns.DefaultResolver

View File

@ -154,8 +154,9 @@ For Docker users, the legacy `ipfs/go-ipfs` image name now shows a deprecation n
### 📦️ Important dependency updates
- update `go-libp2p` to [v0.44.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.44.0) with self-healing UPnP port mappings
- update `go-libp2p` to [v0.45.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.45.0) (incl. [v0.44.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.44.0)) with self-healing UPnP port mappings and go-log/slog interop fixes
- update `quic-go` to [v0.55.0](https://github.com/quic-go/quic-go/releases/tag/v0.55.0)
- update `go-log` to [v2.9.0](https://github.com/ipfs/go-log/releases/tag/v2.9.0) with slog integration for go-libp2p
- update `go-ds-pebble` to [v0.5.6](https://github.com/ipfs/go-ds-pebble/releases/tag/v0.5.6) (includes pebble [v2.1.1](https://github.com/cockroachdb/pebble/releases/tag/v2.1.1))
- update `boxo` to [v0.35.1](https://github.com/ipfs/boxo/releases/tag/v0.35.1)
- update `ipfs-webui` to [v4.10.0](https://github.com/ipfs/ipfs-webui/releases/tag/v4.10.0)

View File

@ -9,7 +9,7 @@ replace github.com/ipfs/kubo => ./../../..
require (
github.com/ipfs/boxo v0.35.1
github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
github.com/libp2p/go-libp2p v0.44.0
github.com/libp2p/go-libp2p v0.45.0
github.com/multiformats/go-multiaddr v0.16.1
)
@ -93,7 +93,7 @@ require (
github.com/ipfs/go-ipld-format v0.6.3 // indirect
github.com/ipfs/go-ipld-git v0.1.1 // indirect
github.com/ipfs/go-ipld-legacy v0.2.2 // indirect
github.com/ipfs/go-log/v2 v2.8.2 // indirect
github.com/ipfs/go-log/v2 v2.9.0 // indirect
github.com/ipfs/go-metrics-interface v0.3.0 // indirect
github.com/ipfs/go-peertaskqueue v0.8.2 // indirect
github.com/ipfs/go-test v0.2.3 // indirect

View File

@ -349,8 +349,8 @@ github.com/ipfs/go-ipld-git v0.1.1/go.mod h1:+VyMqF5lMcJh4rwEppV0e6g4nCCHXThLYYD
github.com/ipfs/go-ipld-legacy v0.2.2 h1:DThbqCPVLpWBcGtU23KDLiY2YRZZnTkXQyfz8aOfBkQ=
github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7eYlWeAOPyDU=
github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
github.com/ipfs/go-log/v2 v2.8.2 h1:nVG4nNHUwwI/sTs9Bi5iE8sXFQwXs3AjkkuWhg7+Y2I=
github.com/ipfs/go-log/v2 v2.8.2/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c=
github.com/ipfs/go-log/v2 v2.9.0 h1:l4b06AwVXwldIzbVPZy5z7sKp9lHFTX0KWfTBCtHaOk=
github.com/ipfs/go-log/v2 v2.9.0/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c=
github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU=
github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY=
github.com/ipfs/go-peertaskqueue v0.8.2 h1:PaHFRaVFdxQk1Qo3OKiHPYjmmusQy7gKQUaL8JDszAU=
@ -424,8 +424,8 @@ github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZ
github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=
github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784=
github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo=
github.com/libp2p/go-libp2p v0.44.0 h1:5Gtt8OrF8yiXmH+Mx4+/iBeFRMK1TY3a8OrEBDEqAvs=
github.com/libp2p/go-libp2p v0.44.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc=
github.com/libp2p/go-libp2p v0.45.0 h1:Pdhr2HsFXaYjtfiNcBP4CcRUONvbMFdH3puM9vV4Tiw=
github.com/libp2p/go-libp2p v0.45.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc=
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g=

4
go.mod
View File

@ -39,7 +39,7 @@ require (
github.com/ipfs/go-ipld-format v0.6.3
github.com/ipfs/go-ipld-git v0.1.1
github.com/ipfs/go-ipld-legacy v0.2.2
github.com/ipfs/go-log/v2 v2.8.2
github.com/ipfs/go-log/v2 v2.9.0
github.com/ipfs/go-metrics-interface v0.3.0
github.com/ipfs/go-metrics-prometheus v0.1.0
github.com/ipfs/go-test v0.2.3
@ -51,7 +51,7 @@ require (
github.com/jbenet/go-temp-err-catcher v0.1.0
github.com/julienschmidt/httprouter v1.3.0
github.com/libp2p/go-doh-resolver v0.5.0
github.com/libp2p/go-libp2p v0.44.0
github.com/libp2p/go-libp2p v0.45.0
github.com/libp2p/go-libp2p-http v0.5.0
github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32
github.com/libp2p/go-libp2p-kbucket v0.8.0

8
go.sum
View File

@ -416,8 +416,8 @@ github.com/ipfs/go-ipld-git v0.1.1/go.mod h1:+VyMqF5lMcJh4rwEppV0e6g4nCCHXThLYYD
github.com/ipfs/go-ipld-legacy v0.2.2 h1:DThbqCPVLpWBcGtU23KDLiY2YRZZnTkXQyfz8aOfBkQ=
github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7eYlWeAOPyDU=
github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
github.com/ipfs/go-log/v2 v2.8.2 h1:nVG4nNHUwwI/sTs9Bi5iE8sXFQwXs3AjkkuWhg7+Y2I=
github.com/ipfs/go-log/v2 v2.8.2/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c=
github.com/ipfs/go-log/v2 v2.9.0 h1:l4b06AwVXwldIzbVPZy5z7sKp9lHFTX0KWfTBCtHaOk=
github.com/ipfs/go-log/v2 v2.9.0/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c=
github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU=
github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY=
github.com/ipfs/go-metrics-prometheus v0.1.0 h1:bApWOHkrH3VTBHzTHrZSfq4n4weOZDzZFxUXv+HyKcA=
@ -504,8 +504,8 @@ github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZ
github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=
github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784=
github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo=
github.com/libp2p/go-libp2p v0.44.0 h1:5Gtt8OrF8yiXmH+Mx4+/iBeFRMK1TY3a8OrEBDEqAvs=
github.com/libp2p/go-libp2p v0.44.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc=
github.com/libp2p/go-libp2p v0.45.0 h1:Pdhr2HsFXaYjtfiNcBP4CcRUONvbMFdH3puM9vV4Tiw=
github.com/libp2p/go-libp2p v0.45.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc=
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g=

View File

@ -1,11 +1,16 @@
package cli
import (
"bufio"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"os/exec"
"strings"
"testing"
"time"
"github.com/ipfs/kubo/test/cli/harness"
. "github.com/ipfs/kubo/test/cli/testutils"
@ -599,6 +604,164 @@ func TestLogLevel(t *testing.T) {
})
})
// Constants for slog interop tests
const (
slogTestLogTailTimeout = 10 * time.Second
slogTestLogWaitTimeout = 5 * time.Second
slogTestLogStartupDelay = 1 * time.Second // Wait for log tail to start
slogTestSubsystemCmdsHTTP = "cmds/http" // Native go-log subsystem
slogTestSubsystemNetIdentify = "net/identify" // go-libp2p slog subsystem
)
// logMatch represents a matched log entry for slog interop tests
type logMatch struct {
subsystem string
line string
}
// startLogMonitoring starts ipfs log tail and returns command and channel for matched logs.
startLogMonitoring := func(t *testing.T, node *harness.Node) (*exec.Cmd, chan logMatch) {
t.Helper()
ctx, cancel := context.WithTimeout(context.Background(), slogTestLogTailTimeout)
t.Cleanup(cancel)
cmd := exec.CommandContext(ctx, node.IPFSBin, "log", "tail")
cmd.Env = append([]string(nil), os.Environ()...)
for k, v := range node.Runner.Env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
}
cmd.Dir = node.Runner.Dir
stdout, err := cmd.StdoutPipe()
require.NoError(t, err)
require.NoError(t, cmd.Start())
matches := make(chan logMatch, 10)
go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
line := scanner.Text()
// Check for actual logger field in JSON, not just substring match
if strings.Contains(line, `"logger":"cmds/http"`) {
matches <- logMatch{slogTestSubsystemCmdsHTTP, line}
}
if strings.Contains(line, `"logger":"net/identify"`) {
matches <- logMatch{slogTestSubsystemNetIdentify, line}
}
}
}()
return cmd, matches
}
// waitForBothSubsystems waits for both native go-log and slog subsystems to appear in logs.
waitForBothSubsystems := func(t *testing.T, matches chan logMatch, timeout time.Duration) {
t.Helper()
seen := make(map[string]struct{})
deadline := time.After(timeout)
for len(seen) < 2 {
select {
case match := <-matches:
if _, exists := seen[match.subsystem]; !exists {
t.Logf("Found %s log", match.subsystem)
seen[match.subsystem] = struct{}{}
}
case <-deadline:
t.Fatalf("Timeout waiting for logs. Seen: %v", seen)
}
}
assert.Contains(t, seen, slogTestSubsystemCmdsHTTP, "should see cmds/http (native go-log)")
assert.Contains(t, seen, slogTestSubsystemNetIdentify, "should see net/identify (slog from go-libp2p)")
}
// triggerIdentifyProtocol connects node1 to node2, triggering net/identify logs.
triggerIdentifyProtocol := func(t *testing.T, node1, node2 *harness.Node) {
t.Helper()
// Get node2's peer ID and address
node2ID := node2.PeerID().String()
addrsRes := node2.IPFS("id", "-f", "<addrs>")
require.NoError(t, addrsRes.Err)
addrs := strings.Split(strings.TrimSpace(addrsRes.Stdout.String()), "\n")
require.NotEmpty(t, addrs, "node2 should have at least one address")
// Connect node1 to node2
multiaddr := fmt.Sprintf("%s/p2p/%s", addrs[0], node2ID)
res := node1.IPFS("swarm", "connect", multiaddr)
require.NoError(t, res.Err)
}
// verifySlogInterop verifies that both native go-log and slog from go-libp2p
// appear in ipfs log tail with correct formatting and level control.
verifySlogInterop := func(t *testing.T, node1, node2 *harness.Node) {
t.Helper()
cmd, matches := startLogMonitoring(t, node1)
defer func() {
_ = cmd.Process.Kill()
}()
time.Sleep(slogTestLogStartupDelay)
// Trigger cmds/http (native go-log)
node1.IPFS("version")
// Trigger net/identify (slog from go-libp2p)
triggerIdentifyProtocol(t, node1, node2)
waitForBothSubsystems(t, matches, slogTestLogWaitTimeout)
}
// This test verifies that go-log's slog bridge works with go-libp2p's gologshim
// when log levels are set via GOLOG_LOG_LEVEL environment variable.
// It tests both native go-log loggers (cmds/http) and slog-based loggers from
// go-libp2p (net/identify), ensuring both types appear in `ipfs log tail`.
t.Run("slog interop via env var", func(t *testing.T) {
t.Parallel()
h := harness.NewT(t)
node1 := h.NewNode().Init()
node1.Runner.Env["GOLOG_LOG_LEVEL"] = "error,cmds/http=debug,net/identify=debug"
node1.StartDaemon()
defer node1.StopDaemon()
node2 := h.NewNode().Init().StartDaemon()
defer node2.StopDaemon()
verifySlogInterop(t, node1, node2)
})
// This test verifies that go-log's slog bridge works with go-libp2p's gologshim
// when log levels are set dynamically via `ipfs log level` CLI commands.
// It tests the key feature that SetLogLevel auto-creates level entries for subsystems
// that don't exist yet, enabling `ipfs log level net/identify debug` to work even
// before the net/identify logger is created. This is critical for slog interop.
t.Run("slog interop via CLI", func(t *testing.T) {
t.Parallel()
h := harness.NewT(t)
node1 := h.NewNode().Init().StartDaemon()
defer node1.StopDaemon()
node2 := h.NewNode().Init().StartDaemon()
defer node2.StopDaemon()
// Set levels via CLI for both subsystems BEFORE triggering events
res := node1.IPFS("log", "level", slogTestSubsystemCmdsHTTP, "debug")
require.NoError(t, res.Err)
res = node1.IPFS("log", "level", slogTestSubsystemNetIdentify, "debug")
require.NoError(t, res.Err) // Auto-creates level entry for slog subsystem
verifySlogInterop(t, node1, node2)
})
}
func getExpectedSubsystems(t *testing.T, node *harness.Node) []string {

View File

@ -8,7 +8,7 @@ require (
github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd
github.com/golangci/golangci-lint v1.64.8
github.com/ipfs/go-cidutil v0.1.0
github.com/ipfs/go-log/v2 v2.8.2
github.com/ipfs/go-log/v2 v2.9.0
github.com/ipfs/go-test v0.2.3
github.com/ipfs/hang-fds v0.1.0
github.com/ipfs/iptb v1.4.1
@ -182,7 +182,7 @@ require (
github.com/libp2p/go-cidranger v1.1.0 // indirect
github.com/libp2p/go-doh-resolver v0.5.0 // indirect
github.com/libp2p/go-flow-metrics v0.3.0 // indirect
github.com/libp2p/go-libp2p v0.44.0 // indirect
github.com/libp2p/go-libp2p v0.45.0 // indirect
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32 // indirect
github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect

View File

@ -364,8 +364,8 @@ github.com/ipfs/go-ipld-format v0.6.3 h1:9/lurLDTotJpZSuL++gh3sTdmcFhVkCwsgx2+rA
github.com/ipfs/go-ipld-format v0.6.3/go.mod h1:74ilVN12NXVMIV+SrBAyC05UJRk0jVvGqdmrcYZvCBk=
github.com/ipfs/go-ipld-legacy v0.2.2 h1:DThbqCPVLpWBcGtU23KDLiY2YRZZnTkXQyfz8aOfBkQ=
github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7eYlWeAOPyDU=
github.com/ipfs/go-log/v2 v2.8.2 h1:nVG4nNHUwwI/sTs9Bi5iE8sXFQwXs3AjkkuWhg7+Y2I=
github.com/ipfs/go-log/v2 v2.8.2/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c=
github.com/ipfs/go-log/v2 v2.9.0 h1:l4b06AwVXwldIzbVPZy5z7sKp9lHFTX0KWfTBCtHaOk=
github.com/ipfs/go-log/v2 v2.9.0/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c=
github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU=
github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY=
github.com/ipfs/go-peertaskqueue v0.8.2 h1:PaHFRaVFdxQk1Qo3OKiHPYjmmusQy7gKQUaL8JDszAU=
@ -458,8 +458,8 @@ github.com/libp2p/go-doh-resolver v0.5.0 h1:4h7plVVW+XTS+oUBw2+8KfoM1jF6w8XmO7+s
github.com/libp2p/go-doh-resolver v0.5.0/go.mod h1:aPDxfiD2hNURgd13+hfo29z9IC22fv30ee5iM31RzxU=
github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784=
github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo=
github.com/libp2p/go-libp2p v0.44.0 h1:5Gtt8OrF8yiXmH+Mx4+/iBeFRMK1TY3a8OrEBDEqAvs=
github.com/libp2p/go-libp2p v0.44.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc=
github.com/libp2p/go-libp2p v0.45.0 h1:Pdhr2HsFXaYjtfiNcBP4CcRUONvbMFdH3puM9vV4Tiw=
github.com/libp2p/go-libp2p v0.45.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc=
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32 h1:xZj18PsLD157snR/BFo547jwOkGDH7jZjMEkBDOoD4Q=