Files
nginx-logtail/cmd/cli/flags.go

133 lines
3.3 KiB
Go

package main
import (
"flag"
"fmt"
"os"
"strings"
st "git.ipng.ch/ipng/nginx-logtail/internal/store"
pb "git.ipng.ch/ipng/nginx-logtail/proto/logtailpb"
)
// sharedFlags holds the flags common to every subcommand.
type sharedFlags struct {
targets []string
jsonOut bool
website string
prefix string
uri string
status string // expression: "200", "!=200", ">=400", etc.
websiteRe string // RE2 regex against website
uriRe string // RE2 regex against request URI
}
// bindShared registers the shared flags on fs and returns a pointer to the
// populated struct. Call fs.Parse before reading the struct.
func bindShared(fs *flag.FlagSet) (*sharedFlags, *string) {
sf := &sharedFlags{}
target := fs.String("target", "localhost:9090", "comma-separated host:port list")
fs.BoolVar(&sf.jsonOut, "json", false, "emit newline-delimited JSON")
fs.StringVar(&sf.website, "website", "", "filter: exact website match")
fs.StringVar(&sf.prefix, "prefix", "", "filter: exact client prefix match")
fs.StringVar(&sf.uri, "uri", "", "filter: exact request URI match")
fs.StringVar(&sf.status, "status", "", "filter: HTTP status expression (200, !=200, >=400, <500, …)")
fs.StringVar(&sf.websiteRe, "website-re", "", "filter: RE2 regex against website")
fs.StringVar(&sf.uriRe, "uri-re", "", "filter: RE2 regex against request URI")
return sf, target
}
func (sf *sharedFlags) resolve(target string) {
sf.targets = parseTargets(target)
}
func parseTargets(s string) []string {
seen := make(map[string]bool)
var out []string
for _, t := range strings.Split(s, ",") {
t = strings.TrimSpace(t)
if t == "" || seen[t] {
continue
}
seen[t] = true
out = append(out, t)
}
return out
}
func buildFilter(sf *sharedFlags) *pb.Filter {
if sf.website == "" && sf.prefix == "" && sf.uri == "" && sf.status == "" && sf.websiteRe == "" && sf.uriRe == "" {
return nil
}
f := &pb.Filter{}
if sf.website != "" {
f.Website = &sf.website
}
if sf.prefix != "" {
f.ClientPrefix = &sf.prefix
}
if sf.uri != "" {
f.HttpRequestUri = &sf.uri
}
if sf.status != "" {
n, op, ok := st.ParseStatusExpr(sf.status)
if !ok {
fmt.Fprintf(os.Stderr, "--status: invalid expression %q; use e.g. 200, !=200, >=400, <500\n", sf.status)
os.Exit(1)
}
f.HttpResponse = &n
f.StatusOp = op
}
if sf.websiteRe != "" {
f.WebsiteRegex = &sf.websiteRe
}
if sf.uriRe != "" {
f.UriRegex = &sf.uriRe
}
return f
}
func parseWindow(s string) pb.Window {
switch s {
case "1m":
return pb.Window_W1M
case "5m":
return pb.Window_W5M
case "15m":
return pb.Window_W15M
case "60m":
return pb.Window_W60M
case "6h":
return pb.Window_W6H
case "24h":
return pb.Window_W24H
default:
fmt.Fprintf(os.Stderr, "--window: unknown value %q; valid: 1m 5m 15m 60m 6h 24h\n", s)
os.Exit(1)
panic("unreachable")
}
}
func parseGroupBy(s string) pb.GroupBy {
switch s {
case "website":
return pb.GroupBy_WEBSITE
case "prefix":
return pb.GroupBy_CLIENT_PREFIX
case "uri":
return pb.GroupBy_REQUEST_URI
case "status":
return pb.GroupBy_HTTP_RESPONSE
default:
fmt.Fprintf(os.Stderr, "--group-by: unknown value %q; valid: website prefix uri status\n", s)
os.Exit(1)
panic("unreachable")
}
}
func dieUsage(fs *flag.FlagSet, msg string) {
fmt.Fprintln(os.Stderr, msg)
fs.PrintDefaults()
os.Exit(1)
}