Skip to content

Commit 3fe4e57

Browse files
authored
feat(internal/librarian): add flag to configure verbose logging (#2285)
Adds -v flag to the CLI that sets the logging level to debug for each command Fixes #2148
1 parent 9a2fbfe commit 3fe4e57

File tree

4 files changed

+108
-2
lines changed

4 files changed

+108
-2
lines changed

internal/librarian/doc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ Flags:
124124
local file path like /path/to/repo. Both absolute and relative paths are
125125
supported. If not specified, will try to detect if the current working directory
126126
is configured as a language repository.
127+
-v enables verbose logging
127128
128129
# release
129130

internal/librarian/flags.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,7 @@ func addFlagWorkRoot(fs *flag.FlagSet, cfg *config.Config) {
112112
`Working directory root. When this is not specified, a working directory
113113
will be created in /tmp.`)
114114
}
115+
116+
func addFlagVerbose(fs *flag.FlagSet, p *bool) {
117+
fs.BoolVar(p, "v", false, "enables verbose logging")
118+
}

internal/librarian/librarian.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package librarian
1717
import (
1818
"context"
1919
"fmt"
20+
"log/slog"
21+
"os"
2022

2123
"github.com/googleapis/librarian/internal/cli"
2224
)
@@ -27,6 +29,16 @@ func Run(ctx context.Context, arg ...string) error {
2729
return cmd.Run(ctx, arg)
2830
}
2931

