Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0b6ca66

Browse files
committed
feature/logging (#18)
Reviewed-on: http://localhost:3000/MABD/reposcan/pulls/18
1 parent 81f1c42 commit 0b6ca66

9 files changed

Lines changed: 126 additions & 5 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ dist/
99
.vscode/
1010
# OS
1111
.DS_Store
12+
todo.md
1213
output-samples/

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Common flags
6464
-w, --max-workers int # Number of concurrent git checks (default 8)
6565
-o, --output string # Output format: json|table|interactive|none (default "table")
6666
-r, --root stringArray # Root directory to scan (repeatable). Defaults to $HOME if unset in config. (default [$HOME])
67+
, --debug # Enable/Disable debug mode
6768
```
6869

6970
Help
@@ -82,6 +83,7 @@ By default, `reposcan` looks for a config file in:
8283
Example
8384
```toml
8485
version = 1
86+
debug = false
8587

8688
# directories to search for git repos inside
8789
roots = ["~/Code", "~/work"]

cmd/reposcan/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,5 @@ func init() {
3636
RootCmd.PersistentFlags().StringP("filter", "f", string(configs.Only), "Repository filter: all|dirty|uncommitted|unpushed|unpulled")
3737
RootCmd.PersistentFlags().String("json-output-path", configs.Output.JSONPath, "Write scan report JSON files to this directory (optional)")
3838
RootCmd.PersistentFlags().IntP("max-workers", "w", configs.MaxWorkers, "Number of concurrent git checks")
39+
RootCmd.PersistentFlags().BoolP("debug", "", configs.Debug, "Enable/Disable debug mode")
3940
}

cmd/reposcan/rootCmd.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/mabd-dev/reposcan/internal/config"
1111
"github.com/mabd-dev/reposcan/internal/gitx"
12+
"github.com/mabd-dev/reposcan/internal/logger"
1213
"github.com/mabd-dev/reposcan/internal/render/file"
1314
"github.com/mabd-dev/reposcan/internal/render/stdout"
1415
"github.com/mabd-dev/reposcan/internal/render/tui"
@@ -54,6 +55,8 @@ var RootCmd = &cobra.Command{
5455
return fmt.Errorf("invalid configuration after flags")
5556
}
5657

58+
logger.Init(configs.Debug, paths.LogFileDir)
59+
5760
return run(configs)
5861
},
5962
}
@@ -64,10 +67,11 @@ var RootCmd = &cobra.Command{
6467
// Supported flags:
6568
// - root (-r) : repeatable directory roots to scan
6669
// - dirIgnore (-d) : repeatable glob patterns to ignore during scan
67-
// - output (-o) : output format: json|table|none
70+
// - output (-o) : output format: json|table|interactive|none
6871
// - filter (-f) : repository filter: all|dirty|uncommitted|unpushed|unpulled
6972
// - json-output-path : directory to write JSON report files
7073
// - max-workers (-w) : number of concurrent git checks
74+
// - debug (--debug) : enable/disable debug mode
7175
func readFlags(cmd *cobra.Command, configs *config.Config) error {
7276
// Read roots flags
7377
roots, err := cmd.Flags().GetStringArray("root")
@@ -121,6 +125,12 @@ func readFlags(cmd *cobra.Command, configs *config.Config) error {
121125
}
122126
(*configs).MaxWorkers = maxWorkers
123127

128+
debug, err := cmd.Flags().GetBool("debug")
129+
if err != nil {
130+
return err
131+
}
132+
(*configs).Debug = debug
133+
124134
return nil
125135
}
126136

docs/cli-flags.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,8 @@ This document explains each CLI flag, its equivalent `config.toml` field, what i
5757
- Description: Concurrency for git state checks when scanning many repos.
5858
- Example: `reposcan -w 16`
5959

60+
- `--debug true/false`
61+
- Config: `debug = true/false`
62+
- Description: Enable/disable logging mode. Log file will be in `~/.config/reposcan/logs/`
63+
- Example: `--debug=false` or `--debug` same as `--debug=true`
64+

internal/config/paths.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ package config
44
const (
55
DefaultConfigDir = "/.config/reposcan/"
66
DefaultConfigToml = "config.toml"
7+
DefaultLogFileDir = "/.config/reposcan/logs/"
78
)
89

910
// Paths contains resolved file-system locations for configuration.
1011
type Paths struct {
1112
ConfigDir string
1213
ConfigFilePath string
14+
LogFileDir string
1315
}
1416

1517
// DefaultPaths returns the default config directory and file path relative
@@ -18,5 +20,6 @@ func DefaultPaths() Paths {
1820
return Paths{
1921
ConfigDir: DefaultConfigDir,
2022
ConfigFilePath: DefaultConfigDir + DefaultConfigToml,
23+
LogFileDir: DefaultLogFileDir,
2124
}
2225
}

internal/config/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ type Config struct {
1212
// Max git checker workers
1313
MaxWorkers int `toml:"maxWorkers"`
1414

15+
// Debug if true, enable logging to a file in [DefaultLogFileDir]
16+
Debug bool `toml:"debug"`
17+
1518
Version int `toml:"version"`
1619
}
1720

@@ -91,6 +94,7 @@ func Defaults() Config {
9194
Only: OnlyDirty,
9295
Output: newOutput,
9396
MaxWorkers: 8,
97+
Debug: false,
9498
Version: 1,
9599
}
96100
}

