Make metrics human
Issue #572 exposes metrics but they carry the peer ID in binary. This was ok with our internal codecs but it doesn't seem to work very well with json, and makes the output format unusable. This makes the Metric.Peer field a string. Additinoally, fixes calling the command without arguments and displaying the date in the right format. License: MIT Signed-off-by: Hector Sanjuan <code@hector.link>
This commit is contained in:
parent
53a16fba8f
commit
19b1124999
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -205,9 +206,12 @@ func (c *defaultClient) GetConnectGraph() (api.ConnectGraphSerial, error) {
|
|||
return graphS, err
|
||||
}
|
||||
|
||||
// Metrics returns a map with the latest metrics of matching name
|
||||
// Metrics returns a map with the latest valid metrics of the given name
|
||||
// for the current cluster peers.
|
||||
func (c *defaultClient) Metrics(name string) ([]api.Metric, error) {
|
||||
if name == "" {
|
||||
return nil, errors.New("bad metric name")
|
||||
}
|
||||
var metrics []api.Metric
|
||||
err := c.do("GET", fmt.Sprintf("/monitor/metrics/%s", name), nil, nil, &metrics)
|
||||
return metrics, err
|
||||
|
|
|
@ -92,9 +92,10 @@ func (c *defaultClient) handleResponse(resp *http.Response, obj interface{}) err
|
|||
var apiErr api.Error
|
||||
err = json.Unmarshal(body, &apiErr)
|
||||
if err != nil {
|
||||
// not json. 404s etc.
|
||||
return &api.Error{
|
||||
Code: resp.StatusCode,
|
||||
Message: err.Error(),
|
||||
Message: string(body),
|
||||
}
|
||||
}
|
||||
return &apiErr
|
||||
|
|
53
api/types.go
53
api/types.go
|
@ -9,6 +9,8 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
|
@ -847,10 +849,10 @@ func (n *NodeWithMeta) Size() uint64 {
|
|||
// the Value, which should be interpreted by the PinAllocator.
|
||||
type Metric struct {
|
||||
Name string
|
||||
Peer peer.ID // filled-in by Cluster.
|
||||
Peer peer.ID
|
||||
Value string
|
||||
Expire int64 // UnixNano
|
||||
Valid bool // if the metric is not valid it will be discarded
|
||||
Expire int64
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// SetTTL sets Metric to expire after the given time.Duration
|
||||
|
@ -876,6 +878,51 @@ func (m *Metric) Discard() bool {
|
|||
return !m.Valid || m.Expired()
|
||||
}
|
||||
|
||||
// helper for JSON marshaling. The Metric type is already
|
||||
// serializable, but not pretty to humans (API).
|
||||
type metricSerial struct {
|
||||
Name string `json:"name"`
|
||||
Peer string `json:"peer"`
|
||||
Value string `json:"value"`
|
||||
Expire int64 `json:"expire"`
|
||||
Valid bool `json:"valid"`
|
||||
}
|
||||
|
||||
// MarshalJSON allows a Metric to produce a JSON representation
|
||||
// of itself.
|
||||
func (m *Metric) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&metricSerial{
|
||||
Name: m.Name,
|
||||
Peer: peer.IDB58Encode(m.Peer),
|
||||
Value: m.Value,
|
||||
Expire: m.Expire,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON on top of the Metric.
|
||||
func (m *Metric) UnmarshalJSON(j []byte) error {
|
||||
if bytes.Equal(j, []byte("null")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ms := &metricSerial{}
|
||||
err := json.Unmarshal(j, ms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := peer.IDB58Decode(ms.Peer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Name = ms.Name
|
||||
m.Peer = p
|
||||
m.Value = ms.Value
|
||||
m.Expire = ms.Expire
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alert carries alerting information about a peer. WIP.
|
||||
type Alert struct {
|
||||
Peer peer.ID
|
||||
|
|
|
@ -275,7 +275,10 @@ func (c *Cluster) alertsHandler() {
|
|||
// only the leader handles alerts
|
||||
leader, err := c.consensus.Leader()
|
||||
if err == nil && leader == c.id {
|
||||
logger.Warningf("Peer %s received alert for %s in %s", c.id, alrt.MetricName, alrt.Peer.Pretty())
|
||||
logger.Warningf(
|
||||
"Peer %s received alert for %s in %s",
|
||||
c.id, alrt.MetricName, alrt.Peer,
|
||||
)
|
||||
switch alrt.MetricName {
|
||||
case pingMetricName:
|
||||
c.repinFromPeer(alrt.Peer)
|
||||
|
|
|
@ -6,8 +6,10 @@ import (
|
|||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/ipfs-cluster/api"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
)
|
||||
|
||||
func jsonFormatObject(resp interface{}) {
|
||||
|
@ -216,7 +218,8 @@ func textFormatPrintAddedOutput(obj *api.AddedOutput) {
|
|||
}
|
||||
|
||||
func textFormatPrintMetric(obj *api.Metric) {
|
||||
fmt.Printf("%s: %s | Expire : %d\n", obj.Peer.Pretty(), obj.Value, obj.Expire)
|
||||
date := time.Unix(0, obj.Expire).UTC().Format(time.RFC3339)
|
||||
fmt.Printf("%s: %s | Expire: %s\n", peer.IDB58Encode(obj.Peer), obj.Value, date)
|
||||
}
|
||||
|
||||
func textFormatPrintError(obj *api.Error) {
|
||||
|
|
|
@ -768,10 +768,21 @@ graph of the connections. Output is a dot file encoding the cluster's connectio
|
|||
Description: `
|
||||
This commands displays the latest valid metrics of the given type logged
|
||||
by this peer for all current cluster peers.
|
||||
|
||||
Currently supported metrics depend on the informer component used,
|
||||
but usually are:
|
||||
|
||||
- freespace
|
||||
- ping
|
||||
`,
|
||||
ArgsUsage: "Metric name",
|
||||
ArgsUsage: "<metric name>",
|
||||
Action: func(c *cli.Context) error {
|
||||
resp, cerr := globalClient.Metrics(c.Args().First())
|
||||
metric := c.Args().First()
|
||||
if metric == "" {
|
||||
checkErr("", errors.New("provide a metric name"))
|
||||
}
|
||||
|
||||
resp, cerr := globalClient.Metrics(metric)
|
||||
formatResponse(c, resp, cerr)
|
||||
return nil
|
||||
},
|
||||
|
|
|
@ -71,14 +71,14 @@ func (mtrs *Store) Latest(name string) []api.Metric {
|
|||
|
||||
// PeerMetrics returns the latest metrics for a given peer ID for
|
||||
// all known metrics types. It may return expired metrics.
|
||||
func (mtrs *Store) PeerMetrics(peer peer.ID) []api.Metric {
|
||||
func (mtrs *Store) PeerMetrics(pid peer.ID) []api.Metric {
|
||||
mtrs.mux.RLock()
|
||||
defer mtrs.mux.RUnlock()
|
||||
|
||||
result := make([]api.Metric, 0)
|
||||
|
||||
for _, byPeer := range mtrs.byName {
|
||||
window, ok := byPeer[peer]
|
||||
window, ok := byPeer[pid]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
// peerset
|
||||
func PeersetFilter(metrics []api.Metric, peerset []peer.ID) []api.Metric {
|
||||
peerMap := make(map[peer.ID]struct{})
|
||||
for _, peer := range peerset {
|
||||
peerMap[peer] = struct{}{}
|
||||
for _, pid := range peerset {
|
||||
peerMap[pid] = struct{}{}
|
||||
}
|
||||
|
||||
filtered := make([]api.Metric, 0, len(metrics))
|
||||
|
|
|
@ -205,12 +205,12 @@ func (rpcapi *RPCAPI) BlockAllocate(ctx context.Context, in api.PinSerial, out *
|
|||
// Returned metrics are Valid and belong to current
|
||||
// Cluster peers.
|
||||
metrics := rpcapi.c.monitor.LatestMetrics(pingMetricName)
|
||||
peers := make([]peer.ID, len(metrics), len(metrics))
|
||||
peers := make([]string, len(metrics), len(metrics))
|
||||
for i, m := range metrics {
|
||||
peers[i] = m.Peer
|
||||
peers[i] = peer.IDB58Encode(m.Peer)
|
||||
}
|
||||
|
||||
*out = api.PeersToStrings(peers)
|
||||
*out = peers
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user