-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmd.go
201 lines (159 loc) · 5.55 KB
/
cmd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package cmd
import (
"fmt"
"github.com/auhau/allot"
"github.com/auhau/gredux"
"github.com/auhau/loggy/state"
"github.com/auhau/loggy/store"
"github.com/auhau/loggy/ui"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
)
// Option names
const (
CONFIG_OPTION_NAME = "config"
BUFFER_SIZE_NAME = "buffer-size"
PARSE_PATTERN_NAME = "pattern"
PARSE_PATTERN_NAME_NAME = "pattern-name"
FOLLOW_NAME = "follow"
DISABLE_REGEX_ESCAPE = "disable-regex-escape"
)
// Default values
const (
DEFAULT_BUFFER_SIZE = 10000
)
var LONG_DESCRIPTION = fmt.Sprintf(`By default loggy reads from STDIN or you can specify file path to read the logs from specific file.
You quit the application by pressing Ctrl+C.
Configuration
-------------
All options that can be passed using CLI flags can be configured using config file or environment variables.
loggy looks for a config file ".loggy.toml" in current working directory and in $HOME folder.
Moreover you can use environment variables with "LOGGY_" prefix, where for example flag "--pattern" would be "LOGGY_PATTERN" env. variable.
The order of precedence is: $HOME config > CWD config > --config config > Env. variables > CLI flags.
Parsing pattern
---------------
%s
Pattern names
-------------
In your config file you can create a [patterns] section where you can predefine your patterns using <name>="<pattern>" syntax and then use --pattern-name/-n flag to use it.
Filter
------
%s
Keyboard shortcuts
------------------
%s
%s
%s
`, ui.HelpParsingPatternText, ui.HelpFilterText, ui.HelpHomeText, ui.HelpNavigationText, ui.HelpInputsText)
var cfgFile string
// TODO: Add option to set filter
// TODO: Add option to save the logs into file (as the logs are discarded when exceeding buffer size)
// TODO: Allow "infinite" buffer size?
// cmd represents the entry command
var cmd = &cobra.Command{
Use: "loggy [path to log file]",
Short: "Swiss knife for logs",
Long: LONG_DESCRIPTION,
Args: cobra.MaximumNArgs(1),
Version: ui.Version,
Run: func(cmd *cobra.Command, args []string) {
var (
inputName string
inputStream *os.File
err error
)
pattern := viper.GetString(PARSE_PATTERN_NAME)
patterName := viper.GetString(PARSE_PATTERN_NAME_NAME)
if patterName != "" {
pattern, err = resolvePatterName(patterName)
cobra.CheckErr(err)
}
bufferSize := viper.GetInt(BUFFER_SIZE_NAME)
if len(args) == 1 {
info, err := os.Stat(args[0])
cobra.CheckErr(err)
if info.IsDir() {
cmd.PrintErr("passed path is a directory")
os.Exit(1)
}
file, err := os.Open(args[0])
cobra.CheckErr(err)
inputStream = file
inputName = info.Name()
} else {
inputStream = os.Stdin
inputName = "STDIN"
}
parsingPatternInstance, err := allot.NewWithEscaping(pattern, store.Types)
cobra.CheckErr(err)
initialState := state.State{
IsFollowing: viper.GetBool(FOLLOW_NAME),
ShouldEscapeParsingPattern: !viper.GetBool(DISABLE_REGEX_ESCAPE),
IsFilterOn: false,
FilterString: "",
ParsingPatternString: pattern,
ParsingPattern: parsingPatternInstance,
InputName: inputName,
IsLogsFirstLine: true,
}
stateStore := gredux.New(initialState)
uiApp, err := ui.Bootstrap(stateStore, bufferSize)
cobra.CheckErr(err)
go store.StartBuffering(inputStream, uiApp, stateStore, bufferSize)
// App runs until CtrlC is pressed which terminates the UI and continue the execution bellow.
// The tview capture the key press so SIGINT is not properly raised so not using it here.
err = uiApp.Run()
cobra.CheckErr(err)
// Clean up
err = inputStream.Close()
cobra.CheckErr(err)
// Lets kill the whole process group as mitigation of https://github.com/AuHau/loggy/issues/32
killGroup()
},
}
// resolvePatterName looks into configured `patterns` and if find one by the name it will use it as parse pattern for the logs
func resolvePatterName(name string) (string, error) {
patterns := viper.GetStringMapString("patterns")
chosenPattern, exists := patterns[name]
if !exists {
return "", fmt.Errorf("pattern with name '%s' does not exist", name)
}
return chosenPattern, nil
}
func init() {
cobra.OnInitialize(initConfig)
cmd.Flags().StringVar(&cfgFile, CONFIG_OPTION_NAME, "", "config file (default is $HOME/.loggy.yaml)")
cmd.Flags().IntP(BUFFER_SIZE_NAME, "b", DEFAULT_BUFFER_SIZE, "number of lines that will be buffered")
cmd.Flags().StringP(PARSE_PATTERN_NAME, "p", "", "parsing pattern see above for details")
cmd.Flags().StringP(PARSE_PATTERN_NAME_NAME, "n", "", "use predefined pattern in config")
cmd.Flags().BoolP(FOLLOW_NAME, "f", false, "turn on following mode which always show latest logs")
cmd.Flags().BoolP(DISABLE_REGEX_ESCAPE, "r", false, "turn off pattern regex escaping")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
viper.SetConfigType("toml")
viper.SetConfigName(".loggy")
// Home directory config
home, err := os.UserHomeDir()
cobra.CheckErr(err)
viper.AddConfigPath(home)
_ = viper.ReadInConfig() // if no config is found we don't care
// Get current directory config
viper.AddConfigPath(".")
_ = viper.MergeInConfig() // if no config is found we don't care
// Config file from the flag.
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
cobra.CheckErr(viper.MergeInConfig())
}
viper.SetEnvPrefix("loggy")
viper.AutomaticEnv() // read in environment variables that match
cobra.CheckErr(viper.BindPFlags(cmd.Flags()))
}
func Execute() {
err := cmd.Execute()
if err != nil {
os.Exit(1)
}
}