Skip to content

Conversation

@MarcoPolo
Copy link
Collaborator

lidel, take a look and let me know your thoughts. This is targeting your branch.

This removes the goLogBridge interface check and having to modify the default slog logger. Instead you explicitly set the handler via gologshim.SetDefaultHandler.

Copy link
Contributor

@gammazero gammazero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this makes the code cleaner/clearer. Your call @lidel

lidel added a commit to ipfs/kubo that referenced this pull request Nov 3, 2025
wire gologshim to go-log's slog bridge via init() in cmd/ipfs/kubo/start.go

update go-libp2p to v0.44.1-0.20251029234611-789d14c6effe

see libp2p/go-libp2p#3419
Copy link
Member

@lidel lidel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for us also, pushed docs + some thoughts below:

On the trade-off here

Original #3418 (commit ea2c010 before merging this PR into that one) required no changes in existing apps, just updating both go-log and go-libp2p to latest versions. That was all end user would have to do to restore logs.

This PR requires every legacy application that used both go-log and go-libp2p to add this extra code (if they use automatic slog bridge):

  import (
      "github.com/libp2p/go-libp2p/gologshim"
  )

  func init() {
      // After go-log's init() runs and installs slog bridge,
      // tell gologshim to use it      
      handler := slog.Default().Handler()      
      // Verify it's go-log's bridge
      type goLogBridge interface {
	      GoLogBridge()
      }
      if _, ok := handler.(goLogBridge); !ok {
	      panic("aborting startup: slog.Default() is not go-log's bridge, logs would be missing due to incorrect wiring")
      }
      // use the implicit go-log bridge      
      gologshim.SetDefaultHandler(handler)
  }

or this, if they dont touch slog default:

  import (
      logging "github.com/ipfs/go-log/v2"
      "github.com/libp2p/go-libp2p/gologshim"
  )

  func init() {
      // use the explicit go-log bridge      
      gologshim.SetDefaultHandler(golog.SlogHandler())
  }

If this is the price to pay to fix logs, we can do the above in Kubo, Rainbow and every other application Shipyard maintains, but everyone else will be missing logs unless they do the same.

Just a trade-off decision.

Next steps

@MarcoPolo lgtm, i've tested this PR in ipfs/kubo#11039 and it also works for us (with the above extra code).

Up to you if you prefer implicit or explicit.

  • If you prefer explicit, feel free to merge this PR into #3418, and then merge that (#3419+#3418).
  • If you are ok with implicit detection, this PR can be closed and only merge #3418

Either way, we would appreciate a release with a fix 🙏

@MarcoPolo
Copy link
Collaborator Author

This PR requires every legacy application that used both go-log and go-libp2p to add this extra code:

I don't follow, do you mind explaining a bit more? Where are you calling slog.SetDefault? And why not directly call gologshim.SetDefaultHandler instead?

@lidel
Copy link
Member

lidel commented Nov 4, 2025

@MarcoPolo I wrote an (wip draft) explainer in ipfs/go-log#176 for go-log users, check "Slog integration" section in README in that PR and especially the "For library authors" section, which has two approaches and their ramifications illustrated with sample code:

Where are you calling slog.SetDefault?

go-log already called SetupLogging in own go-log's init() here to correctly handle ENV-based configuration. This already happened automatically if code imported go-log.

What we added to that init in ipfs/go-log#176 is an opt-out slog support which calls captureSlog here that sets up bridge and sets it via slog.SetDefault here

This has to be done because we dont want to lose any slog entries in go-lang apps, nor want to have different formatting from libraries that use slog. Going forward, various libraries may switch to slog, and we dont want to have regressions like the one go-libp2p caused when it switched to slog.

In the future, we may decide to switch the go-log backend from zap to slog, but the fact we call slog.SetDefault would not change. If app choses to use go-log, they expect it to handle all logs.

And why not directly call gologshim.SetDefaultHandler instead?

The go-libp2p/gologshim only takes care of go-libp2p and ignores any other application that use slog.

go-log is how applications in ecosystem control log levels and we do not want to have regressions like ipfs/kubo#11035 anywhere else.

The goal was to unbreak existing users without asking them to change any code.

If go-libp2p forces our hand to add extra call for gologshim.SetDefaultHandler to have per subsystem visibility in Kubo, that is fine, but unfortunate for turn-key experience across ecosystem. Ideally, we would not require end users to do extra work to fix every application.

@MarcoPolo
Copy link
Collaborator Author

Going forward, various libraries may switch to slog, and we dont want to have regressions like the one go-libp2p caused when it switched to slog.

Are you expecting to have all other libraries add the goLogBridge interface check? I'm not sure that makes sense.

I think setting the default log handler is something that should be owned by the application, not by a library that could possibly be a transitive dependency of another library.

I don't mind merging this change, but this will change again if/when we move away from global loggers to passing loggers explicitly. At that point applications would need to pass go-log explicitly to go-libp2p anyways, and any init trick won't work.

@MarcoPolo MarcoPolo force-pushed the gologshim-runtime-controlled-handler branch from 7a7ab1b to 326593e Compare November 4, 2025 20:38
@MarcoPolo MarcoPolo merged commit 326593e into feat/gologshim-use-slog-default Nov 4, 2025
8 checks passed
lidel added a commit to ipfs/go-log that referenced this pull request Nov 5, 2025
add SlogHandler() function that returns go-log's slog.Handler directly.
this allows applications to wire slog-based libraries to go-log even
when GOLOG_CAPTURE_DEFAULT_SLOG=false.

the bridge is now always created during SetupLogging(), but only
installed as slog.Default() when automatic capture is enabled.

use atomic.Pointer[slog.Handler] for thread-safe access without locks,
following idiomatic Go patterns for single-writer, multiple-reader scenarios.

benefits:
- works when GOLOG_CAPTURE_DEFAULT_SLOG=false
- more explicit API (no indirection through slog.Default())
- simpler application code (no duck-typing verification needed)
- thread-safe without mutex overhead

addresses feedback from libp2p/go-libp2p#3419
@lidel lidel mentioned this pull request Nov 5, 2025
lidel added a commit to ipfs/go-log that referenced this pull request Nov 5, 2025
change GOLOG_CAPTURE_DEFAULT_SLOG to default to false, requiring explicit
opt-in for automatic slog.Default() capture. applications wanting slog
integration must now explicitly call slog.SetDefault(slog.New(golog.SlogHandler())).

rationale:
- go-libp2p decided to use explicit wiring (gologshim.SetDefaultHandler)
  rather than relying on automatic slog.Default() capture
- automatic capture was the original motivation for default=true, but is
  no longer needed
- makes go-log a better Go library by not modifying global state by default
- follows Go best practice: libraries should not call slog.SetDefault()
- opt-in is better than opt-out for invasive features

changes:
- setup.go: change condition from != "false" to == "true"
- README.md: updated documentation to show explicit setup pattern,
  reposition GOLOG_CAPTURE_DEFAULT_SLOG as development/opt-in feature
- tests: updated expectations to match new default behavior

references:
- #176 (comment)
- libp2p/go-libp2p#3419 (review)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants