Skip to content
This repository has been archived by the owner on Nov 8, 2022. It is now read-only.

Commit

Permalink
SDI-1394 a metric can be changed from static to dynamic
Browse files Browse the repository at this point in the history
  • Loading branch information
candysmurf committed Jun 21, 2016
1 parent 93f40c3 commit e58015d
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 5 deletions.
27 changes: 22 additions & 5 deletions control/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ func errorMetricEndsWithAsterisk(ns string) error {
return fmt.Errorf("Metric namespace %s ends with an asterisk is not allowed", ns)
}

func errorMetricStaticElementHasName(value, name, ns string) error {
return fmt.Errorf("A static element %s should not define name %s for namespace %s.", value, name, ns)
}

func errorMetricDynamicElemnetHasNoName(value, ns string) error {
return fmt.Errorf("A dynamic element %s requires a name for namespace %s.", value, ns)
}

// listNotAllowedChars returns list of not allowed characters in metric's namespace as a string
// which is used in construct errorMetricContainsNotAllowedChars as a recommendation
// exemplary output: "brackets [( ) [ ] { }], spaces [ ], punctuations [. , ; ? !], slashes [| \ /], carets [^], quotations [" ` ']"
Expand Down Expand Up @@ -338,19 +346,28 @@ func (mc *metricCatalog) removeMatchedKey(key string) {

// validateMetricNamespace validates metric namespace in terms of containing not allowed characters and ending with an asterisk
func validateMetricNamespace(ns core.Namespace) error {
name := ""
value := ""
for _, i := range ns {
name += i.Value
// A dynamic element requires the name while a static element does not.
if i.Name != "" && i.Value != "*" {
return errorMetricStaticElementHasName(i.Value, i.Name, ns.String())
}
if i.Name == "" && i.Value == "*" {
return errorMetricDynamicElemnetHasNoName(i.Value, ns.String())
}

value += i.Value
}

for _, chars := range notAllowedChars {
for _, ch := range chars {
if strings.ContainsAny(name, ch) {
if strings.ContainsAny(value, ch) {
return errorMetricContainsNotAllowedChars(ns.String())
}
}
}
// plugin should NOT advertise metrics ending with a wildcard
if strings.HasSuffix(name, "*") {
if strings.HasSuffix(value, "*") {
return errorMetricEndsWithAsterisk(ns.String())
}

Expand All @@ -363,7 +380,7 @@ func (mc *metricCatalog) AddLoadedMetricType(lp *loadedPlugin, mt core.Metric) e
"_module": "control",
"_file": "metrics.go,",
"_block": "add-loaded-metric-type",
"error": fmt.Errorf("Metric namespace %s contains not allowed characters", mt.Namespace()),
"error": fmt.Errorf("Metric namespace %s is invalid", mt.Namespace()),
}).Error("error adding loaded metric type")
return err
}
Expand Down
27 changes: 27 additions & 0 deletions control/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,3 +561,30 @@ func TestMetricNamespaceValidation(t *testing.T) {
})
})
}

func TestMetricStaticDynamicNamespace(t *testing.T) {
Convey("validateStaticDynamic()", t, func() {
Convey("has static elements only", func() {
ns := core.NewNamespace("mock", "foo", "bar")
err := validateMetricNamespace(ns)
So(err, ShouldBeNil)
})
Convey("had both static and dynamic elements", func() {
ns := core.NewNamespace("mock", "foo", "*", "bar")
ns[2].Name = "dynamic element"
err := validateMetricNamespace(ns)
So(err, ShouldBeNil)
})
Convey("has name for a static element", func() {
ns := core.NewNamespace("mock", "foo")
ns[0].Name = "static element"
err := validateMetricNamespace(ns)
So(err, ShouldNotBeNil)
})
Convey("has * but no name", func() {
ns := core.NewNamespace("mock", "foo", "*", "bar")
err := validateMetricNamespace(ns)
So(err, ShouldNotBeNil)
})
})
}
100 changes: 100 additions & 0 deletions docs/METRICS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@

# Snap Static and Dynamic Metrics

Snap Framework supports two types of metrics. They are static and dynamic metrics. A Snap metric consists of a namespace and value pair.

### Static Metrics
A namespace having no wildcard and only string literals separated by slashes is a static metric.

String representation examples:
```
/intel/cassandra/node/zeus/type/Cache/scope/KeyCache/name/Requests/OneMinuteRate
/intel/cassandra/node/zeus/type/ClientRequest/scope/CASRead/name/Unavailables/FiveMinuteRate
/intel/cassandra/node/apollo/type/Cache/scope/RowCache/name/Hits/OneMinuteRate
/intel/cassandra/node/apollo/type/ClientRequest/scope/RangeSlice/name/Latency/OneMinuteRate
```
### Dynamic Metrics
A namespace including at least one wildcard is a dynamic metric.