internal/logger/logger.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Package logger is a logger package :)
2+
package logger
3+
4+
import (
5+
"fmt"
6+
"log/slog"
7+
"os"
8+
"path/filepath"
9+
"time"
10+
)
11+
12+
var (
13+
enabled bool = false
14+
logger *slog.Logger
15+
)
16+
17+
func Init(
18+
isEnabled bool,
19+
logFilePath string,
20+
) {
21+
enabled = isEnabled
22+
if !enabled {
23+
return
24+
}
25+
26+
file, err := createLogFile(logFilePath)
27+
if err != nil {
28+
panic(err)
29+
}
30+
31+
opts := &slog.HandlerOptions{
32+
Level: slog.LevelDebug,
33+
}
34+
logger = slog.New(slog.NewTextHandler(file, opts))
35+
logger.Info("Logger initialized")
36+
}
37+
38+
func createLogFile(logFilePath string) (*os.File, error) {
39+
homeDir, err := os.UserHomeDir()
40+
if err != nil {
41+
panic(fmt.Errorf("failed to get user home dir: %w", err))
42+
}
43+
logDir := filepath.Join(homeDir, logFilePath)
44+
45+
// Ensure directory exists
46+
if err := os.MkdirAll(logDir, 0o755); err != nil {
47+
panic(fmt.Errorf("failed to create log dir: %w", err))
48+
}
49+
50+
// Create log file with today's date
51+
today := time.Now().Format("2006-01-02")
52+
logFile := filepath.Join(logDir, today+".log")
53+
54+
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
55+
if err != nil {
56+
return nil, fmt.Errorf("failed to open log file: %w", err)
57+
}
58+
59+
return file, nil
60+
}
61+
62+
func Debug(msg string, args ...any) {
63+
if enabled {
64+
logger.Debug(msg, args...)
65+
}
66+
}
67+
68+
func Info(msg string, args ...any) {
69+
if enabled {
70+
logger.Info(msg, args...)
71+
}
72+
}
73+
74+
func Warn(msg string, args ...any) {
75+
if enabled {
76+
logger.Warn(msg, args...)
77+
}
78+
}
79+
80+
func Error(msg string, args ...any) {
81+
if enabled {
82+
logger.Error(msg, args...)
83+
}
84+
}
85+
86+
func BoolAttr(key string, v bool) slog.Attr {
87+
return slog.Bool(key, v)
88+
}
89+
90+
func StringAttr(key string, v string) slog.Attr {
91+
return slog.String(key, v)
92+
}

sample/config.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
Version = 1
1+
version = 1
22

3-
Roots = [
3+
# When true log file will appear in ~/.config/reposcan/logs/
4+
debug = false
5+
6+
roots = [
47
"/home/mabd/"
58
]
69

710
# options: all|dirty
8-
Only = 'dirty'
11+
only = 'dirty'
912

10-
DirIgnore = [
13+
dirIgnore = [
1114
# --- Package managers / deps ---
1215
"**/node_modules/**",
1316
"**/vendor/**",

0 commit comments

Comments
 (0)