Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
38 changes: 38 additions & 0 deletions cmd/cli/client/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package client

import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/spf13/viper"
)

func SetLogLevel(ctx context.Context, subsystem, level string) error {
url := fmt.Sprintf("http://%s/log/level", viper.GetString("manage_api"))
jsonStr, err := json.Marshal(map[string]string{"subsystem": subsystem, "level": level})
if err != nil {
return err
}

req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to set log level: %s", resp.Status)
}

return nil
}
79 changes: 79 additions & 0 deletions cmd/cli/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package cli

import (
"fmt"

"github.com/spf13/cobra"
"github.com/storacha/piri/cmd/cliutil"
"github.com/storacha/piri/pkg/admin"
)

func NewLogCmd() *cobra.Command {
logCmd := &cobra.Command{
Use: "log",
Short: "Manage logging subsystems and levels",
}
logCmd.PersistentFlags().String("manage-api", "127.0.0.1:8888", "Management API address")

logListCmd := &cobra.Command{
Use: "list",
Short: "List all logging subsystems and their levels",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
client := admin.NewClient(cliutil.MustGetManageAPI(cmd))
resp, err := client.ListLogLevels(cmd.Context())
if err != nil {
return err
}

for subsystem, level := range resp.Levels {
fmt.Printf("%-30s %s\n", subsystem, level)
}

return nil
},
}

logSetLevelCmd := &cobra.Command{
Use: "set-level <level>",
Short: "Set log level for a subsystem or all subsystems",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
level := args[0]
systems, err := cmd.Flags().GetStringSlice("system")
if err != nil {
return err
}

client := admin.NewClient(cliutil.MustGetManageAPI(cmd))

if len(systems) == 0 {
// If no systems are specified, get all of them and set the level
resp, err := client.ListLogLevels(cmd.Context())
if err != nil {
return err
}
for subsystem := range resp.Levels {
if err := client.SetLogLevel(cmd.Context(), subsystem, level); err != nil {
return err
}
}
return nil
}

for _, system := range systems {
if err := client.SetLogLevel(cmd.Context(), system, level); err != nil {
return err
}
}

return nil
},
}

logCmd.AddCommand(logListCmd)
logCmd.AddCommand(logSetLevelCmd)
logSetLevelCmd.Flags().StringSlice("system", []string{}, "Subsystem to target. Pass multiple times for multiple systems.")

return logCmd
}
135 changes: 135 additions & 0 deletions cmd/cli/log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@

package cli

import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/require"
"github.com/storacha/piri/pkg/admin"
)

func TestLogListCmd(t *testing.T) {
expected := map[string]string{
"system1": "INFO",
"system2": "DEBUG",
}

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/log/level", r.URL.Path)
require.Equal(t, http.MethodGet, r.Method)

resp := admin.ListLogLevelsResponse{
Levels: expected,
}
json.NewEncoder(w).Encode(resp)
}))
defer server.Close()

// capture stdout
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

rootCmd.SetArgs([]string{"log", "list", "--manage-api", strings.TrimPrefix(server.URL, "http://")})
require.NoError(t, rootCmd.Execute())

// restore stdout
w.Close()
os.Stdout = old

var out bytes.Buffer
io.Copy(&out, r)

for k, v := range expected {
require.Contains(t, out.String(), k)
require.Contains(t, out.String(), v)
}
}

func TestLogSetLevelCmd(t *testing.T) {
t.Run("sets level for a single system", func(t *testing.T) {
rootCmd := newRootCmd()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/log/level", r.URL.Path)
require.Equal(t, http.MethodPost, r.Method)

var req admin.SetLogLevelRequest
json.NewDecoder(r.Body).Decode(&req)

require.Equal(t, "system1", req.Subsystem)
require.Equal(t, "DEBUG", req.Level)
}))
defer server.Close()

rootCmd.SetArgs([]string{"log", "set-level", "--system", "system1", "DEBUG", "--manage-api", strings.TrimPrefix(server.URL, "http://")})
require.NoError(t, rootCmd.Execute())
})

t.Run("sets level for multiple systems", func(t *testing.T) {
rootCmd := newRootCmd()
requests := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requests++
require.Equal(t, "/log/level", r.URL.Path)
require.Equal(t, http.MethodPost, r.Method)

var req admin.SetLogLevelRequest
json.NewDecoder(r.Body).Decode(&req)

require.Contains(t, []string{"system1", "system2"}, req.Subsystem)
require.Equal(t, "WARN", req.Level)
}))
defer server.Close()

