Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
40bbd2a
feat: add log get-level command
SgtPooki Jul 24, 2025
44e1119
chore: update changelog
SgtPooki Jul 24, 2025
9839fa4
test: add log get-level tests
SgtPooki Jul 24, 2025
254f5c9
chore: cleanup
SgtPooki Jul 24, 2025
970e687
chore: fix typo and harden tests
lidel Jul 24, 2025
c5b7d21
fix: TestCommands
lidel Jul 24, 2025
32c8363
docs: relation to GOLOG_LOG_LEVEL
lidel Jul 25, 2025
5b2597f
chore(go.mod): switch to PR dependency
lidel Jul 25, 2025
8adcf34
chore: update to latest go-log PR changes
SgtPooki Jul 28, 2025
1042bab
fix: GetLogLevel requires an argument
SgtPooki Jul 28, 2025
987d378
fix: do not output single subsystem name in CLI
SgtPooki Jul 28, 2025
b7489b9
test: explicit subsystem request dont output subsystem
SgtPooki Jul 28, 2025
0d910d0
update to new release og go-log
gammazero Jul 29, 2025
dc2cc3f
Use go-log v2.8.0. Use default keyword to identify default level
gammazero Jul 30, 2025
95835cb
LevelFromString renamed to Parse
gammazero Jul 30, 2025
6990fef
Recognize keywords "all", "default", and "*" when setting log levels
gammazero Jul 31, 2025
3b725d7
Merge branch 'master' into fix/add-api-v0-log--get-level
gammazero Aug 6, 2025
9a6d341
Modify `ipfs log level` to show log levels
gammazero Aug 6, 2025
2ee9c40
Denote default level with sdubsystem name '(defult)'. Fix tests
gammazero Aug 6, 2025
a9521a7
fix help formatting
gammazero Aug 6, 2025
0cd62f2
test: extra CLI/RPC tests for examples from --help
lidel Aug 6, 2025
2b5ab33
refactor: restore 'all' alias for '*'
lidel Aug 6, 2025
fb0f89d
Merge branch 'master' into fix/add-api-v0-log--get-level
gammazero Aug 11, 2025
fefffdb
Merge branch 'master' into fix/add-api-v0-log--get-level
lidel Aug 11, 2025
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
4 changes: 2 additions & 2 deletions cmd/ipfs/kubo/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ func insideGUI() bool {
func checkDebug(req *cmds.Request) {
// check if user wants to debug. option OR env var.
debug, _ := req.Options["debug"].(bool)
ipfsLogLevel, _ := logging.LevelFromString(os.Getenv("IPFS_LOGGING")) // IPFS_LOGGING is deprecated
goLogLevel, _ := logging.LevelFromString(os.Getenv("GOLOG_LOG_LEVEL"))
ipfsLogLevel, _ := logging.Parse(os.Getenv("IPFS_LOGGING")) // IPFS_LOGGING is deprecated
goLogLevel, _ := logging.Parse(os.Getenv("GOLOG_LOG_LEVEL"))

if debug || goLogLevel == logging.LevelDebug || ipfsLogLevel == logging.LevelDebug {
u.Debug = true
Expand Down
1 change: 1 addition & 0 deletions core/commands/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ func TestCommands(t *testing.T) {
"/key/sign",
"/key/verify",
"/log",
"/log/get-level",
"/log/level",
"/log/ls",
"/log/tail",
Expand Down
118 changes: 103 additions & 15 deletions core/commands/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import (
logging "github.com/ipfs/go-log/v2"
)

// Golang os.Args overrides * and replaces the character argument with
// an array which includes every file in the user's CWD. As a
// workaround, we use 'all' instead. The util library still uses * so
// we convert it at this step.
var logAllKeyword = "all"
const (
// allLogSubsystems is used to specify all log subsystems when setting the
// log level.
allLogSubsystems = "all"
// defaultLogLevel is used to request and to identify the default log
// level.
defaultLogLevel = "default"
)

var LogCmd = &cmds.Command{
Helptext: cmds.HelpText{
Expand All @@ -31,9 +34,10 @@ system (not just for the daemon logs, but all commands):
},

Subcommands: map[string]*cmds.Command{
"level": logLevelCmd,
"ls": logLsCmd,
"tail": logTailCmd,
"level": logLevelCmd,
"get-level": logGetLevelCmd,
"ls": logLsCmd,
"tail": logTailCmd,
},
}

Expand All @@ -43,26 +47,31 @@ var logLevelCmd = &cmds.Command{
ShortDescription: `
Change the verbosity of one or all subsystems log output. This does not affect
the event log.

This provides a dynamic, runtime alternative to the GOLOG_LOG_LEVEL
environment variable documented in 'ipfs log'.
`,
},

Arguments: []cmds.Argument{
// TODO use a different keyword for 'all' because all can theoretically
// clash with a subsystem name
cmds.StringArg("subsystem", true, false, fmt.Sprintf("The subsystem logging identifier. Use '%s' for all subsystems.", logAllKeyword)),
cmds.StringArg("level", true, false, `The log level, with 'debug' the most verbose and 'fatal' the least verbose.
One of: debug, info, warn, error, dpanic, panic, fatal.
`),
cmds.StringArg("subsystem", true, false, fmt.Sprintf("The subsystem logging identifier. Use '%s' to set all subsystems and the default level.", allLogSubsystems)),
cmds.StringArg("level", true, false, fmt.Sprintf("The log level, with 'debug' as the most verbose and 'fatal' the least verbose. Use '%s' to set to the current default level.\n One of: debug, info, warn, error, dpanic, panic, fatal, %s", defaultLogLevel, defaultLogLevel)),
},
NoLocal: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
args := req.Arguments
subsystem, level := args[0], args[1]

if subsystem == logAllKeyword {
if subsystem == allLogSubsystems || subsystem == "*" {
subsystem = "*"
}

if level == defaultLogLevel {
level = logging.DefaultLevel().String()
}

if err := logging.SetLogLevel(subsystem, level); err != nil {
return err
}
Expand Down Expand Up @@ -108,7 +117,7 @@ const logLevelOption = "log-level"
var logTailCmd = &cmds.Command{
Status: cmds.Experimental,
Helptext: cmds.HelpText{
Tagline: "Read and outpt log messages.",
Tagline: "Read and output log messages.",
ShortDescription: `
Outputs log messages as they are generated.

Expand All @@ -130,7 +139,7 @@ This will only return 'info' logs from bitswap and skip 'debug'.
var pipeReader *logging.PipeReader
logLevelString, _ := req.Options[logLevelOption].(string)
if logLevelString != "" {
logLevel, err := logging.LevelFromString(logLevelString)
logLevel, err := logging.Parse(logLevelString)
if err != nil {
return fmt.Errorf("setting log level %s: %w", logLevelString, err)
}
Expand All @@ -146,3 +155,82 @@ This will only return 'info' logs from bitswap and skip 'debug'.
return res.Emit(pipeReader)
},
}

var logGetLevelCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Get the logging level.",
ShortDescription: `
'ipfs log get-level' is a utility command used to get the logging level for
a specific subsystem, the global default, or all subsystems.

This complements 'ipfs log level' and provides runtime visibility into the
current logging configuration (whether set by GOLOG_LOG_LEVEL environment
variable or changed dynamically).

Examples:
ipfs log get-level # Show levels for all subsystems
ipfs log get-level all # Show the global default level
ipfs log get-level core # Show the core subsystem level
`,
},

Arguments: []cmds.Argument{
cmds.StringArg("subsystem", false, false, fmt.Sprintf("The subsystem logging identifier. Use '%s' for the default level. If not specified, returns levels for all subsystems.", defaultLogLevel)),
},
NoLocal: true,
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
var subsystem string
if len(req.Arguments) > 0 {
subsystem = req.Arguments[0]
}

switch subsystem {
case "":
// Return levels for all subsystems (default behavior)
levels := logging.SubsystemLevelNames()

// Replace the log package default name with the command default name.
delete(levels, logging.DefaultName)
levels[defaultLogLevel] = logging.DefaultLevel().String()
return cmds.EmitOnce(res, &logLevelsOutput{Levels: levels})
case defaultLogLevel:
// Return the default log level
levelMap := map[string]string{defaultLogLevel: logging.DefaultLevel().String()}
return cmds.EmitOnce(res, &logLevelsOutput{Levels: levelMap})
default:
// Return level for a specific subsystem.
level, err := logging.SubsystemLevelName(subsystem)
if err != nil {
return err
}
levelMap := map[string]string{subsystem: level}
return cmds.EmitOnce(res, &logLevelsOutput{Levels: levelMap})
}
},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *logLevelsOutput) error {
// Check if this is an RPC call by looking for the encoding option
encoding, _ := req.Options["encoding"].(string)
isRPC := encoding == "json"

// If there are multiple subsystems (no specific subsystem requested), always show names
showNames := isRPC || len(out.Levels) > 1

for subsystem, level := range out.Levels {
if showNames {
// Show subsystem name when it's RPC or when showing multiple subsystems
fmt.Fprintf(w, "%s: %s\n", subsystem, level)
} else {
// For CLI calls with single subsystem, only show the level
fmt.Fprintf(w, "%s\n", level)
}
}
return nil
}),
},
Type: logLevelsOutput{},
}

type logLevelsOutput struct {
Levels map[string]string
}
12 changes: 12 additions & 0 deletions docs/changelogs/v0.37.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This release was brought to you by the [Interplanetary Shipyard](https://ipship
- [Overview](#overview)
- [🔦 Highlights](#-highlights)
- [Clear provide queue when reprovide strategy changes](#clear-provide-queue-when-reprovide-strategy-changes)
- [New log level querying capabilities](#new-log-level-querying-capabilities)
- [Remove unnecessary packages from thirdparty](#remove-unnecessary-packages-from-thirdparty)
- [📦️ Important dependency updates](#-important-dependency-updates)
- [📝 Changelog](#-changelog)
Expand All @@ -31,6 +32,17 @@ A new `ipfs provide clear` command also allows manual queue clearing for debuggi
> [!NOTE]
> Upgrading to Kubo 0.37 will automatically clear any preexisting provide queue. The next time `Reprovider.Interval` hits, `Reprovider.Strategy` will be executed on a clean slate, ensuring consistent behavior with your current configuration.

#### New log level querying capabilities

You can now query current logging levels for subsystems and the global logger.

- `ipfs log get-level` - shows all subsystem log levels
- `ipfs log get-level <subsystem>` - shows log level for a specific subsystem
- `ipfs log get-level all` - shows the global log level
- HTTP API: `POST /api/v0/log/get-level?arg=<subsystem>`

This complements the existing `ipfs log level` command for setting log levels, providing full visibility into your current logging configuration.

#### Remove unnecessary packages from thirdparty

Removed unnecessary packages from the `thirdparty` area of kubo repositroy.
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/kubo-as-a-library/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ require (
github.com/ipfs/go-ipld-format v0.6.2 // 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.6.0 // indirect
github.com/ipfs/go-log/v2 v2.8.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-unixfsnode v1.10.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/kubo-as-a-library/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,8 @@ github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7
github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
github.com/ipfs/go-log/v2 v2.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg=
github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8=
github.com/ipfs/go-log/v2 v2.8.0 h1:SptNTPJQV3s5EF4FdrTu/yVdOKfGbDgn1EBZx4til2o=
github.com/ipfs/go-log/v2 v2.8.0/go.mod h1:2LEEhdv8BGubPeSFTyzbqhCqrwqxCbuTNTLWqgNAipo=
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=
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ require (
github.com/ipfs/go-ipld-format v0.6.2
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.6.0
github.com/ipfs/go-log/v2 v2.8.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.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,8 @@ github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7
github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
github.com/ipfs/go-log/v2 v2.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg=
github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8=
github.com/ipfs/go-log/v2 v2.8.0 h1:SptNTPJQV3s5EF4FdrTu/yVdOKfGbDgn1EBZx4til2o=
github.com/ipfs/go-log/v2 v2.8.0/go.mod h1:2LEEhdv8BGubPeSFTyzbqhCqrwqxCbuTNTLWqgNAipo=
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=
Expand Down
115 changes: 115 additions & 0 deletions test/cli/log_get_level_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package cli

import (
"strings"
"testing"

"github.com/ipfs/kubo/test/cli/harness"
. "github.com/ipfs/kubo/test/cli/testutils"
"github.com/stretchr/testify/assert"
)

func TestLogGetLevel(t *testing.T) {

t.Run("get-level shows all subsystems", func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init().StartDaemon()
defer node.StopDaemon()

// Get expected subsystem count from 'ipfs log ls'
lsRes := node.IPFS("log", "ls")
assert.NoError(t, lsRes.Err)
expectedSubsystems := len(SplitLines(lsRes.Stdout.String()))

res := node.IPFS("log", "get-level")
assert.NoError(t, res.Err)
assert.Equal(t, 0, len(res.Stderr.Lines()))

output := res.Stdout.String()
lines := SplitLines(output)

// Should show all subsystems plus the global '*' level
assert.GreaterOrEqual(t, len(lines), expectedSubsystems)

// Check that each line has the format "subsystem: level"
for _, line := range lines {
if strings.TrimSpace(line) == "" {
continue
}
parts := strings.Split(line, ": ")
assert.Equal(t, 2, len(parts), "Line should have format 'subsystem: level', got: %s", line)
assert.NotEmpty(t, parts[0], "Subsystem should not be empty")
assert.NotEmpty(t, parts[1], "Level should not be empty")
}
})

t.Run("get-level with specific subsystem", func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init().StartDaemon()
defer node.StopDaemon()

node.IPFS("log", "level", "core", "debug")
res := node.IPFS("log", "get-level", "core")
assert.NoError(t, res.Err)
assert.Equal(t, 0, len(res.Stderr.Lines()))

output := res.Stdout.String()
lines := SplitLines(output)

assert.Equal(t, 1, len(lines))

line := strings.TrimSpace(lines[0])
assert.Equal(t, "debug", line)
})

t.Run("get-level with 'default' returns default level", func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init().StartDaemon()
defer node.StopDaemon()

res1 := node.IPFS("log", "level", "all", "fatal")
assert.NoError(t, res1.Err)
assert.Equal(t, 0, len(res1.Stderr.Lines()))

res := node.IPFS("log", "get-level", "default")
assert.NoError(t, res.Err)
assert.Equal(t, 0, len(res.Stderr.Lines()))

output := res.Stdout.String()
lines := SplitLines(output)

assert.Equal(t, 1, len(lines))

line := strings.TrimSpace(lines[0])
assert.Equal(t, "fatal", line)
})

t.Run("get-level reflects runtime log level changes", func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init().StartDaemon("--offline")
defer node.StopDaemon()

node.IPFS("log", "level", "core", "debug")
res := node.IPFS("log", "get-level", "core")
assert.NoError(t, res.Err)

output := res.Stdout.String()
lines := SplitLines(output)

assert.Equal(t, 1, len(lines))

line := strings.TrimSpace(lines[0])
assert.Equal(t, "debug", line)
})

t.Run("get-level with non-existent subsystem returns error", func(t *testing.T) {
t.Parallel()
node := harness.NewT(t).NewNode().Init().StartDaemon()
defer node.StopDaemon()

res := node.RunIPFS("log", "get-level", "non-existent-subsystem")
assert.Error(t, res.Err)
assert.NotEqual(t, 0, len(res.Stderr.Lines()))
})

}
2 changes: 1 addition & 1 deletion test/dependencies/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd
github.com/golangci/golangci-lint v1.60.2
github.com/ipfs/go-cidutil v0.1.0
github.com/ipfs/go-log/v2 v2.6.0
github.com/ipfs/go-log/v2 v2.8.0
github.com/ipfs/go-test v0.2.2
github.com/ipfs/hang-fds v0.1.0
github.com/ipfs/iptb v1.4.1
Expand Down
4 changes: 2 additions & 2 deletions test/dependencies/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,8 @@ github.com/ipfs/go-ipld-format v0.6.2 h1:bPZQ+A05ol0b3lsJSl0bLvwbuQ+HQbSsdGTy4xt
github.com/ipfs/go-ipld-format v0.6.2/go.mod h1:nni2xFdHKx5lxvXJ6brt/pndtGxKAE+FPR1rg4jTkyk=
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.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg=
github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8=
github.com/ipfs/go-log/v2 v2.8.0 h1:SptNTPJQV3s5EF4FdrTu/yVdOKfGbDgn1EBZx4til2o=
github.com/ipfs/go-log/v2 v2.8.0/go.mod h1:2LEEhdv8BGubPeSFTyzbqhCqrwqxCbuTNTLWqgNAipo=
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=
Expand Down
Loading