32+
func setupLogger(verbose bool) {
33+
level := slog.LevelInfo
34+
if verbose {
35+
level = slog.LevelDebug
36+
}
37+
opts := &slog.HandlerOptions{Level: level}
38+
handler := slog.NewTextHandler(os.Stderr, opts)
39+
slog.SetDefault(slog.New(handler))
40+
}
41+
3042
func newLibrarianCommand() *cli.Command {
3143
cmdVersion := &cli.Command{
3244
Short: "version prints the version information",
@@ -65,11 +77,14 @@ func newLibrarianCommand() *cli.Command {
6577
}
6678

6779
func newCmdGenerate() *cli.Command {
80+
var verbose bool
6881
cmdGenerate := &cli.Command{
6982
Short: "generate onboards and generates client library code",
7083
UsageLine: "librarian generate [flags]",
7184
Long: generateLongHelp,
7285
Action: func(ctx context.Context, cmd *cli.Command) error {
86+
setupLogger(verbose)
87+
slog.Debug("generate command verbose logging")
7388
if err := cmd.Config.SetDefaults(); err != nil {
7489
return fmt.Errorf("failed to initialize config: %w", err)
7590
}
@@ -94,15 +109,19 @@ func newCmdGenerate() *cli.Command {
94109
addFlagBranch(cmdGenerate.Flags, cmdGenerate.Config)
95110
addFlagWorkRoot(cmdGenerate.Flags, cmdGenerate.Config)
96111
addFlagPush(cmdGenerate.Flags, cmdGenerate.Config)
112+
addFlagVerbose(cmdGenerate.Flags, &verbose)
97113
return cmdGenerate
98114
}
99115

100116
func newCmdTagAndRelease() *cli.Command {
117+
var verbose bool
101118
cmdTagAndRelease := &cli.Command{
102119
Short: "tag-and-release tags and creates a GitHub release for a merged pull request.",
103120
UsageLine: "librarian release tag-and-release [arguments]",
104121
Long: tagAndReleaseLongHelp,
105122
Action: func(ctx context.Context, cmd *cli.Command) error {
123+
setupLogger(verbose)
124+
slog.Debug("tag-and-release command verbose logging")
106125
if err := cmd.Config.SetDefaults(); err != nil {
107126
return fmt.Errorf("failed to initialize config: %w", err)
108127
}
@@ -120,15 +139,19 @@ func newCmdTagAndRelease() *cli.Command {
120139
addFlagRepo(cmdTagAndRelease.Flags, cmdTagAndRelease.Config)
121140
addFlagPR(cmdTagAndRelease.Flags, cmdTagAndRelease.Config)
122141
addFlagGitHubAPIEndpoint(cmdTagAndRelease.Flags, cmdTagAndRelease.Config)
142+
addFlagVerbose(cmdTagAndRelease.Flags, &verbose)
123143
return cmdTagAndRelease
124144
}
125145

126146
func newCmdInit() *cli.Command {
147+
var verbose bool
127148
cmdInit := &cli.Command{
128149
Short: "init initiates a release by creating a release pull request.",
129150
UsageLine: "librarian release init [flags]",
130151
Long: releaseInitLongHelp,
131152
Action: func(ctx context.Context, cmd *cli.Command) error {
153+
setupLogger(verbose)
154+
slog.Debug("init command verbose logging")
132155
if err := cmd.Config.SetDefaults(); err != nil {
133156
return fmt.Errorf("failed to initialize config: %w", err)
134157
}
@@ -151,5 +174,6 @@ func newCmdInit() *cli.Command {
151174
addFlagRepo(cmdInit.Flags, cmdInit.Config)
152175
addFlagBranch(cmdInit.Flags, cmdInit.Config)
153176
addFlagWorkRoot(cmdInit.Flags, cmdInit.Config)
177+
addFlagVerbose(cmdInit.Flags, &verbose)
154178
return cmdInit
155179
}

internal/librarian/librarian_test.go

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
package librarian
1616

1717
import (
18+
"bytes"
1819
"context"
1920
"fmt"
21+
"io"
2022
"log"
23+
"log/slog"
2124
"math/rand"
2225
"os"
2326
"os/exec"
2427
"path/filepath"
28+
"strings"
2529
"testing"
2630

2731
"github.com/go-git/go-git/v5"
@@ -34,14 +38,87 @@ import (
3438
"github.com/google/go-cmp/cmp"
3539
)
3640

37-
// TODO(https://github.com/googleapis/librarian/issues/202): add better tests
38-
// for librarian.Run.
3941
func TestRun(t *testing.T) {
4042
if err := Run(t.Context(), []string{"version"}...); err != nil {
4143
log.Fatal(err)
4244
}
4345
}
4446

47+
func TestVerboseFlag(t *testing.T) {
48+
for _, test := range []struct {
49+
name string
50+
args []string
51+
expectDebugLog bool
52+
expectDebugSubstr string
53+
}{
54+
{
55+
name: "generate with -v flag",
56+
args: []string{"generate", "-v"},
57+
expectDebugLog: true,
58+
expectDebugSubstr: "generate command verbose logging",
59+
},
60+
{
61+
name: "generate without -v flag",
62+
args: []string{"generate"},
63+
expectDebugLog: false,
64+
},
65+
{
66+
name: "release init with -v flag",
67+
args: []string{"release", "init", "-v"},
68+
expectDebugLog: true,
69+
expectDebugSubstr: "init command verbose logging",
70+
},
71+
{
72+
name: "release init without -v flag",
73+
args: []string{"release", "init"},
74+
expectDebugLog: false,
75+
},
76+
{
77+
name: "release tag-and-release with -v flag",
78+
args: []string{"release", "tag-and-release", "-v"},
79+
expectDebugLog: true,
80+
expectDebugSubstr: "tag-and-release command verbose logging",
81+
},
82+
{
83+
name: "release tag-and-release without -v flag",
84+
args: []string{"release", "tag-and-release"},
85+
expectDebugLog: false,
86+
},
87+
} {
88+
t.Run(test.name, func(t *testing.T) {
89+
// Redirect stderr to capture logs.
90+
oldStderr := os.Stderr
91+
r, w, _ := os.Pipe()
92+
os.Stderr = w
93+
// Reset logger to default for test isolation.
94+
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, nil)))
95+
96+
_ = Run(context.Background(), test.args...)
97+
98+
// Restore stderr and read the output.
99+
w.Close()
100+
os.Stderr = oldStderr
101+
var buf bytes.Buffer
102+
io.Copy(&buf, r)
103+
output := buf.String()
104+
105+
hasDebugLog := strings.Contains(output, "level=DEBUG")
106+
if test.expectDebugLog {
107+
if !hasDebugLog {
108+
t.Errorf("expected debug log to be present, but it wasn't. Output:\n%s", output)
109+
}
110+
if !strings.Contains(output, test.expectDebugSubstr) {
111+
t.Errorf("expected debug log to contain %q, but it didn't. Output:\n%s", test.expectDebugSubstr, output)
112+
}
113+
} else {
114+
if hasDebugLog {
115+
t.Errorf("expected debug log to be absent, but it was present. Output:\n%s", output)
116+
}
117+
}
118+
})
119+
}
120+
}
121+
45122
func TestGenerate_DefaultBehavior(t *testing.T) {
46123
ctx := context.Background()
47124

0 commit comments

Comments
 (0)