rootCmd.SetArgs([]string{"log", "set-level", "--system", "system1", "--system", "system2", "WARN", "--manage-api", strings.TrimPrefix(server.URL, "http://")})
require.NoError(t, rootCmd.Execute())
require.Equal(t, 2, requests)
})

t.Run("sets level for all systems", func(t *testing.T) {
rootCmd := newRootCmd()
postRequests := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/log/level", r.URL.Path)

if r.Method == http.MethodGet {
resp := admin.ListLogLevelsResponse{
Levels: map[string]string{"system1": "INFO", "system2": "INFO"},
}
json.NewEncoder(w).Encode(resp)
return
}

postRequests++
require.Equal(t, http.MethodPost, r.Method)

var req admin.SetLogLevelRequest
json.NewDecoder(r.Body).Decode(&req)

require.Contains(t, []string{"system1", "system2"}, req.Subsystem)
require.Equal(t, "FATAL", req.Level)
}))
defer server.Close()

rootCmd.SetArgs([]string{"log", "set-level", "FATAL", "--manage-api", strings.TrimPrefix(server.URL, "http://")})
require.NoError(t, rootCmd.Execute())
require.Equal(t, 2, postRequests)
})
}

func newRootCmd() *cobra.Command {
root := &cobra.Command{
Use: "piri",
Short: "Piri is the software run by all storage providers on the Storacha network",
}
root.AddCommand(NewLogCmd())
return root
}
6 changes: 5 additions & 1 deletion cmd/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,16 @@ func init() {
cobra.CheckErr(rootCmd.MarkPersistentFlagFilename("key-file", "pem"))
cobra.CheckErr(viper.BindPFlag("key_file", rootCmd.PersistentFlags().Lookup("key-file")))

rootCmd.PersistentFlags().String("manage-api", "127.0.0.1:8888", "Management API address")
cobra.CheckErr(viper.BindPFlag("manage_api", rootCmd.PersistentFlags().Lookup("manage-api")))

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Be sure to remove this still

// register all commands and their subcommands
rootCmd.AddCommand(serve.Cmd)
rootCmd.AddCommand(wallet.Cmd)
rootCmd.AddCommand(identity.Cmd)
rootCmd.AddCommand(delegate.Cmd)
rootCmd.AddCommand(delegate.Cmd)
rootCmd.AddCommand(client.Cmd)
rootCmd.AddCommand(NewLogCmd())

}

Expand Down
14 changes: 14 additions & 0 deletions cmd/cli/serve/full.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/storacha/piri/pkg/config"
configapp "github.com/storacha/piri/pkg/config/app"
"github.com/storacha/piri/pkg/fx/app"
"github.com/storacha/piri/pkg/management"
)

var (
Expand Down Expand Up @@ -148,6 +149,9 @@ func startFullServer(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("transforming config: %w", err)
}

// Start the management server
startManagementServer(viper.GetString("manage_api"))

// Create the fx application with the app config
fxApp := fx.New(
fx.Supply(appConfig),
Expand Down Expand Up @@ -177,6 +181,16 @@ func startFullServer(cmd *cobra.Command, _ []string) error {
return nil
}

func startManagementServer(addr string) {
go func() {
log.Infof("starting management server on %s", addr)
mgtSrv := management.NewServer()
if err := mgtSrv.Start(addr); err != nil {
log.Errorf("failed to start management server: %s", err)
}
}()
}

// printHero prints the hero banner after startup
func printHero(id principal.Signer) {
go func() {
Expand Down
9 changes: 9 additions & 0 deletions cmd/cliutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"path"

"github.com/labstack/gommon/color"
"github.com/spf13/cobra"
"github.com/storacha/go-ucanto/did"

"github.com/storacha/piri/pkg/build"
Expand Down Expand Up @@ -37,3 +38,11 @@ func Mkdirp(dirpath ...string) (string, error) {
}
return dir, nil
}

func MustGetManageAPI(cmd *cobra.Command) string {
val, err := cmd.Flags().GetString("manage-api")
if err != nil {
panic(err)
}
return val
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ require (
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.39.0
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2235,8 +2235,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand Down
Loading