String representation examples:
```
/intel/cassandra/node/*/type/*/scope/*/name/*/OneMinuteRate
/intel/cassandra/node/*/type/*/scope/*/name/*/FiveMinuteRate
/intel/cassandra/node/*/type/*/keyspace/*/name/*/OneMinuteRate
/intel/cassandra/node/*/type/*/keyspace/*/name/*/FiveMinuteRate
```
### Namespace Element
Namespace element in Snap is defined as a struct. Both static and dynamic elements share the same definition.
```
type NamespaceElement struct {
Value string
Description string
Name string
}
```
The _`NamespaceElement`_ forms each cell of a namespace and those cells are separated by slashes.

Create a dynamic element:
```
ns := core.NewNamespace("intel", "cassandra", "node")
.AddDynamicElement("node name", "description")
```

Create multiple dynamic elements:
```
ns := core.NewNamespace("intel", "cassandra", "node")
.AddDynamicElement("node name", "description")
.AddStaticElement("type")
.AddDynamicElement("type value", "description")
.AddStaticElement("scope")
.AddDynamicElement("scope value", "description")
.AddStaticElement("name")
.AddDynamicElement("name value", "description")
.AddStaticElement("50thPercentile")
```
>The key takeaway is that the _`named element`_ is a _`dynamic element`_. A static element has an _`empty Name`_ field.
### Collector Plugins
Building a Snap collector plugin involves two primary tasks. One is to create a collector metric catalog. Another is to collect metric data.

##### Create Metric Catalog
Creating a collector having dynamic metric catalog by utilizing the following Snap methods from the _`core`_ package, Snap CLI(snapctl) verbose output could display the definition and the description of a wildcard.

Methods:
```
(n Namespace) AddStaticElement(value string) Namespace
(n Namespace) AddDynamicElement(name, description string) Namespace
(n Namespace) AddStaticElements(values ...string) Namespace
```

Create Metric Catalog:
```
metricType := plugin.MetricType{
Namespace_: ns,
Unit_: <namesapce measurement type>,
}
```
If the `Unit` is defined, Snap CLI verbose output can show the metric data type too.

Snap CLI verbose output:
```
$ $SNAP_PATH/bin/snapctl metric list --verbose
/intel/cassandra/node/[node name]/type/[type value]/scope/[scope value]/name/[name value]/OneMinuteRate float64
/intel/cassandra/node/[node name]/type/[type value]/scope/[scope value]/name/[name value]/FiveMinuteRate float64
/intel/cassandra/node/[node name]/type/[type value]/keyspace/[scope value]/name/[name value]/OneMinuteRate float64
/intel/cassandra/node/[node name]/type/[type value]/keyspace/[scope value]/name/[name value]/FiveMinuteRate float64
```
##### Collecting metric data
Snap provides the `IsDynamic() bool` method in its control package to determine an element of the namespace is dynamic or static. For _`IsDynamic`_ to function correctly, collectors need to define the `Name` field for every collected _`dynamic`_ element.

>Do not use the _`(n Namespace) AddDynamicElement(value string) Namespace`_ method to build dynamic namespace elements for metric data. A metric needs the actual value. Set the dynamic element name using dot notation.
### Publisher Plugins
Snap publisher plugins may leverage the _`IsDynamic() bool`_ method from Snap control package to determine if an element is dynamic or static _`if and only if`_ a collector correctly defined the _`Name`_ field
for every dynamic element. For instance, publisher plugins may base on `IsDynamic()` to strip off dynamic elements from a metric namespace to create searchable Snap metric tags.

> Snap checks the Name field of a namespace element to determine if an item is static or dynamic. The _`Name`_ field should be empty as a static element while not empty as a dynamic element.
Appropriately define your dynamic element to leverage Snap framework!

4 changes: 4 additions & 0 deletions docs/PLUGIN_AUTHORING.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ Example:

Snap validates the metrics exposed by plugin and, if validation failed, return an error and not load the plugin.

##### c) static and dynamic metrics
Snap supports both static and dynamic metrics. Do you like to know the differences and the recommendation for plugins?
See details [here](./METRICS.md).

### Mandatory packages
There are three mandatory packages that every plugin must use. Other than those three packages, you can use other packages as necessary. There is no danger of colliding dependencies as plugins are separated processes. The mandatory packages are:
```
Expand Down

0 comments on commit e58015d

Please sign in to comment.