github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/admin-prometheus-metrics.go (about) 1 // Copyright (c) 2015-2022 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "errors" 22 "io" 23 "net/http" 24 "os" 25 "time" 26 27 "github.com/minio/cli" 28 json "github.com/minio/colorjson" 29 "github.com/minio/madmin-go/v3" 30 "github.com/minio/mc/pkg/probe" 31 ) 32 33 var adminPrometheusMetricsCmd = cli.Command{ 34 Name: "metrics", 35 Usage: "print cluster wide prometheus metrics", 36 OnUsageError: onUsageError, 37 Action: mainSupportMetrics, 38 Before: setGlobalsFromContext, 39 Flags: globalFlags, 40 CustomHelpTemplate: `NAME: 41 {{.HelpName}} - {{.Usage}} 42 USAGE: 43 {{.HelpName}} TARGET [METRIC-TYPE] 44 45 METRIC-TYPE: 46 valid values are ['cluster', 'node', 'bucket', 'resource']. Defaults to 'cluster' if not specified. 47 48 FLAGS: 49 {{range .VisibleFlags}}{{.}} 50 {{end}} 51 EXAMPLES: 52 1. List of metrics reported cluster wide. 53 {{.Prompt}} {{.HelpName}} play 54 55 2. List of metrics reported at node level. 56 {{.Prompt}} {{.HelpName}} play node 57 58 3. List of metrics reported at bucket level. 59 {{.Prompt}} {{.HelpName}} play bucket 60 61 4. List of resource metrics. 62 {{.Prompt}} {{.HelpName}} play resource 63 `, 64 } 65 66 const metricsEndPointRoot = "/minio/v2/metrics/" 67 68 // checkSupportMetricsSyntax - validate arguments passed by a user 69 func checkSupportMetricsSyntax(ctx *cli.Context) { 70 if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 { 71 showCommandHelpAndExit(ctx, 1) // last argument is exit code 72 } 73 } 74 75 func printPrometheusMetrics(ctx *cli.Context) error { 76 // Get the alias parameter from cli 77 args := ctx.Args() 78 alias := cleanAlias(args.Get(0)) 79 80 if !isValidAlias(alias) { 81 fatalIf(errInvalidAlias(alias), "Invalid alias.") 82 } 83 hostConfig := mustGetHostConfig(alias) 84 if hostConfig == nil { 85 fatalIf(errInvalidAliasedURL(alias), "No such alias `"+alias+"` found.") 86 return nil 87 } 88 89 token, e := getPrometheusToken(hostConfig) 90 if e != nil { 91 return e 92 } 93 metricsSubSystem := args.Get(1) 94 switch metricsSubSystem { 95 case "node", "bucket", "cluster", "resource": 96 case "": 97 metricsSubSystem = "cluster" 98 default: 99 fatalIf(errInvalidArgument().Trace(), "invalid metric type '%v'", metricsSubSystem) 100 } 101 102 req, e := http.NewRequest(http.MethodGet, hostConfig.URL+metricsEndPointRoot+metricsSubSystem, nil) 103 if e != nil { 104 return e 105 } 106 107 if token != "" { 108 req.Header.Add("Authorization", "Bearer "+token) 109 } 110 111 client := httpClient(60 * time.Second) 112 resp, e := client.Do(req) 113 if e != nil { 114 return e 115 } 116 117 defer resp.Body.Close() 118 119 if resp.StatusCode == http.StatusOK { 120 printMsg(prometheusMetricsReader{Reader: resp.Body}) 121 return nil 122 } 123 124 return errors.New(resp.Status) 125 } 126 127 // JSON returns jsonified message 128 func (pm prometheusMetricsReader) JSON() string { 129 results, e := madmin.ParsePrometheusResults(pm.Reader) 130 fatalIf(probe.NewError(e), "Unable to parse Prometheus metrics.") 131 132 jsonMessageBytes, e := json.MarshalIndent(results, "", " ") 133 fatalIf(probe.NewError(e), "Unable to marshal into JSON.") 134 return string(jsonMessageBytes) 135 } 136 137 // String - returns the string representation of the prometheus metrics 138 func (pm prometheusMetricsReader) String() string { 139 _, e := io.Copy(os.Stdout, pm.Reader) 140 141 fatalIf(probe.NewError(e), "Unable to read Prometheus metrics.") 142 143 return "" 144 } 145 146 // prometheusMetricsReader mirrors the MetricFamily proto message. 147 type prometheusMetricsReader struct { 148 Reader io.Reader 149 } 150 151 func mainSupportMetrics(ctx *cli.Context) error { 152 checkSupportMetricsSyntax(ctx) 153 154 fatalIf(probe.NewError(printPrometheusMetrics(ctx)), "Unable to list prometheus metrics.") 155 156 return nil 157 }