github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/juju/metricsdebug/metrics.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package metricsdebug
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"time"
    10  
    11  	"github.com/gosuri/uitable"
    12  	"github.com/juju/cmd"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/gnuflag"
    15  	"gopkg.in/juju/names.v2"
    16  
    17  	"github.com/juju/juju/api/metricsdebug"
    18  	"github.com/juju/juju/apiserver/params"
    19  	"github.com/juju/juju/cmd/modelcmd"
    20  )
    21  
    22  const metricsDoc = `
    23  Display recently collected metrics.
    24  `
    25  
    26  // MetricsCommand retrieves metrics stored in the juju controller.
    27  type MetricsCommand struct {
    28  	modelcmd.ModelCommandBase
    29  	out cmd.Output
    30  
    31  	Tags []string
    32  	All  bool
    33  }
    34  
    35  // New creates a new MetricsCommand.
    36  func New() cmd.Command {
    37  	return modelcmd.Wrap(&MetricsCommand{})
    38  }
    39  
    40  // Info implements Command.Info.
    41  func (c *MetricsCommand) Info() *cmd.Info {
    42  	return &cmd.Info{
    43  		Name:    "metrics",
    44  		Args:    "[tag1[...tagN]]",
    45  		Purpose: "Retrieve metrics collected by specified entities.",
    46  		Doc:     metricsDoc,
    47  	}
    48  }
    49  
    50  // Init reads and verifies the cli arguments for the MetricsCommand
    51  func (c *MetricsCommand) Init(args []string) error {
    52  	if !c.All && len(args) == 0 {
    53  		return errors.New("you need to specify at least one unit or application")
    54  	} else if c.All && len(args) > 0 {
    55  		return errors.New("cannot use --all with additional entities")
    56  	}
    57  	c.Tags = make([]string, len(args))
    58  	for i, arg := range args {
    59  		if names.IsValidUnit(arg) {
    60  			c.Tags[i] = names.NewUnitTag(arg).String()
    61  		} else if names.IsValidApplication(arg) {
    62  			c.Tags[i] = names.NewApplicationTag(arg).String()
    63  		} else {
    64  			return errors.Errorf("%q is not a valid unit or application", args[0])
    65  		}
    66  	}
    67  	return nil
    68  }
    69  
    70  // SetFlags implements cmd.Command.SetFlags.
    71  func (c *MetricsCommand) SetFlags(f *gnuflag.FlagSet) {
    72  	c.ModelCommandBase.SetFlags(f)
    73  	c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{
    74  		"tabular": formatTabular,
    75  		"json":    cmd.FormatJson,
    76  		"yaml":    cmd.FormatYaml,
    77  	})
    78  	f.BoolVar(&c.All, "all", false, "retrieve metrics collected by all units in the model")
    79  }
    80  
    81  type GetMetricsClient interface {
    82  	GetMetrics(tags ...string) ([]params.MetricResult, error)
    83  	Close() error
    84  }
    85  
    86  var newClient = func(env modelcmd.ModelCommandBase) (GetMetricsClient, error) {
    87  	state, err := env.NewAPIRoot()
    88  	if err != nil {
    89  		return nil, errors.Trace(err)
    90  	}
    91  	return metricsdebug.NewClient(state), nil
    92  }
    93  
    94  type metric struct {
    95  	Unit      string    `json:"unit" yaml:"unit"`
    96  	Timestamp time.Time `json:"timestamp" yaml:"timestamp"`
    97  	Metric    string    `json:"metric" yaml:"metric"`
    98  	Value     string    `json:"value" yaml:"value"`
    99  }
   100  
   101  // Run implements Command.Run.
   102  func (c *MetricsCommand) Run(ctx *cmd.Context) error {
   103  	client, err := newClient(c.ModelCommandBase)
   104  	if err != nil {
   105  		return errors.Trace(err)
   106  	}
   107  	var metrics []params.MetricResult
   108  	if c.All {
   109  		metrics, err = client.GetMetrics()
   110  	} else {
   111  		metrics, err = client.GetMetrics(c.Tags...)
   112  	}
   113  	if err != nil {
   114  		return errors.Trace(err)
   115  	}
   116  	defer client.Close()
   117  	if len(metrics) == 0 {
   118  		return nil
   119  	}
   120  	results := make([]metric, len(metrics))
   121  	for i, m := range metrics {
   122  		results[i] = metric{
   123  			Unit:      m.Unit,
   124  			Timestamp: m.Time,
   125  			Metric:    m.Key,
   126  			Value:     m.Value,
   127  		}
   128  	}
   129  	return errors.Trace(c.out.Write(ctx, results))
   130  }
   131  
   132  // formatTabular returns a tabular view of collected metrics.
   133  func formatTabular(writer io.Writer, value interface{}) error {
   134  	metrics, ok := value.([]metric)
   135  	if !ok {
   136  		return errors.Errorf("expected value of type %T, got %T", metrics, value)
   137  	}
   138  	table := uitable.New()
   139  	table.MaxColWidth = 50
   140  	table.Wrap = true
   141  	for _, col := range []int{1, 2, 3, 4} {
   142  		table.RightAlign(col)
   143  	}
   144  	table.AddRow("UNIT", "TIMESTAMP", "METRIC", "VALUE")
   145  	for _, m := range metrics {
   146  		table.AddRow(m.Unit, m.Timestamp.Format(time.RFC3339), m.Metric, m.Value)
   147  	}
   148  	_, err := fmt.Fprint(writer, table.String())
   149  	return errors.Trace(err)
   150  }