From 3960e66f008bfb393f258f03b51c75a1934f69da Mon Sep 17 00:00:00 2001 From: tkuchiki Date: Sun, 24 Sep 2023 23:30:50 +0900 Subject: [PATCH 1/2] Add flags for flag management --- cmd/alp/cmd/count.go | 47 +-- cmd/alp/cmd/diff.go | 29 +- cmd/alp/cmd/flags.go | 921 ++++++++++++++++++++++++++++++++++++++++++ cmd/alp/cmd/json.go | 96 +---- cmd/alp/cmd/ltsv.go | 96 +---- cmd/alp/cmd/option.go | 223 ---------- cmd/alp/cmd/pcap.go | 56 +-- cmd/alp/cmd/regexp.go | 103 +---- cmd/alp/cmd/root.go | 16 +- options/option.go | 21 +- 10 files changed, 1010 insertions(+), 598 deletions(-) create mode 100644 cmd/alp/cmd/flags.go delete mode 100644 cmd/alp/cmd/option.go diff --git a/cmd/alp/cmd/count.go b/cmd/alp/cmd/count.go index 02616a1..4d737d9 100644 --- a/cmd/alp/cmd/count.go +++ b/cmd/alp/cmd/count.go @@ -3,53 +3,41 @@ package cmd import ( "os" + "github.com/tkuchiki/alp/options" + "github.com/spf13/cobra" "github.com/tkuchiki/alp/counter" - "github.com/tkuchiki/alp/helpers" - "github.com/tkuchiki/alp/options" "github.com/tkuchiki/alp/parsers" ) -func NewCountCmd() *cobra.Command { +func NewCountCmd(commandFlags *flags) *cobra.Command { var countCmd = &cobra.Command{ Use: "count", Short: "Count by log entries", Long: `Count by log entries`, RunE: func(cmd *cobra.Command, args []string) error { - file, err := cmd.PersistentFlags().GetString("file") + opts, err := commandFlags.createCountOptions(cmd) if err != nil { return err } - reverse, err := cmd.PersistentFlags().GetBool("reverse") + // TODO: start + // Remove these after implementing `alp (json|ltsv|regex) count`. + pattern, err := cmd.PersistentFlags().GetString("pattern") if err != nil { return err } - pattern, err := cmd.PersistentFlags().GetString("pattern") + format, err := cmd.PersistentFlags().GetString("format") if err != nil { return err } - opts := options.NewOptions() - opts = options.SetOptions(opts, - options.File(file), - options.Reverse(reverse), options.Pattern(pattern), + options.Format(format), ) - - format, err := cmd.PersistentFlags().GetString("format") - if err != nil { - return err - } - - keysStr, err := cmd.PersistentFlags().GetString("keys") - if err != nil { - return err - } - - keys := helpers.SplitCSV(keysStr) + // TODO: end cnter := counter.NewCounter(os.Stdout, os.Stderr, opts.Reverse) @@ -81,18 +69,21 @@ func NewCountCmd() *cobra.Command { cnter.SetParser(parser) - err = cnter.CountAndPrint(keys) + err = cnter.CountAndPrint(opts.Count.Keys) return err }, } + commandFlags.defineCountOptions(countCmd) + + // TODO: Remove these after implementing `alp (json|ltsv|regex) count`. + countCmd.PersistentFlags().StringP("pattern", "", options.DefaultPatternOption, "Regular expressions pattern matching the log") countCmd.PersistentFlags().StringP("format", "", "json", "Log format (json,ltsv,regexp)") - countCmd.PersistentFlags().StringP("pattern", "", options.DefaultPatternOption, "Regular expressions pattern matching the log (only use with --format=regexp)") - countCmd.PersistentFlags().StringP("file", "", "", "The access log file") - countCmd.PersistentFlags().BoolP("reverse", "r", false, "Sort results in reverse order") - countCmd.PersistentFlags().StringP("keys", "", "", "Log key names (comma separated)") - countCmd.MarkPersistentFlagRequired("keys") + + countCmd.Flags().SortFlags = false + countCmd.PersistentFlags().SortFlags = false + countCmd.InheritedFlags().SortFlags = false return countCmd } diff --git a/cmd/alp/cmd/diff.go b/cmd/alp/cmd/diff.go index 8b0367e..4968352 100644 --- a/cmd/alp/cmd/diff.go +++ b/cmd/alp/cmd/diff.go @@ -4,32 +4,17 @@ import ( "os" "github.com/spf13/cobra" - "github.com/tkuchiki/alp/options" "github.com/tkuchiki/alp/stats" ) -func NewDiffCmd() *cobra.Command { +func NewDiffCmd(commandFlags *flags) *cobra.Command { diffCmd := &cobra.Command{ Use: "diff ", Args: cobra.ExactArgs(2), Short: "Show the difference between the two profile results", Long: `Show the difference between the two profile results`, RunE: func(cmd *cobra.Command, args []string) error { - sortOptions := stats.NewSortOptions() - var opts *options.Options - - config, err := cmd.PersistentFlags().GetString("config") - if err != nil { - return err - } - - if config != "" { - bindCommonFlags(cmd) - opts, err = createOptionsFromConfig(cmd, sortOptions, config) - } else { - opts, err = createCommonOptionsFromFlags(cmd, sortOptions) - } - + opts, err := commandFlags.createDiffOptions(cmd) if err != nil { return err } @@ -45,7 +30,7 @@ func NewDiffCmd() *cobra.Command { } sts.SetOptions(opts) - sts.SetSortOptions(sortOptions) + sts.SetSortOptions(commandFlags.sortOptions) printOptions := stats.NewPrintOptions(opts.NoHeaders, opts.ShowFooters, opts.DecodeUri, opts.PaginationLimit) printer := stats.NewPrinter(os.Stdout, opts.Output, opts.Format, opts.Percentiles, printOptions) @@ -72,7 +57,7 @@ func NewDiffCmd() *cobra.Command { } toSts.SetOptions(opts) - toSts.SetSortOptions(sortOptions) + toSts.SetSortOptions(commandFlags.sortOptions) tof, err := os.Open(to) if err != nil { @@ -92,7 +77,11 @@ func NewDiffCmd() *cobra.Command { }, } - defineOptions(diffCmd) + commandFlags.defineDiffOptions(diffCmd) + + diffCmd.Flags().SortFlags = false + diffCmd.PersistentFlags().SortFlags = false + diffCmd.InheritedFlags().SortFlags = false return diffCmd } diff --git a/cmd/alp/cmd/flags.go b/cmd/alp/cmd/flags.go new file mode 100644 index 0000000..b2e3dcc --- /dev/null +++ b/cmd/alp/cmd/flags.go @@ -0,0 +1,921 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tkuchiki/alp/helpers" + "github.com/tkuchiki/alp/options" + "github.com/tkuchiki/alp/stats" +) + +const ( + flagConfig = "config" + flagFile = "file" + flagDump = "dump" + flagLoad = "load" + flagFormat = "format" + flagSort = "sort" + flagReverse = "reverse" + flagNoHeaders = "noheaders" + flagShowFooters = "show-footers" + flagLimit = "limit" + flagOutput = "output" + flagQueryString = "query-string" + flagQueryStringIgnoreValues = "qs-ignore-values" + flagLocation = "location" + flagDecodeUri = "decode-uri" + flagMatchingGroups = "matching-groups" + flagFilters = "filters" + flagPositionFile = "pos" + flagNoSavePositionFile = "nosave-pos" + flagPercentiles = "percentiles" + flagPage = "page" + + // json + flagJSONUriKey = "uri-key" + flagJSONMethodKey = "method-key" + flagJSONTimeKey = "time-key" + flagJSONRestimeKey = "restime-key" + flagJSONReqtimeKey = "reqtime-key" + flagJSONBodyBytesKey = "body-bytes-key" + flagJSONStatusKey = "status-key" + + // ltsv + flagLTSVUriLabel = "uri-label" + flagLTSVMethodLabel = "method-label" + flagLTSVTimeLabel = "time-label" + flagLTSVApptimeLabel = "apptime-label" + flagLTSVReqtimeLabel = "reqtime-label" + flagLTSVSizeLabel = "size-label" + flagLTSVStatusLabel = "status-label" + + // regexp + flagRegexpPattern = "pattern" + flagRegexpUriSubexp = "uri-subexp" + flagRegexpMethodSubexp = "method-subexp" + flagRegexpTimeSubexp = "time-subexp" + flagRegexpRestimeSubexp = "restime-subexp" + flagRegexpReqtimeSubexp = "reqtime-subexp" + flagRegexpBodyBytesSubexp = "body-bytes-subexp" + flagRegexpStatusSubexp = "status-subexp" + + // pcap + flagPcapPcapServerIP = "pcap-server-ip" + flagPcapPcapServerPort = "pcap-server-port" + + // count + flagCountKeys = "keys" +) + +type flags struct { + config string + sortOptions *stats.SortOptions +} + +func newFlags() *flags { + return &flags{ + sortOptions: stats.NewSortOptions(), + } +} + +func (f *flags) defineConfig(cmd *cobra.Command) { + cmd.PersistentFlags().StringVar(&f.config, flagConfig, "", "The configuration file") +} + +func (f *flags) defineFile(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagFile, "", "", "The access log file") +} + +func (f *flags) defineDump(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagDump, "", "", "Dump profiled data as YAML") +} + +func (f *flags) defineLoad(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagLoad, "", "", "Load the profiled YAML data") +} + +func (f *flags) defineFormat(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagFormat, "", options.DefaultFormatOption, "The output format (table, markdown, tsv, csv and html)") +} + +func (f *flags) defineSort(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagSort, "", options.DefaultSortOption, "Output the results in sorted order") +} + +func (f *flags) defineReverse(cmd *cobra.Command) { + cmd.PersistentFlags().BoolP(flagReverse, "r", false, "Sort results in reverse order") +} + +func (f *flags) defineNoHeaders(cmd *cobra.Command) { + cmd.PersistentFlags().BoolP(flagNoHeaders, "", false, "Output no header line at all (only --format=tsv, csv)") +} + +func (f *flags) defineShowFooters(cmd *cobra.Command) { + cmd.PersistentFlags().BoolP(flagShowFooters, "", false, "Output footer line at all (only --format=table, markdown)") +} + +func (f *flags) defineLimit(cmd *cobra.Command) { + cmd.PersistentFlags().IntP(flagLimit, "", options.DefaultLimitOption, "The maximum number of results to display") +} + +func (f *flags) defineOutput(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagOutput, "o", options.DefaultOutputOption, "Specifies the results to display, separated by commas") +} + +func (f *flags) defineQueryString(cmd *cobra.Command) { + cmd.PersistentFlags().BoolP(flagQueryString, "q", false, "Include the URI query string") +} + +func (f *flags) defineQueryStringIgnoreValues(cmd *cobra.Command) { + cmd.PersistentFlags().BoolP(flagQueryStringIgnoreValues, "", false, "Ignore the value of the query string. Replace all values with xxx (only use with -q)") +} + +func (f *flags) defineLocation(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagLocation, "", options.DefaultLocationOption, "Location name for the timezone") +} + +func (f *flags) defineDecodeUri(cmd *cobra.Command) { + cmd.PersistentFlags().BoolP(flagDecodeUri, "", false, "Decode the URI") +} + +func (f *flags) defineMatchingGroups(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagMatchingGroups, "m", "", "Specifies Query matching groups separated by commas") +} + +func (f *flags) defineFilters(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagFilters, "f", "", "Only the logs are profiled that match the conditions") +} + +func (f *flags) definePositionFile(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagPositionFile, "", "", "The position file") +} + +func (f *flags) defineNoSavePositionFile(cmd *cobra.Command) { + cmd.PersistentFlags().BoolP(flagNoSavePositionFile, "", false, "Do not save position file") +} + +func (f *flags) definePercentiles(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagPercentiles, "", "", "Specifies the percentiles separated by commas") +} + +func (f *flags) definePage(cmd *cobra.Command) { + cmd.PersistentFlags().IntP(flagPage, "", options.DefaultPaginationLimit, "Number of pages of pagination") +} + +func (f *flags) defineJSONUriKey(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagJSONUriKey, "", options.DefaultUriKeyOption, "Change the uri key") +} + +func (f *flags) defineJSONMethodKey(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagJSONMethodKey, "", options.DefaultMethodKeyOption, "Change the method key") +} + +func (f *flags) defineJSONTimeKey(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagJSONTimeKey, "", options.DefaultTimeKeyOption, "Change the time key") +} + +func (f *flags) defineJSONRestimeKey(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagJSONRestimeKey, "", options.DefaultResponseTimeKeyOption, "Change the response_time key") +} + +func (f *flags) defineJSONReqtimeKey(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagJSONReqtimeKey, "", options.DefaultRequestTimeKeyOption, "Change the request_time key") +} + +func (f *flags) defineJSONBodyBytesKey(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagJSONBodyBytesKey, "", options.DefaultBodyBytesKeyOption, "Change the body_bytes key") +} + +func (f *flags) defineJSONStatusKey(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagJSONStatusKey, "", options.DefaultStatusKeyOption, "Change the status key") +} + +func (f *flags) defineLTSVUriLabel(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagLTSVUriLabel, "", options.DefaultUriLabelOption, "Change the uri label") +} + +func (f *flags) defineLTSVMethodLabel(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagLTSVMethodLabel, "", options.DefaultMethodLabelOption, "Change the method label") +} + +func (f *flags) defineLTSVTimeLabel(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagLTSVTimeLabel, "", options.DefaultTimeLabelOption, "Change the time label") +} + +func (f *flags) defineLTSVApptimeLabel(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagLTSVApptimeLabel, "", options.DefaultApptimeLabelOption, "Change the apptime label") +} + +func (f *flags) defineLTSVReqtimeLabel(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagLTSVReqtimeLabel, "", options.DefaultReqtimeLabelOption, "Change the reqtime label") +} + +func (f *flags) defineLTSVSizeLabel(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagLTSVSizeLabel, "", options.DefaultSizeLabelOption, "Change the size label") +} + +func (f *flags) defineLTSVStatusLabel(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagLTSVStatusLabel, "", options.DefaultStatusLabelOption, "Change the status label") +} + +func (f *flags) defineRegexpPattern(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagRegexpPattern, "", options.DefaultPatternOption, "Regular expressions pattern matching the log") +} + +func (f *flags) defineRegexpUriSubexp(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagRegexpUriSubexp, "", options.DefaultUriSubexpOption, "Change the uri sub expression") +} + +func (f *flags) defineRegexpMethodSubexp(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagRegexpMethodSubexp, "", options.DefaultMethodSubexpOption, "Change the method sub expression") +} + +func (f *flags) defineRegexpTimeSubexp(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagRegexpTimeSubexp, "", options.DefaultTimeSubexpOption, "Change the time sub expression") +} + +func (f *flags) defineRegexpRestimeSubexp(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagRegexpRestimeSubexp, "", options.DefaultResponseTimeSubexpOption, "Change the response_time sub expression") +} + +func (f *flags) defineRegexpReqtimeSubexp(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagRegexpReqtimeSubexp, "", options.DefaultRequestTimeSubexpOption, "Change the request_time sub expression") +} + +func (f *flags) defineRegexpBodyBytesSubexp(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagRegexpBodyBytesSubexp, "", options.DefaultBodyBytesSubexpOption, "Change the body_bytes sub expression") +} + +func (f *flags) defineRegexpStatusSubexp(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagRegexpStatusSubexp, "", options.DefaultStatusSubexpOption, "Change the status sub expression") +} + +func (f *flags) definePcapPcapServerIP(cmd *cobra.Command) { + cmd.PersistentFlags().StringSliceP(flagPcapPcapServerIP, "", []string{options.DefaultPcapServerIPsOption[0]}, "HTTP server IP address of the captured packets") +} + +func (f *flags) definePcapPcapServerPort(cmd *cobra.Command) { + cmd.PersistentFlags().Uint16P(flagPcapPcapServerPort, "", options.DefaultPcapServerPortOption, "HTTP server TCP port of the captured packets") +} + +func (f *flags) defineCountKeys(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagCountKeys, "", "", "Log key names (comma separated)") + cmd.MarkPersistentFlagRequired(flagCountKeys) +} + +func (f *flags) defineGlobalOptions(cmd *cobra.Command) { + f.defineConfig(cmd) +} + +func (f *flags) defineProfileOptions(cmd *cobra.Command) { + f.defineFile(cmd) + f.defineDump(cmd) + f.defineLoad(cmd) + f.defineFormat(cmd) + f.defineSort(cmd) + f.defineReverse(cmd) + f.defineNoHeaders(cmd) + f.defineShowFooters(cmd) + f.defineLimit(cmd) + f.defineOutput(cmd) + f.defineQueryString(cmd) + f.defineQueryStringIgnoreValues(cmd) + f.defineLocation(cmd) + f.defineDecodeUri(cmd) + f.defineMatchingGroups(cmd) + f.defineFilters(cmd) + f.definePositionFile(cmd) + f.defineNoSavePositionFile(cmd) + f.definePercentiles(cmd) + f.definePage(cmd) +} + +func (f *flags) defineJSONOptions(cmd *cobra.Command) { + f.defineJSONUriKey(cmd) + f.defineJSONMethodKey(cmd) + f.defineJSONTimeKey(cmd) + f.defineJSONRestimeKey(cmd) + f.defineJSONReqtimeKey(cmd) + f.defineJSONBodyBytesKey(cmd) + f.defineJSONStatusKey(cmd) +} + +func (f *flags) defineLTSVOptions(cmd *cobra.Command) { + f.defineLTSVUriLabel(cmd) + f.defineLTSVMethodLabel(cmd) + f.defineLTSVTimeLabel(cmd) + f.defineLTSVApptimeLabel(cmd) + f.defineLTSVReqtimeLabel(cmd) + f.defineLTSVSizeLabel(cmd) + f.defineLTSVStatusLabel(cmd) +} + +func (f *flags) defineRegexpOptions(cmd *cobra.Command) { + f.defineRegexpPattern(cmd) + f.defineRegexpUriSubexp(cmd) + f.defineRegexpMethodSubexp(cmd) + f.defineRegexpTimeSubexp(cmd) + f.defineRegexpRestimeSubexp(cmd) + f.defineRegexpReqtimeSubexp(cmd) + f.defineRegexpBodyBytesSubexp(cmd) + f.defineRegexpStatusSubexp(cmd) +} + +func (f *flags) definePcapOptions(cmd *cobra.Command) { + f.definePcapPcapServerIP(cmd) + f.definePcapPcapServerPort(cmd) +} + +func (f *flags) defineCountOptions(cmd *cobra.Command) { + f.defineFile(cmd) + f.defineReverse(cmd) + f.defineCountKeys(cmd) +} + +func (f *flags) defineDiffOptions(cmd *cobra.Command) { + f.defineFormat(cmd) + f.defineSort(cmd) + f.defineReverse(cmd) + f.defineNoHeaders(cmd) + f.defineShowFooters(cmd) + f.defineLimit(cmd) + f.defineOutput(cmd) + f.defineQueryString(cmd) + f.defineQueryStringIgnoreValues(cmd) + f.defineLocation(cmd) + f.defineDecodeUri(cmd) + f.defineMatchingGroups(cmd) + f.defineFilters(cmd) + f.definePercentiles(cmd) + f.definePage(cmd) +} + +func (f *flags) bindFlags(cmd *cobra.Command) { + viper.BindPFlag("file", cmd.PersistentFlags().Lookup(flagFile)) + viper.BindPFlag("dump", cmd.PersistentFlags().Lookup(flagDump)) + viper.BindPFlag("load", cmd.PersistentFlags().Lookup(flagLoad)) + viper.BindPFlag("sort", cmd.PersistentFlags().Lookup(flagSort)) + viper.BindPFlag("reverse", cmd.PersistentFlags().Lookup(flagReverse)) + viper.BindPFlag("query_string", cmd.PersistentFlags().Lookup(flagQueryString)) + viper.BindPFlag("query_string_ignore_values", cmd.PersistentFlags().Lookup(flagQueryStringIgnoreValues)) + viper.BindPFlag("decode_uri", cmd.PersistentFlags().Lookup(flagDecodeUri)) + viper.BindPFlag("format", cmd.PersistentFlags().Lookup(flagFormat)) + viper.BindPFlag("noheaders", cmd.PersistentFlags().Lookup(flagNoHeaders)) + viper.BindPFlag("show_footers", cmd.PersistentFlags().Lookup(flagShowFooters)) + viper.BindPFlag("limit", cmd.PersistentFlags().Lookup(flagLimit)) + viper.BindPFlag("matching_groups", cmd.PersistentFlags().Lookup(flagMatchingGroups)) + viper.BindPFlag("filters", cmd.PersistentFlags().Lookup(flagFilters)) + viper.BindPFlag("pos_file", cmd.PersistentFlags().Lookup(flagPositionFile)) + viper.BindPFlag("nosave_pos", cmd.PersistentFlags().Lookup(flagNoSavePositionFile)) + viper.BindPFlag("location", cmd.PersistentFlags().Lookup(flagLocation)) + viper.BindPFlag("output", cmd.PersistentFlags().Lookup(flagOutput)) + viper.BindPFlag("pagenation_limit", cmd.PersistentFlags().Lookup(flagPage)) + + // json + viper.BindPFlag("json.uri_key", cmd.PersistentFlags().Lookup(flagJSONUriKey)) + viper.BindPFlag("json.method_key", cmd.PersistentFlags().Lookup(flagJSONMethodKey)) + viper.BindPFlag("json.time_key", cmd.PersistentFlags().Lookup(flagJSONTimeKey)) + viper.BindPFlag("json.restime_key", cmd.PersistentFlags().Lookup(flagJSONRestimeKey)) + viper.BindPFlag("json.reqtime_key", cmd.PersistentFlags().Lookup(flagJSONReqtimeKey)) + viper.BindPFlag("json.body_bytes_key", cmd.PersistentFlags().Lookup(flagJSONBodyBytesKey)) + viper.BindPFlag("json.status_key", cmd.PersistentFlags().Lookup(flagJSONStatusKey)) + + // ltsv + viper.BindPFlag("ltsv.uri_label", cmd.PersistentFlags().Lookup(flagLTSVUriLabel)) + viper.BindPFlag("ltsv.method_label", cmd.PersistentFlags().Lookup(flagLTSVMethodLabel)) + viper.BindPFlag("ltsv.time_label", cmd.PersistentFlags().Lookup(flagLTSVTimeLabel)) + viper.BindPFlag("ltsv.apptime_label", cmd.PersistentFlags().Lookup(flagLTSVApptimeLabel)) + viper.BindPFlag("ltsv.reqtime_label", cmd.PersistentFlags().Lookup(flagLTSVReqtimeLabel)) + viper.BindPFlag("ltsv.size_label", cmd.PersistentFlags().Lookup(flagLTSVSizeLabel)) + viper.BindPFlag("ltsv.status_label", cmd.PersistentFlags().Lookup(flagLTSVStatusLabel)) + + // regexp + viper.BindPFlag("regexp.pattern", cmd.PersistentFlags().Lookup(flagRegexpPattern)) + viper.BindPFlag("regexp.uri_subexp", cmd.PersistentFlags().Lookup(flagRegexpUriSubexp)) + viper.BindPFlag("regexp.method_subexp", cmd.PersistentFlags().Lookup(flagRegexpMethodSubexp)) + viper.BindPFlag("regexp.time_subexp", cmd.PersistentFlags().Lookup(flagRegexpTimeSubexp)) + viper.BindPFlag("regexp.restime_subexp", cmd.PersistentFlags().Lookup(flagRegexpRestimeSubexp)) + viper.BindPFlag("regexp.reqtime_subexp", cmd.PersistentFlags().Lookup(flagRegexpReqtimeSubexp)) + viper.BindPFlag("regexp.body_bytes_subexp", cmd.PersistentFlags().Lookup(flagRegexpBodyBytesSubexp)) + viper.BindPFlag("regexp.status_subexp", cmd.PersistentFlags().Lookup(flagRegexpStatusSubexp)) + + // pcap + viper.BindPFlag("pcap.server_port", cmd.PersistentFlags().Lookup(flagPcapPcapServerPort)) + + // count + viper.BindPFlag("count.keys", cmd.PersistentFlags().Lookup(flagCountKeys)) +} + +func (f *flags) createOptionsFromConfig(cmd *cobra.Command) (*options.Options, error) { + opts := options.NewOptions() + viper.SetConfigFile(f.config) + viper.SetConfigType("yaml") + + // Start workaround + // viper seems to merge slices, so we'll set empty slice and overwrite it manually. + opts.Percentiles = []int{} + opts.Pcap.ServerIPs = []string{} + + if err := viper.ReadInConfig(); err != nil { + return nil, err + } + + if err := viper.Unmarshal(opts); err != nil { + return nil, err + } + + if len(opts.Percentiles) == 0 { + opts.Percentiles = options.DefaultPercentilesOption + } + + if len(opts.Pcap.ServerIPs) == 0 { + opts.Pcap.ServerIPs = []string{options.DefaultPcapServerIPsOption[0]} + } + // End workaround + + percentilesFlag := cmd.PersistentFlags().Lookup(flagPercentiles) + if percentilesFlag != nil && percentilesFlag.Changed { + ps := cmd.PersistentFlags().Lookup(flagPercentiles).Value.String() + var percentiles []int + var err error + if ps != "" { + percentiles, err = helpers.SplitCSVIntoInts(ps) + if err != nil { + return nil, err + } + + if err = helpers.ValidatePercentiles(percentiles); err != nil { + return nil, err + } + } + opts.Percentiles = percentiles + } + + srvIPFlag := cmd.PersistentFlags().Lookup(flagPcapPcapServerIP) + if srvIPFlag != nil && srvIPFlag.Changed { + ips := cmd.PersistentFlags().Lookup(flagPcapPcapServerIP).Value.String() + opts.Pcap.ServerIPs = helpers.SplitCSV(ips) + } + + if err := f.sortOptions.SetAndValidate(opts.Sort); err != nil { + return nil, err + } + opts.Sort = f.sortOptions.SortType() + + return opts, nil +} + +func (f *flags) setOptions(cmd *cobra.Command, opts *options.Options, flags []string) (*options.Options, error) { + for _, flag := range flags { + switch flag { + case flagFile: + file, err := cmd.PersistentFlags().GetString(flagFile) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.File(file)) + case flagDump: + dump, err := cmd.PersistentFlags().GetString(flagDump) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.Dump(dump)) + case flagLoad: + load, err := cmd.PersistentFlags().GetString(flagLoad) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.Load(load)) + case flagFormat: + format, err := cmd.PersistentFlags().GetString(flagFormat) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.Format(format)) + case flagSort: + sort, err := cmd.PersistentFlags().GetString(flagSort) + if err != nil { + return nil, err + } + + err = f.sortOptions.SetAndValidate(sort) + if err != nil { + return nil, err + } + + opts = options.SetOptions(opts, options.Sort(sort)) + case flagReverse: + reverse, err := cmd.PersistentFlags().GetBool(flagReverse) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.Reverse(reverse)) + case flagNoHeaders: + noHeaders, err := cmd.PersistentFlags().GetBool(flagNoHeaders) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.NoHeaders(noHeaders)) + case flagShowFooters: + showFooters, err := cmd.PersistentFlags().GetBool(flagShowFooters) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.ShowFooters(showFooters)) + case flagLimit: + limit, err := cmd.PersistentFlags().GetInt(flagLimit) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.Limit(limit)) + case flagOutput: + output, err := cmd.PersistentFlags().GetString(flagOutput) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.Output(output)) + case flagQueryString: + queryString, err := cmd.PersistentFlags().GetBool(flagQueryString) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.QueryString(queryString)) + case flagQueryStringIgnoreValues: + queryStringIgnoreValues, err := cmd.PersistentFlags().GetBool(flagQueryStringIgnoreValues) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.QueryStringIgnoreValues(queryStringIgnoreValues)) + case flagDecodeUri: + decodeUri, err := cmd.PersistentFlags().GetBool(flagDecodeUri) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.DecodeUri(decodeUri)) + case flagLocation: + location, err := cmd.PersistentFlags().GetString(flagLocation) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.Location(location)) + case flagMatchingGroups: + matchingGroups, err := cmd.PersistentFlags().GetString(flagMatchingGroups) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.CSVGroups(matchingGroups)) + case flagFilters: + filters, err := cmd.PersistentFlags().GetString(flagFilters) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.Filters(filters)) + case flagPositionFile: + pos, err := cmd.PersistentFlags().GetString(flagPositionFile) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.PosFile(pos)) + case flagNoSavePositionFile: + noSavePos, err := cmd.PersistentFlags().GetBool(flagNoSavePositionFile) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.NoSavePos(noSavePos)) + case flagPercentiles: + ps, err := cmd.PersistentFlags().GetString(flagPercentiles) + if err != nil { + return nil, err + } + + var percentiles []int + if ps != "" { + percentiles, err = helpers.SplitCSVIntoInts(ps) + if err != nil { + return nil, err + } + + if err = helpers.ValidatePercentiles(percentiles); err != nil { + return nil, err + } + } + opts = options.SetOptions(opts, options.Percentiles(percentiles)) + case flagPage: + paginationLimit, err := cmd.PersistentFlags().GetInt(flagPage) + if err != nil { + return nil, err + } + opts = options.SetOptions(opts, options.PaginationLimit(paginationLimit)) + } + } + + return opts, nil +} + +func (f *flags) setProfileOptions(cmd *cobra.Command, opts *options.Options) (*options.Options, error) { + _flags := []string{ + flagConfig, + flagFile, + flagDump, + flagLoad, + flagFormat, + flagSort, + flagReverse, + flagNoHeaders, + flagShowFooters, + flagLimit, + flagOutput, + flagQueryString, + flagQueryStringIgnoreValues, + flagLocation, + flagDecodeUri, + flagMatchingGroups, + flagFilters, + flagPositionFile, + flagNoSavePositionFile, + flagPercentiles, + flagPage, + } + + return f.setOptions(cmd, opts, _flags) +} + +func (f *flags) setJSONOptions(cmd *cobra.Command, opts *options.Options) (*options.Options, error) { + uriKey, err := cmd.PersistentFlags().GetString(flagJSONUriKey) + if err != nil { + return nil, err + } + + methodKey, err := cmd.PersistentFlags().GetString(flagJSONMethodKey) + if err != nil { + return nil, err + } + + timeKey, err := cmd.PersistentFlags().GetString(flagJSONTimeKey) + if err != nil { + return nil, err + } + + responseTimeKey, err := cmd.PersistentFlags().GetString(flagJSONRestimeKey) + if err != nil { + return nil, err + } + + requestTimeKey, err := cmd.PersistentFlags().GetString(flagJSONReqtimeKey) + if err != nil { + return nil, err + } + + bodyBytesKey, err := cmd.PersistentFlags().GetString(flagJSONBodyBytesKey) + if err != nil { + return nil, err + } + + statusKey, err := cmd.PersistentFlags().GetString(flagJSONStatusKey) + if err != nil { + return nil, err + } + + return options.SetOptions(opts, + options.UriKey(uriKey), + options.MethodKey(methodKey), + options.TimeKey(timeKey), + options.ResponseTimeKey(responseTimeKey), + options.RequestTimeKey(requestTimeKey), + options.BodyBytesKey(bodyBytesKey), + options.StatusKey(statusKey), + ), nil +} + +func (f *flags) setLTSVOptions(cmd *cobra.Command, opts *options.Options) (*options.Options, error) { + uriLabel, err := cmd.PersistentFlags().GetString(flagLTSVUriLabel) + if err != nil { + return nil, err + } + + methodLabel, err := cmd.PersistentFlags().GetString(flagLTSVMethodLabel) + if err != nil { + return nil, err + } + + timeLabel, err := cmd.PersistentFlags().GetString(flagLTSVTimeLabel) + if err != nil { + return nil, err + } + + appTimeLabel, err := cmd.PersistentFlags().GetString(flagLTSVApptimeLabel) + if err != nil { + return nil, err + } + + reqTimeLabel, err := cmd.PersistentFlags().GetString(flagLTSVReqtimeLabel) + if err != nil { + return nil, err + } + + sizeLabel, err := cmd.PersistentFlags().GetString(flagLTSVSizeLabel) + if err != nil { + return nil, err + } + + statusLabel, err := cmd.PersistentFlags().GetString(flagLTSVStatusLabel) + if err != nil { + return nil, err + } + + return options.SetOptions(opts, + options.UriLabel(uriLabel), + options.MethodLabel(methodLabel), + options.TimeLabel(timeLabel), + options.ApptimeLabel(appTimeLabel), + options.ReqtimeLabel(reqTimeLabel), + options.SizeLabel(sizeLabel), + options.StatusLabel(statusLabel), + ), nil +} + +func (f *flags) setRegexpOptions(cmd *cobra.Command, opts *options.Options) (*options.Options, error) { + pattern, err := cmd.PersistentFlags().GetString(flagRegexpPattern) + if err != nil { + return nil, err + } + + uriSubexp, err := cmd.PersistentFlags().GetString(flagRegexpUriSubexp) + if err != nil { + return nil, err + } + + methodSubexp, err := cmd.PersistentFlags().GetString(flagRegexpMethodSubexp) + if err != nil { + return nil, err + } + + timeSubexp, err := cmd.PersistentFlags().GetString(flagRegexpTimeSubexp) + if err != nil { + return nil, err + } + restimeSubexp, err := cmd.PersistentFlags().GetString(flagRegexpRestimeSubexp) + if err != nil { + return nil, err + } + + reqtimeSubexp, err := cmd.PersistentFlags().GetString(flagRegexpReqtimeSubexp) + if err != nil { + return nil, err + } + + bodyBytesSubexp, err := cmd.PersistentFlags().GetString(flagRegexpBodyBytesSubexp) + if err != nil { + return nil, err + } + + statusSubexp, err := cmd.PersistentFlags().GetString(flagRegexpStatusSubexp) + if err != nil { + return nil, err + } + + return options.SetOptions(opts, + options.Pattern(pattern), + options.UriSubexp(uriSubexp), + options.MethodSubexp(methodSubexp), + options.TimeSubexp(timeSubexp), + options.ResponseTimeSubexp(restimeSubexp), + options.RequestTimeSubexp(reqtimeSubexp), + options.BodyBytesSubexp(bodyBytesSubexp), + options.StatusSubexp(statusSubexp), + ), nil +} + +func (f *flags) setPcapOptions(cmd *cobra.Command, opts *options.Options) (*options.Options, error) { + serverIPs, err := cmd.PersistentFlags().GetStringSlice(flagPcapPcapServerIP) + if err != nil { + return nil, err + } + + serverPort, err := cmd.PersistentFlags().GetUint16(flagPcapPcapServerPort) + if err != nil { + return nil, err + } + + return options.SetOptions(opts, + options.PcapServerIPs(serverIPs), + options.PcapServerPort(serverPort), + ), nil +} + +func (f *flags) setCountOptions(cmd *cobra.Command, opts *options.Options) (*options.Options, error) { + keys, err := cmd.PersistentFlags().GetString(flagCountKeys) + if err != nil { + return nil, err + } + + return options.SetOptions(opts, + options.CountKeys(helpers.SplitCSV(keys)), + ), nil +} + +func (f *flags) setDiffOptions(cmd *cobra.Command, opts *options.Options) (*options.Options, error) { + _flags := []string{ + flagFormat, + flagSort, + flagReverse, + flagNoHeaders, + flagShowFooters, + flagLimit, + flagOutput, + flagQueryString, + flagQueryStringIgnoreValues, + flagLocation, + flagDecodeUri, + flagMatchingGroups, + flagFilters, + flagPercentiles, + flagPage, + } + + return f.setOptions(cmd, opts, _flags) +} + +func (f *flags) createJSONOptions(cmd *cobra.Command) (*options.Options, error) { + if f.config != "" { + f.bindFlags(cmd) + return f.createOptionsFromConfig(cmd) + } + + opts, err := f.setProfileOptions(cmd, options.NewOptions()) + if err != nil { + return nil, err + } + + return f.setJSONOptions(cmd, opts) +} + +func (f *flags) createLTSVOptions(cmd *cobra.Command) (*options.Options, error) { + if f.config != "" { + f.bindFlags(cmd) + return f.createOptionsFromConfig(cmd) + } + + opts, err := f.setProfileOptions(cmd, options.NewOptions()) + if err != nil { + return nil, err + } + + return f.setLTSVOptions(cmd, opts) +} + +func (f *flags) createRegexpOptions(cmd *cobra.Command) (*options.Options, error) { + if f.config != "" { + f.bindFlags(cmd) + return f.createOptionsFromConfig(cmd) + } + + opts, err := f.setProfileOptions(cmd, options.NewOptions()) + if err != nil { + return nil, err + } + + return f.setRegexpOptions(cmd, opts) +} + +func (f *flags) createPcapOptions(cmd *cobra.Command) (*options.Options, error) { + if f.config != "" { + f.bindFlags(cmd) + return f.createOptionsFromConfig(cmd) + } + + opts, err := f.setProfileOptions(cmd, options.NewOptions()) + if err != nil { + return nil, err + } + + return f.setPcapOptions(cmd, opts) +} + +func (f *flags) createCountOptions(cmd *cobra.Command) (*options.Options, error) { + if f.config != "" { + f.bindFlags(cmd) + return f.createOptionsFromConfig(cmd) + } + + flags := []string{ + flagFile, + flagReverse, + } + + opts, err := f.setOptions(cmd, options.NewOptions(), flags) + if err != nil { + return nil, err + } + + return f.setCountOptions(cmd, opts) +} + +func (f *flags) createDiffOptions(cmd *cobra.Command) (*options.Options, error) { + if f.config != "" { + f.bindFlags(cmd) + return f.createOptionsFromConfig(cmd) + } + + return f.setDiffOptions(cmd, options.NewOptions()) +} diff --git a/cmd/alp/cmd/json.go b/cmd/alp/cmd/json.go index a5c462f..2e34940 100644 --- a/cmd/alp/cmd/json.go +++ b/cmd/alp/cmd/json.go @@ -3,23 +3,19 @@ package cmd import ( "os" - "github.com/tkuchiki/alp/options" "github.com/tkuchiki/alp/parsers" "github.com/tkuchiki/alp/profiler" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tkuchiki/alp/stats" ) -func NewJSONCmd() *cobra.Command { +func NewJSONCmd(commandFlags *flags) *cobra.Command { var jsonCmd = &cobra.Command{ Use: "json", Short: "Profile the logs for JSON", Long: `Profile the logs for JSON`, RunE: func(cmd *cobra.Command, args []string) error { - sortOptions := stats.NewSortOptions() - opts, err := createJSONOptions(cmd, sortOptions) + opts, err := commandFlags.createJSONOptions(cmd) if err != nil { return err } @@ -36,94 +32,18 @@ func NewJSONCmd() *cobra.Command { opts.JSON.ResponseTimeKey, opts.JSON.RequestTimeKey, opts.JSON.BodyBytesKey, opts.JSON.StatusKey) parser := parsers.NewJSONParser(f, keys, opts.QueryString, opts.QueryStringIgnoreValues) - err = prof.Run(sortOptions, parser) + err = prof.Run(commandFlags.sortOptions, parser) return err }, } - defineOptions(jsonCmd) + commandFlags.defineProfileOptions(jsonCmd) + commandFlags.defineJSONOptions(jsonCmd) - jsonCmd.PersistentFlags().StringP("uri-key", "", options.DefaultUriKeyOption, "Change the uri key") - jsonCmd.PersistentFlags().StringP("method-key", "", options.DefaultMethodKeyOption, "Change the method key") - jsonCmd.PersistentFlags().StringP("time-key", "", options.DefaultTimeKeyOption, "Change the time key") - jsonCmd.PersistentFlags().StringP("restime-key", "", options.DefaultResponseTimeKeyOption, "Change the response_time key") - jsonCmd.PersistentFlags().StringP("reqtime-key", "", options.DefaultRequestTimeKeyOption, "Change the request_time key") - jsonCmd.PersistentFlags().StringP("body-bytes-key", "", options.DefaultBodyBytesKeyOption, "Change the body_bytes key") - jsonCmd.PersistentFlags().StringP("status-key", "", options.DefaultStatusKeyOption, "Change the status key") + jsonCmd.Flags().SortFlags = false + jsonCmd.PersistentFlags().SortFlags = false + jsonCmd.InheritedFlags().SortFlags = false return jsonCmd } - -func createJSONOptions(cmd *cobra.Command, sortOptions *stats.SortOptions) (*options.Options, error) { - config, err := cmd.PersistentFlags().GetString("config") - if err != nil { - return nil, err - } - if config != "" { - bindCommonFlags(cmd) - bindJSONFlags(cmd) - return createOptionsFromConfig(cmd, sortOptions, config) - } - - opts, err := createCommonOptionsFromFlags(cmd, sortOptions) - if err != nil { - return nil, err - } - - uriKey, err := cmd.PersistentFlags().GetString("uri-key") - if err != nil { - return nil, err - } - - methodKey, err := cmd.PersistentFlags().GetString("method-key") - if err != nil { - return nil, err - } - - timeKey, err := cmd.PersistentFlags().GetString("time-key") - if err != nil { - return nil, err - } - - responseTimeKey, err := cmd.PersistentFlags().GetString("restime-key") - if err != nil { - return nil, err - } - - requestTimeKey, err := cmd.PersistentFlags().GetString("reqtime-key") - if err != nil { - return nil, err - } - - bodyBytesKey, err := cmd.PersistentFlags().GetString("body-bytes-key") - if err != nil { - return nil, err - } - - statusKey, err := cmd.PersistentFlags().GetString("status-key") - if err != nil { - return nil, err - } - - return options.SetOptions(opts, - options.UriKey(uriKey), - options.MethodKey(methodKey), - options.TimeKey(timeKey), - options.ResponseTimeKey(responseTimeKey), - options.RequestTimeKey(requestTimeKey), - options.BodyBytesKey(bodyBytesKey), - options.StatusKey(statusKey), - ), nil -} - -func bindJSONFlags(cmd *cobra.Command) { - viper.BindPFlag("json.uri_key", cmd.PersistentFlags().Lookup("uri-key")) - viper.BindPFlag("json.method_key", cmd.PersistentFlags().Lookup("method-key")) - viper.BindPFlag("json.time_key", cmd.PersistentFlags().Lookup("time-key")) - viper.BindPFlag("json.restime_key", cmd.PersistentFlags().Lookup("restime-key")) - viper.BindPFlag("json.reqtime_key", cmd.PersistentFlags().Lookup("reqtime-key")) - viper.BindPFlag("json.body_bytes_key", cmd.PersistentFlags().Lookup("body-bytes-key")) - viper.BindPFlag("json.status_key", cmd.PersistentFlags().Lookup("status-key")) - -} diff --git a/cmd/alp/cmd/ltsv.go b/cmd/alp/cmd/ltsv.go index 3648d78..d6afe32 100644 --- a/cmd/alp/cmd/ltsv.go +++ b/cmd/alp/cmd/ltsv.go @@ -3,23 +3,19 @@ package cmd import ( "os" - "github.com/tkuchiki/alp/options" "github.com/tkuchiki/alp/parsers" "github.com/tkuchiki/alp/profiler" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tkuchiki/alp/stats" ) -func NewLTSVCmd() *cobra.Command { +func NewLTSVCmd(commandFlags *flags) *cobra.Command { var ltsvCmd = &cobra.Command{ Use: "ltsv", Short: "Profile the logs for LTSV", Long: `Profile the logs for LTSV`, RunE: func(cmd *cobra.Command, args []string) error { - sortOptions := stats.NewSortOptions() - opts, err := createLTSVOptions(cmd, sortOptions) + opts, err := commandFlags.createLTSVOptions(cmd) if err != nil { return err } @@ -37,94 +33,18 @@ func NewLTSVCmd() *cobra.Command { ) parser := parsers.NewLTSVParser(f, label, opts.QueryString, opts.QueryStringIgnoreValues) - err = prof.Run(sortOptions, parser) + err = prof.Run(commandFlags.sortOptions, parser) return err }, } - defineOptions(ltsvCmd) + commandFlags.defineProfileOptions(ltsvCmd) + commandFlags.defineLTSVOptions(ltsvCmd) - ltsvCmd.PersistentFlags().StringP("uri-label", "", options.DefaultUriLabelOption, "Change the uri label") - ltsvCmd.PersistentFlags().StringP("method-label", "", options.DefaultMethodLabelOption, "Change the method label") - ltsvCmd.PersistentFlags().StringP("time-label", "", options.DefaultTimeLabelOption, "Change the time label") - ltsvCmd.PersistentFlags().StringP("apptime-label", "", options.DefaultApptimeLabelOption, "Change the apptime label") - ltsvCmd.PersistentFlags().StringP("reqtime-label", "", options.DefaultReqtimeLabelOption, "Change the reqtime label") - ltsvCmd.PersistentFlags().StringP("size-label", "", options.DefaultSizeLabelOption, "Change the size label") - ltsvCmd.PersistentFlags().StringP("status-label", "", options.DefaultStatusLabelOption, "Change the status label") + ltsvCmd.Flags().SortFlags = false + ltsvCmd.PersistentFlags().SortFlags = false + ltsvCmd.InheritedFlags().SortFlags = false return ltsvCmd } - -func createLTSVOptions(cmd *cobra.Command, sortOptions *stats.SortOptions) (*options.Options, error) { - config, err := cmd.PersistentFlags().GetString("config") - if err != nil { - return nil, err - } - if config != "" { - bindCommonFlags(cmd) - bindLTSVFlags(cmd) - return createOptionsFromConfig(cmd, sortOptions, config) - } - - opts, err := createCommonOptionsFromFlags(cmd, sortOptions) - if err != nil { - return nil, err - } - - uriLabel, err := cmd.PersistentFlags().GetString("uri-label") - if err != nil { - return nil, err - } - - methodLabel, err := cmd.PersistentFlags().GetString("method-label") - if err != nil { - return nil, err - } - - timeLabel, err := cmd.PersistentFlags().GetString("time-label") - if err != nil { - return nil, err - } - - appTimeLabel, err := cmd.PersistentFlags().GetString("apptime-label") - if err != nil { - return nil, err - } - - reqTimeLabel, err := cmd.PersistentFlags().GetString("reqtime-label") - if err != nil { - return nil, err - } - - sizeLabel, err := cmd.PersistentFlags().GetString("size-label") - if err != nil { - return nil, err - } - - statusLabel, err := cmd.PersistentFlags().GetString("status-label") - if err != nil { - return nil, err - } - - return options.SetOptions(opts, - // ltsv - options.UriLabel(uriLabel), - options.MethodLabel(methodLabel), - options.TimeLabel(timeLabel), - options.ApptimeLabel(appTimeLabel), - options.ReqtimeLabel(reqTimeLabel), - options.SizeLabel(sizeLabel), - options.StatusLabel(statusLabel), - ), nil -} - -func bindLTSVFlags(cmd *cobra.Command) { - viper.BindPFlag("ltsv.uri_label", cmd.PersistentFlags().Lookup("uri-label")) - viper.BindPFlag("ltsv.method_label", cmd.PersistentFlags().Lookup("method-label")) - viper.BindPFlag("ltsv.time_label", cmd.PersistentFlags().Lookup("time-label")) - viper.BindPFlag("ltsv.apptime_label", cmd.PersistentFlags().Lookup("apptime-label")) - viper.BindPFlag("ltsv.reqtime_label", cmd.PersistentFlags().Lookup("reqtime-label")) - viper.BindPFlag("ltsv.size_label", cmd.PersistentFlags().Lookup("size-label")) - viper.BindPFlag("ltsv.status_label", cmd.PersistentFlags().Lookup("status-label")) -} diff --git a/cmd/alp/cmd/option.go b/cmd/alp/cmd/option.go deleted file mode 100644 index 06b33f3..0000000 --- a/cmd/alp/cmd/option.go +++ /dev/null @@ -1,223 +0,0 @@ -package cmd - -import ( - "github.com/tkuchiki/alp/helpers" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tkuchiki/alp/options" - "github.com/tkuchiki/alp/stats" -) - -func defineOptions(cmd *cobra.Command) { - cmd.PersistentFlags().StringP("config", "", "", "The configuration file") - cmd.PersistentFlags().StringP("file", "", "", "The access log file") - cmd.PersistentFlags().StringP("dump", "", "", "Dump profiled data as YAML") - cmd.PersistentFlags().StringP("load", "", "", "Load the profiled YAML data") - cmd.PersistentFlags().StringP("format", "", options.DefaultFormatOption, "The output format (table, markdown, tsv, csv and html)") - cmd.PersistentFlags().StringP("sort", "", options.DefaultSortOption, "Output the results in sorted order") - cmd.PersistentFlags().BoolP("reverse", "r", false, "Sort results in reverse order") - cmd.PersistentFlags().BoolP("noheaders", "", false, "Output no header line at all (only --format=tsv, csv)") - cmd.PersistentFlags().BoolP("show-footers", "", false, "Output footer line at all (only --format=table, markdown)") - cmd.PersistentFlags().IntP("limit", "", options.DefaultLimitOption, "The maximum number of results to display") - cmd.PersistentFlags().StringP("output", "o", options.DefaultOutputOption, "Specifies the results to display, separated by commas") - cmd.PersistentFlags().BoolP("query-string", "q", false, "Include the URI query string") - cmd.PersistentFlags().BoolP("qs-ignore-values", "", false, "Ignore the value of the query string. Replace all values with xxx (only use with -q)") - cmd.PersistentFlags().StringP("location", "", options.DefaultLocationOption, "Location name for the timezone") - cmd.PersistentFlags().BoolP("decode-uri", "", false, "Decode the URI") - cmd.PersistentFlags().StringP("matching-groups", "m", "", "Specifies Query matching groups separated by commas") - cmd.PersistentFlags().StringP("filters", "f", "", "Only the logs are profiled that match the conditions") - cmd.PersistentFlags().StringP("pos", "", "", "The position file") - cmd.PersistentFlags().BoolP("nosave-pos", "", false, "Do not save position file") - cmd.PersistentFlags().StringP("percentiles", "", "", "Specifies the percentiles separated by commas") - cmd.PersistentFlags().IntP("page", "", options.DefaultPaginationLimit, "Number of pages of pagination") - -} - -func createCommonOptionsFromFlags(cmd *cobra.Command, sortOptions *stats.SortOptions) (*options.Options, error) { - file, err := cmd.PersistentFlags().GetString("file") - if err != nil { - return nil, err - } - - dump, err := cmd.PersistentFlags().GetString("dump") - if err != nil { - return nil, err - } - - load, err := cmd.PersistentFlags().GetString("load") - if err != nil { - return nil, err - } - - format, err := cmd.PersistentFlags().GetString("format") - if err != nil { - return nil, err - } - - sort, err := cmd.PersistentFlags().GetString("sort") - if err != nil { - return nil, err - } - - err = sortOptions.SetAndValidate(sort) - if err != nil { - return nil, err - } - - reverse, err := cmd.PersistentFlags().GetBool("reverse") - if err != nil { - return nil, err - } - - noHeaders, err := cmd.PersistentFlags().GetBool("noheaders") - if err != nil { - return nil, err - } - - showFooters, err := cmd.PersistentFlags().GetBool("show-footers") - if err != nil { - return nil, err - } - - limit, err := cmd.PersistentFlags().GetInt("limit") - if err != nil { - return nil, err - } - - output, err := cmd.PersistentFlags().GetString("output") - if err != nil { - return nil, err - } - - queryString, err := cmd.PersistentFlags().GetBool("query-string") - if err != nil { - return nil, err - } - - queryStringIgnoreValues, err := cmd.PersistentFlags().GetBool("qs-ignore-values") - if err != nil { - return nil, err - } - - decodeUri, err := cmd.PersistentFlags().GetBool("decode-uri") - if err != nil { - return nil, err - } - - location, err := cmd.PersistentFlags().GetString("location") - if err != nil { - return nil, err - } - - matchingGroups, err := cmd.PersistentFlags().GetString("matching-groups") - if err != nil { - return nil, err - } - - filters, err := cmd.PersistentFlags().GetString("filters") - if err != nil { - return nil, err - } - - pos, err := cmd.PersistentFlags().GetString("pos") - if err != nil { - return nil, err - } - - noSavePos, err := cmd.PersistentFlags().GetBool("nosave-pos") - if err != nil { - return nil, err - } - - ps, err := cmd.PersistentFlags().GetString("percentiles") - if err != nil { - return nil, err - } - - var percentiles []int - if ps != "" { - percentiles, err = helpers.SplitCSVIntoInts(ps) - if err != nil { - return nil, err - } - - if err = helpers.ValidatePercentiles(percentiles); err != nil { - return nil, err - } - } - - paginationLimit, err := cmd.PersistentFlags().GetInt("page") - if err != nil { - return nil, err - } - - opts := options.NewOptions() - - return options.SetOptions(opts, - options.File(file), - options.Dump(dump), - options.Load(load), - options.Sort(sortOptions.SortType()), - options.Reverse(reverse), - options.Format(format), - options.Limit(limit), - options.Output(output), - options.QueryString(queryString), - options.QueryStringIgnoreValues(queryStringIgnoreValues), - options.DecodeUri(decodeUri), - options.Location(location), - options.NoHeaders(noHeaders), - options.ShowFooters(showFooters), - options.CSVGroups(matchingGroups), - options.Filters(filters), - options.PosFile(pos), - options.NoSavePos(noSavePos), - options.Percentiles(percentiles), - options.PaginationLimit(paginationLimit), - ), nil -} - -func createOptionsFromConfig(cmd *cobra.Command, sortOptions *stats.SortOptions, config string) (*options.Options, error) { - opts := options.NewOptions() - viper.SetConfigFile(config) - viper.SetConfigType("yaml") - - if err := viper.ReadInConfig(); err != nil { - return nil, err - } - - if err := viper.Unmarshal(opts); err != nil { - return nil, err - } - - if err := sortOptions.SetAndValidate(opts.Sort); err != nil { - return nil, err - } - opts.Sort = sortOptions.SortType() - - return opts, nil -} - -func bindCommonFlags(cmd *cobra.Command) { - viper.BindPFlag("file", cmd.PersistentFlags().Lookup("file")) - viper.BindPFlag("dump", cmd.PersistentFlags().Lookup("dump")) - viper.BindPFlag("load", cmd.PersistentFlags().Lookup("load")) - viper.BindPFlag("sort", cmd.PersistentFlags().Lookup("sort")) - viper.BindPFlag("reverse", cmd.PersistentFlags().Lookup("reverse")) - viper.BindPFlag("query_string", cmd.PersistentFlags().Lookup("query-string")) - viper.BindPFlag("query_string_ignore_values", cmd.PersistentFlags().Lookup("qs-ignore-values")) - viper.BindPFlag("decode_uri", cmd.PersistentFlags().Lookup("decode-uri")) - viper.BindPFlag("format", cmd.PersistentFlags().Lookup("format")) - viper.BindPFlag("noheaders", cmd.PersistentFlags().Lookup("noheaders")) - viper.BindPFlag("show_footers", cmd.PersistentFlags().Lookup("show-footers")) - viper.BindPFlag("limit", cmd.PersistentFlags().Lookup("limit")) - viper.BindPFlag("matching_groups", cmd.PersistentFlags().Lookup("matching-groups")) - viper.BindPFlag("filters", cmd.PersistentFlags().Lookup("filters")) - viper.BindPFlag("pos_file", cmd.PersistentFlags().Lookup("pos")) - viper.BindPFlag("nosave_pos", cmd.PersistentFlags().Lookup("nosave-pos")) - viper.BindPFlag("location", cmd.PersistentFlags().Lookup("location")) - viper.BindPFlag("output", cmd.PersistentFlags().Lookup("output")) - viper.BindPFlag("percentiles", cmd.PersistentFlags().Lookup("percentiles")) - viper.BindPFlag("pagenation_limit", cmd.PersistentFlags().Lookup("page")) -} diff --git a/cmd/alp/cmd/pcap.go b/cmd/alp/cmd/pcap.go index 49117cf..57905f7 100644 --- a/cmd/alp/cmd/pcap.go +++ b/cmd/alp/cmd/pcap.go @@ -3,23 +3,19 @@ package cmd import ( "os" - "github.com/tkuchiki/alp/options" "github.com/tkuchiki/alp/parsers" "github.com/tkuchiki/alp/profiler" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tkuchiki/alp/stats" ) -func NewPcapCmd() *cobra.Command { +func NewPcapCmd(commandFlags *flags) *cobra.Command { var pcapCmd = &cobra.Command{ Use: "pcap", Short: "Profile the HTTP requests for captured packets", Long: `Profile the HTTP requests for captured packets`, RunE: func(cmd *cobra.Command, args []string) error { - sortOptions := stats.NewSortOptions() - opts, err := createPcapOptions(cmd, sortOptions) + opts, err := commandFlags.createPcapOptions(cmd) if err != nil { return err } @@ -37,54 +33,18 @@ func NewPcapCmd() *cobra.Command { return err } - err = prof.Run(sortOptions, parser) + err = prof.Run(commandFlags.sortOptions, parser) return err }, } - defineOptions(pcapCmd) + commandFlags.defineProfileOptions(pcapCmd) + commandFlags.definePcapOptions(pcapCmd) - pcapCmd.PersistentFlags().StringSliceP("pcap-server-ip", "", []string{options.DefaultPcapServerIPsOption[0]}, "HTTP server IP address of the captured packets") - pcapCmd.PersistentFlags().Uint16P("pcap-server-port", "", options.DefaultPcapServerPortOption, "HTTP server TCP port of the captured packets") + pcapCmd.Flags().SortFlags = false + pcapCmd.PersistentFlags().SortFlags = false + pcapCmd.InheritedFlags().SortFlags = false return pcapCmd } - -func createPcapOptions(cmd *cobra.Command, sortOptions *stats.SortOptions) (*options.Options, error) { - config, err := cmd.PersistentFlags().GetString("config") - if err != nil { - return nil, err - } - if config != "" { - bindCommonFlags(cmd) - bindPcapFlags(cmd) - return createOptionsFromConfig(cmd, sortOptions, config) - } - - opts, err := createCommonOptionsFromFlags(cmd, sortOptions) - if err != nil { - return nil, err - } - - serverIPs, err := cmd.PersistentFlags().GetStringSlice("pcap-server-ip") - if err != nil { - return nil, err - } - - serverPort, err := cmd.PersistentFlags().GetUint16("pcap-server-port") - if err != nil { - return nil, err - } - - return options.SetOptions(opts, - options.PcapServerIPs(serverIPs), - options.PcapServerPort(serverPort), - ), nil -} - -func bindPcapFlags(cmd *cobra.Command) { - viper.BindPFlag("pcap.server_ips", cmd.PersistentFlags().Lookup("pcap-server-ip")) - viper.BindPFlag("pcap.server_port", cmd.PersistentFlags().Lookup("pcap-server-port")) - -} diff --git a/cmd/alp/cmd/regexp.go b/cmd/alp/cmd/regexp.go index 4c97abf..b86e999 100644 --- a/cmd/alp/cmd/regexp.go +++ b/cmd/alp/cmd/regexp.go @@ -3,23 +3,19 @@ package cmd import ( "os" - "github.com/tkuchiki/alp/options" "github.com/tkuchiki/alp/parsers" "github.com/tkuchiki/alp/profiler" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tkuchiki/alp/stats" ) -func NewRegexpCmd() *cobra.Command { +func NewRegexpCmd(commandFlags *flags) *cobra.Command { var regexpCmd = &cobra.Command{ Use: "regexp", Short: "Profile the logs that match a regular expression", Long: `Profile the logs that match a regular expression`, RunE: func(cmd *cobra.Command, args []string) error { - sortOptions := stats.NewSortOptions() - opts, err := createRegexpOptions(cmd, sortOptions) + opts, err := commandFlags.createRegexpOptions(cmd) if err != nil { return err } @@ -39,101 +35,18 @@ func NewRegexpCmd() *cobra.Command { return err } - err = prof.Run(sortOptions, parser) + err = prof.Run(commandFlags.sortOptions, parser) return err }, } - defineOptions(regexpCmd) + commandFlags.defineProfileOptions(regexpCmd) + commandFlags.defineRegexpOptions(regexpCmd) - regexpCmd.PersistentFlags().StringP("pattern", "", options.DefaultPatternOption, "Regular expressions pattern matching the log") - regexpCmd.PersistentFlags().StringP("uri-subexp", "", options.DefaultUriSubexpOption, "Change the uri sub expression") - regexpCmd.PersistentFlags().StringP("method-subexp", "", options.DefaultMethodSubexpOption, "Change the method sub expression") - regexpCmd.PersistentFlags().StringP("time-subexp", "", options.DefaultTimeSubexpOption, "Change the time sub expression") - regexpCmd.PersistentFlags().StringP("restime-subexp", "", options.DefaultResponseTimeSubexpOption, "Change the response_time sub expression") - regexpCmd.PersistentFlags().StringP("reqtime-subexp", "", options.DefaultRequestTimeSubexpOption, "Change the request_time sub expression") - regexpCmd.PersistentFlags().StringP("body-bytes-subexp", "", options.DefaultBodyBytesSubexpOption, "Change the body_bytes sub expression") - regexpCmd.PersistentFlags().StringP("status-subexp", "", options.DefaultStatusSubexpOption, "Change the status sub expression") + regexpCmd.Flags().SortFlags = false + regexpCmd.PersistentFlags().SortFlags = false + regexpCmd.InheritedFlags().SortFlags = false return regexpCmd } - -func createRegexpOptions(cmd *cobra.Command, sortOptions *stats.SortOptions) (*options.Options, error) { - config, err := cmd.PersistentFlags().GetString("config") - if err != nil { - return nil, err - } - if config != "" { - bindCommonFlags(cmd) - bindRegexpFlags(cmd) - return createOptionsFromConfig(cmd, sortOptions, config) - } - - opts, err := createCommonOptionsFromFlags(cmd, sortOptions) - if err != nil { - return nil, err - } - - pattern, err := cmd.PersistentFlags().GetString("pattern") - if err != nil { - return nil, err - } - - uriSubexp, err := cmd.PersistentFlags().GetString("uri-subexp") - if err != nil { - return nil, err - } - - methodSubexp, err := cmd.PersistentFlags().GetString("method-subexp") - if err != nil { - return nil, err - } - - timeSubexp, err := cmd.PersistentFlags().GetString("time-subexp") - if err != nil { - return nil, err - } - - restimeSubexp, err := cmd.PersistentFlags().GetString("restime-subexp") - if err != nil { - return nil, err - } - - reqtimeSubexp, err := cmd.PersistentFlags().GetString("reqtime-subexp") - if err != nil { - return nil, err - } - - bodyBytesSubexp, err := cmd.PersistentFlags().GetString("body-bytes-subexp") - if err != nil { - return nil, err - } - - statusSubexp, err := cmd.PersistentFlags().GetString("status-subexp") - if err != nil { - return nil, err - } - - return options.SetOptions(opts, - options.Pattern(pattern), - options.UriSubexp(uriSubexp), - options.MethodSubexp(methodSubexp), - options.TimeSubexp(timeSubexp), - options.ResponseTimeSubexp(restimeSubexp), - options.RequestTimeSubexp(reqtimeSubexp), - options.BodyBytesSubexp(bodyBytesSubexp), - options.StatusSubexp(statusSubexp), - ), nil -} - -func bindRegexpFlags(cmd *cobra.Command) { - viper.BindPFlag("regexp.pattern", cmd.PersistentFlags().Lookup("pattern")) - viper.BindPFlag("regexp.uri_subexp", cmd.PersistentFlags().Lookup("uri-subexp")) - viper.BindPFlag("regexp.method_subexp", cmd.PersistentFlags().Lookup("method-subexp")) - viper.BindPFlag("regexp.time_subexp", cmd.PersistentFlags().Lookup("time-subexp")) - viper.BindPFlag("regexp.restime_subexp", cmd.PersistentFlags().Lookup("restime-subexp")) - viper.BindPFlag("regexp.reqtime_subexp", cmd.PersistentFlags().Lookup("reqtime-subexp")) - viper.BindPFlag("regexp.body_bytes_subexp", cmd.PersistentFlags().Lookup("body-bytes-subexp")) - viper.BindPFlag("regexp.status_subexp", cmd.PersistentFlags().Lookup("status-subexp")) -} diff --git a/cmd/alp/cmd/root.go b/cmd/alp/cmd/root.go index dcc619d..a2aaa0b 100644 --- a/cmd/alp/cmd/root.go +++ b/cmd/alp/cmd/root.go @@ -23,12 +23,16 @@ func NewRootCmd(version string) *cobra.Command { }, } - rootCmd.AddCommand(NewLTSVCmd()) - rootCmd.AddCommand(NewJSONCmd()) - rootCmd.AddCommand(NewRegexpCmd()) - rootCmd.AddCommand(NewPcapCmd()) - rootCmd.AddCommand(NewDiffCmd()) - rootCmd.AddCommand(NewCountCmd()) + commandFlags := newFlags() + + commandFlags.defineGlobalOptions(rootCmd) + + rootCmd.AddCommand(NewLTSVCmd(commandFlags)) + rootCmd.AddCommand(NewJSONCmd(commandFlags)) + rootCmd.AddCommand(NewRegexpCmd(commandFlags)) + rootCmd.AddCommand(NewPcapCmd(commandFlags)) + rootCmd.AddCommand(NewDiffCmd(commandFlags)) + rootCmd.AddCommand(NewCountCmd(commandFlags)) rootCmd.SetVersionTemplate(fmt.Sprintln(version)) return rootCmd diff --git a/options/option.go b/options/option.go index 81018cf..d44f36b 100644 --- a/options/option.go +++ b/options/option.go @@ -82,6 +82,7 @@ type Options struct { Regexp *RegexpOptions `mapstructure:"regexp"` JSON *JSONOptions `mapstructure:"json"` Pcap *PcapOptions `mapstructure:"pcap"` + Count *CountOptions `mapstructure:"count"` } type LTSVOptions struct { @@ -116,8 +117,12 @@ type JSONOptions struct { } type PcapOptions struct { - ServerIPs []string `yaml:"server_ips"` - ServerPort uint16 `yaml:"server_port"` + ServerIPs []string `mapstructure:"server_ips"` + ServerPort uint16 `mapstructure:"server_port"` +} + +type CountOptions struct { + Keys []string `mapstructure:"keys"` } type Option func(*Options) @@ -487,6 +492,15 @@ func PcapServerPort(n uint16) Option { } } +// count +func CountKeys(ss []string) Option { + return func(opts *Options) { + if len(ss) > 0 { + opts.Count.Keys = ss + } + } +} + func NewOptions(opt ...Option) *Options { ltsv := <SVOptions{ ApptimeLabel: DefaultApptimeLabelOption, @@ -524,6 +538,8 @@ func NewOptions(opt ...Option) *Options { ServerPort: DefaultPcapServerPortOption, } + count := &CountOptions{} + options := &Options{ Sort: DefaultSortOption, Format: DefaultFormatOption, @@ -536,6 +552,7 @@ func NewOptions(opt ...Option) *Options { Regexp: regexp, JSON: json, Pcap: pcap, + Count: count, } for _, o := range opt { From 6052499e649ec31514c54267bed003086d594e5b Mon Sep 17 00:00:00 2001 From: tkuchiki Date: Sun, 24 Sep 2023 23:32:10 +0900 Subject: [PATCH 2/2] Add tests for flags --- cmd/alp/cmd/common_test.go | 8 +- cmd/alp/cmd/flags_test.go | 135 +++++++++++++++++++++ cmd/alp/cmd/json_test.go | 2 +- cmd/alp/cmd/ltsv_test.go | 2 +- cmd/alp/cmd/regexp_test.go | 2 +- go.mod | 1 + internal/testutil/cmd.go | 221 ++++++++++++++++++++++++++++++++++- internal/testutil/strconv.go | 10 ++ 8 files changed, 370 insertions(+), 11 deletions(-) create mode 100644 cmd/alp/cmd/flags_test.go create mode 100644 internal/testutil/strconv.go diff --git a/cmd/alp/cmd/common_test.go b/cmd/alp/cmd/common_test.go index 275aae5..f87a116 100644 --- a/cmd/alp/cmd/common_test.go +++ b/cmd/alp/cmd/common_test.go @@ -10,22 +10,22 @@ import ( func TestCommonFlags(t *testing.T) { tempDir := t.TempDir() - tempLog, err := testutil.CreateTempDirAndFile(tempDir, testutil.JsonLog(testutil.NewJsonLogKeys())) + tempLog, err := testutil.CreateTempDirAndFile(tempDir, "test_common_flags_temp_log", testutil.JsonLog(testutil.NewJsonLogKeys())) if err != nil { t.Fatal(err) } - tempConfig, err := testutil.CreateTempDirAndFile(tempDir, testutil.ConfigFile()) + tempConfig, err := testutil.CreateTempDirAndFile(tempDir, "test_common_flags_temp_config", testutil.ConfigFile()) if err != nil { t.Fatal(err) } - tempPos, err := testutil.CreateTempDirAndFile(tempDir, "") + tempPos, err := testutil.CreateTempDirAndFile(tempDir, "test_common_flags_temp_pos", "") if err != nil { t.Fatal(err) } - tempDump, err := testutil.CreateTempDirAndFile(tempDir, "") + tempDump, err := testutil.CreateTempDirAndFile(tempDir, "test_common_flags_temp_dump", "") if err != nil { t.Fatal(err) } diff --git a/cmd/alp/cmd/flags_test.go b/cmd/alp/cmd/flags_test.go new file mode 100644 index 0000000..d9e8ad1 --- /dev/null +++ b/cmd/alp/cmd/flags_test.go @@ -0,0 +1,135 @@ +package cmd + +import ( + "strings" + "testing" + + "github.com/spf13/viper" + + "github.com/google/go-cmp/cmp" + "github.com/tkuchiki/alp/internal/testutil" + "github.com/tkuchiki/alp/options" +) + +func Test_createOptionsFromConfig(t *testing.T) { + viper.Reset() + rootCmd := NewRootCmd("test") + flags := newFlags() + + flags.defineProfileOptions(rootCmd) + flags.defineJSONOptions(rootCmd) + flags.defineLTSVOptions(rootCmd) + flags.defineRegexpOptions(rootCmd) + flags.definePcapOptions(rootCmd) + flags.defineCountKeys(rootCmd) + + tempDir := t.TempDir() + sort := "max" + dummyOpts := testutil.DummyOptions(sort) + + var err error + flags.config, err = testutil.CreateTempDirAndFile(tempDir, "test_create_options_from_config_config", testutil.DummyConfigFile(sort, dummyOpts)) + if err != nil { + t.Fatal(err) + } + + var opts *options.Options + opts, err = flags.createOptionsFromConfig(rootCmd) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(dummyOpts, opts); diff != "" { + t.Errorf("%s", diff) + } +} + +func Test_createOptionsFromConfig_overwrite(t *testing.T) { + rootCmd := NewRootCmd("test") + flags := newFlags() + + flags.defineProfileOptions(rootCmd) + flags.defineJSONOptions(rootCmd) + flags.defineLTSVOptions(rootCmd) + flags.defineRegexpOptions(rootCmd) + flags.definePcapOptions(rootCmd) + flags.defineCountKeys(rootCmd) + + tempDir := t.TempDir() + sort := "max" + + overwrittenSort := "min" + overwrittenOpts := testutil.DummyOverwrittenOptions(overwrittenSort) + + var err error + flags.config, err = testutil.CreateTempDirAndFile(tempDir, "test_create_options_from_config_overwrite_config", testutil.DummyConfigFile(sort, overwrittenOpts)) + if err != nil { + t.Fatal(err) + } + + viper.Set("file", overwrittenOpts.File) + viper.Set("dump", overwrittenOpts.Dump) + viper.Set("load", overwrittenOpts.Load) + viper.Set("sort", overwrittenSort) + viper.Set("reverse", overwrittenOpts.Reverse) + viper.Set("query_string", overwrittenOpts.QueryString) + viper.Set("query_string_ignore_values", overwrittenOpts.QueryStringIgnoreValues) + viper.Set("decode_uri", overwrittenOpts.DecodeUri) + viper.Set("format", overwrittenOpts.Format) + viper.Set("noheaders", overwrittenOpts.NoHeaders) + viper.Set("show_footers", overwrittenOpts.ShowFooters) + viper.Set("limit", overwrittenOpts.Limit) + viper.Set("matching_groups", strings.Join(overwrittenOpts.MatchingGroups, ",")) + viper.Set("filters", overwrittenOpts.Filters) + viper.Set("pos_file", overwrittenOpts.PosFile) + viper.Set("nosave_pos", overwrittenOpts.NoSavePos) + viper.Set("location", overwrittenOpts.Location) + viper.Set("output", overwrittenOpts.Output) + viper.Set("percentiles", testutil.IntSliceToString(overwrittenOpts.Percentiles)) + viper.Set("pagination_limit", overwrittenOpts.PaginationLimit) + + // json + viper.Set("json.uri_key", overwrittenOpts.JSON.UriKey) + viper.Set("json.method_key", overwrittenOpts.JSON.MethodKey) + viper.Set("json.time_key", overwrittenOpts.JSON.TimeKey) + viper.Set("json.restime_key", overwrittenOpts.JSON.ResponseTimeKey) + viper.Set("json.reqtime_key", overwrittenOpts.JSON.RequestTimeKey) + viper.Set("json.body_bytes_key", overwrittenOpts.JSON.BodyBytesKey) + viper.Set("json.status_key", overwrittenOpts.JSON.StatusKey) + + // ltsv + viper.Set("ltsv.uri_label", overwrittenOpts.LTSV.UriLabel) + viper.Set("ltsv.method_label", overwrittenOpts.LTSV.MethodLabel) + viper.Set("ltsv.time_label", overwrittenOpts.LTSV.TimeLabel) + viper.Set("ltsv.apptime_label", overwrittenOpts.LTSV.ApptimeLabel) + viper.Set("ltsv.reqtime_label", overwrittenOpts.LTSV.ReqtimeLabel) + viper.Set("ltsv.size_label", overwrittenOpts.LTSV.SizeLabel) + viper.Set("ltsv.status_label", overwrittenOpts.LTSV.StatusLabel) + + // regexp + viper.Set("regexp.pattern", overwrittenOpts.Regexp.Pattern) + viper.Set("regexp.uri_subexp", overwrittenOpts.Regexp.UriSubexp) + viper.Set("regexp.method_subexp", overwrittenOpts.Regexp.MethodSubexp) + viper.Set("regexp.time_subexp", overwrittenOpts.Regexp.TimeSubexp) + viper.Set("regexp.restime_subexp", overwrittenOpts.Regexp.ResponseTimeSubexp) + viper.Set("regexp.reqtime_subexp", overwrittenOpts.Regexp.RequestTimeSubexp) + viper.Set("regexp.body_bytes_subexp", overwrittenOpts.Regexp.BodyBytesSubexp) + viper.Set("regexp.status_subexp", overwrittenOpts.Regexp.StatusSubexp) + + // pcap + viper.Set("pcap.server_ips", strings.Join(overwrittenOpts.Pcap.ServerIPs, ",")) + viper.Set("pcap.server_port", overwrittenOpts.Pcap.ServerPort) + + // count + viper.Set("count.keys", overwrittenOpts.Count.Keys) + + var opts *options.Options + opts, err = flags.createOptionsFromConfig(rootCmd) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(overwrittenOpts, opts); diff != "" { + t.Errorf("%s", diff) + } +} diff --git a/cmd/alp/cmd/json_test.go b/cmd/alp/cmd/json_test.go index 29fe726..f630f3d 100644 --- a/cmd/alp/cmd/json_test.go +++ b/cmd/alp/cmd/json_test.go @@ -19,7 +19,7 @@ func TestJSONCmd(t *testing.T) { jsonLog := testutil.JsonLog(keys) - tempFile, err := testutil.CreateTempDirAndFile(t.TempDir(), jsonLog) + tempFile, err := testutil.CreateTempDirAndFile(t.TempDir(), "test_json_cmd_temp_file", jsonLog) if err != nil { t.Fatal(err) } diff --git a/cmd/alp/cmd/ltsv_test.go b/cmd/alp/cmd/ltsv_test.go index 809c917..8745627 100644 --- a/cmd/alp/cmd/ltsv_test.go +++ b/cmd/alp/cmd/ltsv_test.go @@ -19,7 +19,7 @@ func TestLTSVCmd(t *testing.T) { ltsvLog := testutil.LTSVLog(keys) - tempFile, err := testutil.CreateTempDirAndFile(t.TempDir(), ltsvLog) + tempFile, err := testutil.CreateTempDirAndFile(t.TempDir(), "test_ltsv_cmd_temp_file", ltsvLog) if err != nil { t.Fatal(err) } diff --git a/cmd/alp/cmd/regexp_test.go b/cmd/alp/cmd/regexp_test.go index 199bdac..1a5b837 100644 --- a/cmd/alp/cmd/regexp_test.go +++ b/cmd/alp/cmd/regexp_test.go @@ -19,7 +19,7 @@ func TestRegexpCmd(t *testing.T) { regexpLog := testutil.RegexpLog() - tempFile, err := testutil.CreateTempDirAndFile(t.TempDir(), regexpLog) + tempFile, err := testutil.CreateTempDirAndFile(t.TempDir(), "test_regexp_cmd_temp_file", regexpLog) if err != nil { t.Fatal(err) } diff --git a/go.mod b/go.mod index 3082bf0..dc59b57 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/tkuchiki/alp require ( github.com/Songmu/go-ltsv v0.0.0-20200903131950-a608c3f6a014 github.com/antonmedv/expr v1.8.9 + github.com/google/go-cmp v0.5.9 github.com/google/gopacket v1.1.19 github.com/kylelemons/godebug v1.1.0 github.com/olekukonko/tablewriter v0.0.5 diff --git a/internal/testutil/cmd.go b/internal/testutil/cmd.go index 1786cba..e4415b2 100644 --- a/internal/testutil/cmd.go +++ b/internal/testutil/cmd.go @@ -1,11 +1,14 @@ package testutil import ( - "fmt" + "bytes" "os" "path/filepath" "strings" - "time" + "text/template" + + "github.com/tkuchiki/alp/options" + "github.com/tkuchiki/alp/stats" ) type LogKeys struct { @@ -42,8 +45,8 @@ func NewLTSVLogKeys() LogKeys { } } -func CreateTempDirAndFile(dir, content string) (string, error) { - fpath := filepath.Join(dir, fmt.Sprint(time.Now().UnixNano())) +func CreateTempDirAndFile(dir, filename, content string) (string, error) { + fpath := filepath.Join(dir, filename) err := os.WriteFile(fpath, []byte(content), 0644) return fpath, err @@ -108,3 +111,213 @@ func ConfigFile() string { reverse: true query_string: true` } + +func DummyOptions(sort string) *options.Options { + sortOptions := stats.NewSortOptions() + sortOptions.SetAndValidate(sort) + + return &options.Options{ + File: "/path/to/file", + Sort: sortOptions.SortType(), + Location: "dummy", + Reverse: false, + QueryString: false, + QueryStringIgnoreValues: false, + DecodeUri: false, + Format: "markdown", + Limit: 100, + NoHeaders: false, + ShowFooters: false, + MatchingGroups: []string{ + "/foo/.+", + }, + Filters: ".Uri == '/foo/bar'", + Output: "count,uri,min,max", + PosFile: "/path/to/pos", + NoSavePos: false, + Percentiles: []int{1, 5}, + PaginationLimit: 10, + LTSV: &options.LTSVOptions{ + UriLabel: "u", + MethodLabel: "m", + TimeLabel: "t", + ApptimeLabel: "a", + ReqtimeLabel: "r", + SizeLabel: "sz", + StatusLabel: "st", + }, + JSON: &options.JSONOptions{ + UriKey: "u", + MethodKey: "m", + TimeKey: "t", + ResponseTimeKey: "res", + RequestTimeKey: "req", + BodyBytesKey: "b", + StatusKey: "s", + }, + Regexp: &options.RegexpOptions{ + Pattern: "dummy pattern", + UriSubexp: "u", + MethodSubexp: "m", + TimeSubexp: "t", + ResponseTimeSubexp: "res", + RequestTimeSubexp: "req", + BodyBytesSubexp: "b", + StatusSubexp: "s", + }, + Pcap: &options.PcapOptions{ + ServerIPs: []string{ + "192.168.1.10", + }, + ServerPort: 12345, + }, + Count: &options.CountOptions{ + Keys: []string{ + "ua", + }, + }, + } +} + +func DummyOverwrittenOptions(sort string) *options.Options { + sortOptions := stats.NewSortOptions() + sortOptions.SetAndValidate(sort) + + return &options.Options{ + File: "/path/to/overwritten/file", + Sort: sortOptions.SortType(), + Location: "overwritten location", + Reverse: true, + QueryString: true, + QueryStringIgnoreValues: true, + DecodeUri: true, + Format: "tsv", + Limit: 200, + NoHeaders: true, + ShowFooters: true, + MatchingGroups: []string{ + "/foo/bar/.+", + "/bar/.+", + }, + Filters: ".Status == 200", + Output: "uri,avg", + PosFile: "/path/to/overwritten/pos", + NoSavePos: true, + Percentiles: []int{5, 9}, + PaginationLimit: 20, + LTSV: &options.LTSVOptions{ + UriLabel: "u2", + MethodLabel: "m2", + TimeLabel: "t2", + ApptimeLabel: "a2", + ReqtimeLabel: "r2", + SizeLabel: "sz2", + StatusLabel: "st2", + }, + JSON: &options.JSONOptions{ + UriKey: "u2", + MethodKey: "m2", + TimeKey: "t2", + ResponseTimeKey: "res2", + RequestTimeKey: "req2", + BodyBytesKey: "b2", + StatusKey: "s2", + }, + Regexp: &options.RegexpOptions{ + UriSubexp: "u2", + MethodSubexp: "m2", + TimeSubexp: "t2", + ResponseTimeSubexp: "res2", + RequestTimeSubexp: "req2", + BodyBytesSubexp: "b2", + StatusSubexp: "s2", + }, + Pcap: &options.PcapOptions{ + ServerIPs: []string{ + "192.168.1.20", + }, + ServerPort: 54321, + }, + Count: &options.CountOptions{ + Keys: []string{ + "host", + "user_agent", + }, + }, + } +} + +func DummyConfigFile(sort string, dummyOpts *options.Options) string { + configTmpl := `file: {{ .File }} +sort: ` + sort + ` +reverse: {{ .Reverse }} +query_string: {{ .QueryString }} +query_string_ignore_values: {{ .QueryStringIgnoreValues }} +decode_uri: {{ .DecodeUri }} +format: {{ .Format }} +limit: {{ .Limit }} +noheaders: {{ .NoHeaders }} +show_footers: {{ .ShowFooters }} +matching_groups: +{{ range .MatchingGroups }} + - {{ . }} +{{ end }} +filters: {{ .Filters }} +output: {{ .Output }} +pos_file: {{ .PosFile }} +nosave_pos: {{ .NoSavePos }} +location: {{ .Location }} +percentiles: +{{ range .Percentiles }} + - {{ . }} +{{ end }} +pagination_limit: {{ .PaginationLimit }} +ltsv: + uri_label: {{ .LTSV.UriLabel }} + method_label: {{ .LTSV.MethodLabel }} + time_label: {{ .LTSV.TimeLabel }} + apptime_label: {{ .LTSV.ApptimeLabel }} + reqtime_label: {{ .LTSV.ReqtimeLabel }} + size_label: {{ .LTSV.SizeLabel }} + status_label: {{ .LTSV.StatusLabel }} +json: + uri_key: {{ .JSON.UriKey }} + method_key: {{ .JSON.MethodKey }} + time_key: {{ .JSON.TimeKey }} + response_time_key: {{ .JSON.ResponseTimeKey }} + request_time_key: {{ .JSON.RequestTimeKey }} + body_bytes_key: {{ .JSON.BodyBytesKey }} + status_key: {{ .JSON.StatusKey }} +regexp: + pattern: {{ .Regexp.Pattern }} + uri_subexp: {{ .Regexp.UriSubexp }} + method_subexp: {{ .Regexp.MethodSubexp }} + time_subexp: {{ .Regexp.TimeSubexp }} + response_time_subexp: {{ .Regexp.ResponseTimeSubexp }} + request_time_subexp: {{ .Regexp.RequestTimeSubexp }} + body_bytes_subexp: {{ .Regexp.BodyBytesSubexp }} + status_subexp: {{ .Regexp.StatusSubexp }} +pcap: + server_ips: +{{ range .Pcap.ServerIPs }} + - {{ . }} +{{ end }} + server_port: {{ .Pcap.ServerPort }} +count: + keys: +{{ range .Count.Keys }} + - {{ . }} +{{ end }} +` + t, err := template.New("dummy_config").Parse(configTmpl) + if err != nil { + panic(err) + } + + var buf bytes.Buffer + if err = t.Execute(&buf, dummyOpts); err != nil { + panic(err) + } + + return buf.String() +} diff --git a/internal/testutil/strconv.go b/internal/testutil/strconv.go new file mode 100644 index 0000000..df19f34 --- /dev/null +++ b/internal/testutil/strconv.go @@ -0,0 +1,10 @@ +package testutil + +import ( + "fmt" + "strings" +) + +func IntSliceToString(si []int) string { + return strings.Trim(strings.Replace(fmt.Sprint(si), " ", ",", -1), "[]") +}