mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-11 21:51:21 +00:00
PT-182 Added --output-format for MongoDB tools
Added `--output-format` option for pt-mongodb-summary and pt-mongodb-query-digest. Valid values are: text & json. Defaulf: text
This commit is contained in:
@@ -45,6 +45,10 @@ Options
|
|||||||
``-d``, ``--database``
|
``-d``, ``--database``
|
||||||
Specifies which database to profile
|
Specifies which database to profile
|
||||||
|
|
||||||
|
``-f``, ``--output-format``
|
||||||
|
Specifies the report output format. Valid options are: ``text``, ``json``.
|
||||||
|
The default value is ``text``.
|
||||||
|
|
||||||
``-l``, ``--log-level``
|
``-l``, ``--log-level``
|
||||||
Specifies the log level:
|
Specifies the log level:
|
||||||
``panic``, ``fatal``, ``error``, ``warn``, ``info``, ``debug error``
|
``panic``, ``fatal``, ``error``, ``warn``, ``info``, ``debug error``
|
||||||
|
@@ -35,6 +35,10 @@ Options
|
|||||||
with a MongoDB server.
|
with a MongoDB server.
|
||||||
By default, the ``admin`` database is used.
|
By default, the ``admin`` database is used.
|
||||||
|
|
||||||
|
``-f``, ``--output-format``
|
||||||
|
Specifies the report output format. Valid options are: ``text``, ``json``.
|
||||||
|
The default value is ``text``.
|
||||||
|
|
||||||
``-p``, ``--password``
|
``-p``, ``--password``
|
||||||
Specifies the password to use when connecting to a server
|
Specifies the password to use when connecting to a server
|
||||||
with authentication enabled.
|
with authentication enabled.
|
||||||
|
@@ -38,6 +38,7 @@ The last step is sorting the results. The default sort order is by ascending que
|
|||||||
|-a|--authenticationDatabase|database used to establish credentials and privileges with a MongoDB server admin|
|
|-a|--authenticationDatabase|database used to establish credentials and privileges with a MongoDB server admin|
|
||||||
|-c|--no-version-check|Don't check for updates|
|
|-c|--no-version-check|Don't check for updates|
|
||||||
|-d|--database|database to profile|
|
|-d|--database|database to profile|
|
||||||
|
|-f|--output-format|report output format. Valid values are text, json. Default: text|
|
||||||
|-l|--log-level|Log level:, panic, fatal, error, warn, info, debug error|
|
|-l|--log-level|Log level:, panic, fatal, error, warn, info, debug error|
|
||||||
|-n|--limit|show the first n queries|
|
|-n|--limit|show the first n queries|
|
||||||
|-o|--order-by|comma separated list of order by fields (max values): `count`, `ratio`, `query-time`, `docs-scanned`, `docs-returned`.<br> A `-` in front of the field name denotes reverse order.<br> Example:`--order-by="count,-ratio"`).|
|
|-o|--order-by|comma separated list of order by fields (max values): `count`, `ratio`, `query-time`, `docs-scanned`, `docs-returned`.<br> A `-` in front of the field name denotes reverse order.<br> Example:`--order-by="count,-ratio"`).|
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -59,6 +61,12 @@ type options struct {
|
|||||||
Version bool
|
Version bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type report struct {
|
||||||
|
Headers []string
|
||||||
|
QueryStats []stats.QueryStats
|
||||||
|
QueryTotals stats.QueryStats
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
opts, err := getOptions()
|
opts, err := getOptions()
|
||||||
@@ -148,65 +156,56 @@ func main() {
|
|||||||
queriesStats := queries.CalcQueriesStats(uptime)
|
queriesStats := queries.CalcQueriesStats(uptime)
|
||||||
sortedQueryStats := sortQueries(queriesStats, opts.OrderBy)
|
sortedQueryStats := sortQueries(queriesStats, opts.OrderBy)
|
||||||
|
|
||||||
|
|
||||||
queryTotals := queries.CalcTotalQueriesStats(uptime)
|
|
||||||
tt, _ := template.New("query").Funcs(template.FuncMap{
|
|
||||||
"Format": format,
|
|
||||||
}).Parse(getTotalsTemplate())
|
|
||||||
tt.Execute(os.Stdout, queryTotals)
|
|
||||||
|
|
||||||
t, _ := template.New("query").Funcs(template.FuncMap{
|
|
||||||
"Format": format,
|
|
||||||
}).Parse(getQueryTemplate())
|
|
||||||
|
|
||||||
if opts.Limit > 0 && len(sortedQueryStats) > opts.Limit {
|
if opts.Limit > 0 && len(sortedQueryStats) > opts.Limit {
|
||||||
sortedQueryStats = sortedQueryStats[:opts.Limit]
|
sortedQueryStats = sortedQueryStats[:opts.Limit]
|
||||||
}
|
}
|
||||||
for _, qs := range sortedQueryStats {
|
|
||||||
t.Execute(os.Stdout, qs)
|
rep := report{
|
||||||
|
Headers: getHeaders(opts),
|
||||||
|
QueryTotals: queries.CalcTotalQueriesStats(uptime),
|
||||||
|
QueryStats: sortedQueryStats,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out, err := formatResults(rep, opts.OutputFormat)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Cannot parse the report: %s", err.Error())
|
||||||
|
os.Exit(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(out))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatResults(stats []stats.QueryStats, opts options, format string) ([]byte, error) {
|
func formatResults(rep report, outputFormat string) ([]byte, error) {
|
||||||
var buf *bytes.Buffer
|
var buf *bytes.Buffer
|
||||||
|
|
||||||
switch format {
|
switch outputFormat {
|
||||||
case "json":
|
case "json":
|
||||||
b, err := json.MarshalIndent(stats, "", " ")
|
b, err := json.MarshalIndent(rep, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("[Error] Cannot convert results to json: %s", err.Error())
|
return nil, fmt.Errorf("[Error] Cannot convert results to json: %s", err.Error())
|
||||||
}
|
}
|
||||||
buf = bytes.NewBuffer(b)
|
buf = bytes.NewBuffer(b)
|
||||||
default:
|
default:
|
||||||
printHeader(opts)
|
|
||||||
buf = new(bytes.Buffer)
|
buf = new(bytes.Buffer)
|
||||||
|
|
||||||
t := template.Must(template.New("replicas").Parse(templates.Replicas))
|
tt, _ := template.New("query").Funcs(template.FuncMap{
|
||||||
t.Execute(buf, ci.ReplicaMembers)
|
"Format": format,
|
||||||
|
}).Parse(getTotalsTemplate())
|
||||||
|
tt.Execute(buf, rep.QueryTotals)
|
||||||
|
|
||||||
t = template.Must(template.New("hosttemplateData").Parse(templates.HostInfo))
|
t, _ := template.New("query").Funcs(template.FuncMap{
|
||||||
t.Execute(buf, ci.HostInfo)
|
"Format": format,
|
||||||
|
}).Parse(getQueryTemplate())
|
||||||
|
|
||||||
t = template.Must(template.New("runningOps").Parse(templates.RunningOps))
|
for _, qs := range rep.QueryStats {
|
||||||
t.Execute(buf, ci.RunningOps)
|
t.Execute(buf, qs)
|
||||||
|
|
||||||
t = template.Must(template.New("ssl").Parse(templates.Security))
|
|
||||||
t.Execute(buf, ci.SecuritySettings)
|
|
||||||
|
|
||||||
if ci.OplogInfo != nil && len(ci.OplogInfo) > 0 {
|
|
||||||
t = template.Must(template.New("oplogInfo").Parse(templates.Oplog))
|
|
||||||
t.Execute(buf, ci.OplogInfo[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t = template.Must(template.New("clusterwide").Parse(templates.Clusterwide))
|
|
||||||
t.Execute(buf, ci.ClusterWideInfo)
|
|
||||||
|
|
||||||
t = template.Must(template.New("balancer").Parse(templates.BalancerStats))
|
|
||||||
t.Execute(buf, ci.BalancerStats)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// format scales a number and returns a string made of the scaled value and unit (K=Kilo, M=Mega, T=Tera)
|
// format scales a number and returns a string made of the scaled value and unit (K=Kilo, M=Mega, T=Tera)
|
||||||
// using I.F where i is the number of digits for the integer part and F is the number of digits for the
|
// using I.F where i is the number of digits for the integer part and F is the number of digits for the
|
||||||
// decimal part
|
// decimal part
|
||||||
@@ -303,6 +302,7 @@ func getOptions() (*options, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.OutputFormat != "json" && opts.OutputFormat != "text" {
|
if opts.OutputFormat != "json" && opts.OutputFormat != "text" {
|
||||||
|
opts.OutputFormat = "text"
|
||||||
log.Infof("Invalid output format '%s'. Using text format", opts.OutputFormat)
|
log.Infof("Invalid output format '%s'. Using text format", opts.OutputFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,11 +348,13 @@ func getDialInfo(opts *options) *pmgo.DialInfo {
|
|||||||
return pmgoDialInfo
|
return pmgoDialInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func printHeader(opts *options) {
|
func getHeaders(opts *options) []string {
|
||||||
fmt.Printf("%s - %s\n", TOOLNAME, time.Now().Format(time.RFC1123Z))
|
h := []string{
|
||||||
fmt.Printf("Host: %s\n", opts.Host)
|
fmt.Sprintf("%s - %s\n", TOOLNAME, time.Now().Format(time.RFC1123Z)),
|
||||||
fmt.Printf("Skipping profiled queries on these collections: %v\n", opts.SkipCollections)
|
fmt.Sprintf("Host: %s\n", opts.Host),
|
||||||
fmt.Println("")
|
fmt.Sprintf("Skipping profiled queries on these collections: %v\n", opts.SkipCollections),
|
||||||
|
}
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func getQueryTemplate() string {
|
func getQueryTemplate() string {
|
||||||
|
@@ -67,6 +67,7 @@ func TestParseArgs(t *testing.T) {
|
|||||||
OrderBy: strings.Split(DEFAULT_ORDERBY, ","),
|
OrderBy: strings.Split(DEFAULT_ORDERBY, ","),
|
||||||
SkipCollections: strings.Split(DEFAULT_SKIPCOLLECTIONS, ","),
|
SkipCollections: strings.Split(DEFAULT_SKIPCOLLECTIONS, ","),
|
||||||
AuthDB: DEFAULT_AUTHDB,
|
AuthDB: DEFAULT_AUTHDB,
|
||||||
|
OutputFormat: "text",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -82,6 +83,7 @@ func TestParseArgs(t *testing.T) {
|
|||||||
SkipCollections: strings.Split(DEFAULT_SKIPCOLLECTIONS, ","),
|
SkipCollections: strings.Split(DEFAULT_SKIPCOLLECTIONS, ","),
|
||||||
AuthDB: DEFAULT_AUTHDB,
|
AuthDB: DEFAULT_AUTHDB,
|
||||||
Help: false,
|
Help: false,
|
||||||
|
OutputFormat: "text",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@@ -15,16 +15,14 @@ Please check the `releases <https://github.com/percona/toolkit-go/releases>`_ ta
|
|||||||
|
|
||||||
Paramters
|
Paramters
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
===== ============== ======= ================================================================================
|
|Short|Long|Default|Description|
|
||||||
Short Long Default Description
|
|-----|----|-------|-----------|
|
||||||
===== ============== ======= ================================================================================
|
|-a|--auth-db|admin|database used to establish credentials and privileges with a MongoDB server|
|
||||||
u user empty user name to use when connecting if DB auth is enabled
|
|-f|--output-format|report output format|Valid values are text, json. Default: text|
|
||||||
p password empty password to use when connecting if DB auth is enabled
|
|-f|--output-format|text|output format: text, json. Default: text|
|
||||||
a auth-db admin database used to establish credentials and privileges with a MongoDB server
|
|-p|--password|empty|password to use when connecting if DB auth is enabled|
|
||||||
f output-format text output format: text, json. Default: text
|
|-u|--user|empty|user name to use when connecting if DB auth is enabled|
|
||||||
===== ============== ======= ================================================================================
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
``-p`` is an optional parameter. If it is used it shouldn't have a blank between the parameter and its value: `-p<password>`
|
``-p`` is an optional parameter. If it is used it shouldn't have a blank between the parameter and its value: `-p<password>`
|
||||||
It can be also used as `-p` without specifying a password; in that case, the program will ask the password to avoid using a password in the command line.
|
It can be also used as `-p` without specifying a password; in that case, the program will ask the password to avoid using a password in the command line.
|
||||||
|
Reference in New Issue
Block a user