-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add plugin to support writing metrics to Clarify.
- Loading branch information
1 parent
129d8eb
commit 2e7a342
Showing
7 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
//go:build !custom || outputs || outputs.clarify | ||
|
||
package all | ||
|
||
import _ "github.com/influxdata/telegraf/plugins/outputs/clarify" // register plugin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# Clarify Output Plugin | ||
|
||
This plugin writes to [Clarify][clarify]. To use this plugin you will | ||
need to obtain a set of [credentials][credentials]. | ||
|
||
## Global configuration options <!-- @/docs/includes/plugin_config.md --> | ||
|
||
In addition to the plugin-specific configuration settings, plugins support | ||
additional global and plugin configuration settings. These settings are used to | ||
modify metrics, tags, and field or create aliases and configure ordering, etc. | ||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details. | ||
|
||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins | ||
|
||
## Configuration | ||
|
||
```toml @sample.conf | ||
[[outputs.clarify]] | ||
## Credentials File (OAuth 2.0 from Clarify integration). | ||
credentials_file = "/path/to/clarify/credentials.json" | ||
|
||
## Clarify username password (Basic Auth from Clarify integration). | ||
username = "i-am-bob" | ||
password = "secret-password" | ||
|
||
## Tags to be included when generating the unique ID for a signal in Clarify. | ||
id_tags = ['sensor'] | ||
``` | ||
|
||
You can use either a credentials file or username/password. | ||
If both are present and valid in the configuration the | ||
credentials file will be used. | ||
|
||
## How Telegraf Metrics map to Clarify signals | ||
|
||
Clarify signal names are formed by joining the Telegraf metric name and the | ||
field key with a `.` character. Telegraf tags are added to signal labels. | ||
|
||
If a tag named `clarify_input_id` is present as a tag and there is only one | ||
field present in the metric, this tag will be used as the inputID in Clarify. | ||
|
||
If information from one or several tags is needed to uniquely identify a metric | ||
field, the id_tags array can be added to the config with the needed tag names. | ||
E.g: | ||
|
||
`id_tags = ['sensor']` | ||
|
||
Clarify only supports values that can be converted to floating point numbers. | ||
Strings and invalid numbers are ignored. | ||
|
||
[clarify]: https://clarify.io | ||
[clarifydoc]: https://docs.clarify.io | ||
[credentials]: https://docs.clarify.io/users/admin/integrations/credentials | ||
|
||
## Example parsing | ||
|
||
The following input would be stored in Clarify with the values shown below: | ||
|
||
```text | ||
temperature,host=demo.clarifylocal,sensor=TC0P value=49 1682670910000000000 | ||
``` | ||
|
||
```json | ||
"signal" { | ||
"id": "temperature.value.TC0P" | ||
"name": "temperature.value" | ||
"labels": { | ||
"host": ["demo.clarifylocal"], | ||
"sensor": ["TC0P"] | ||
} | ||
} | ||
"values" { | ||
"times": ["2023-04-28T08:43:16+00:00"], | ||
"series": { | ||
"temperature.value.TC0P": [49] | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
package clarify | ||
|
||
import ( | ||
"context" | ||
_ "embed" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/clarify/clarify-go" | ||
"github.com/clarify/clarify-go/fields" | ||
"github.com/clarify/clarify-go/views" | ||
"github.com/influxdata/telegraf" | ||
"github.com/influxdata/telegraf/plugins/outputs" | ||
) | ||
|
||
type Clarify struct { | ||
Username string `toml:"username"` | ||
Password string `toml:"password"` | ||
CredentialsFile string `toml:"credentials_file"` | ||
IDTags []string `toml:"id_tags"` | ||
Log telegraf.Logger `toml:"-"` | ||
|
||
client *clarify.Client | ||
} | ||
|
||
//go:embed sample.conf | ||
var sampleConfig string | ||
|
||
func (c *Clarify) Connect() error { | ||
ctx := context.Background() | ||
if c.CredentialsFile != "" { | ||
creds, err := clarify.CredentialsFromFile(c.CredentialsFile) | ||
if err != nil { | ||
return err | ||
} | ||
c.client = creds.Client(ctx) | ||
return nil | ||
} | ||
if c.Username != "" && c.Password != "" { | ||
creds := clarify.BasicAuthCredentials(c.Username, c.Password) | ||
c.client = creds.Client(ctx) | ||
return nil | ||
} | ||
return fmt.Errorf("no Clarify credentials provided") | ||
} | ||
|
||
func verifyValue(v interface{}) (float64, error) { | ||
var value float64 | ||
switch v := v.(type) { | ||
case bool: | ||
value = float64(0) | ||
if v { | ||
value = float64(1) | ||
} | ||
case uint8: | ||
value = float64(v) | ||
case uint16: | ||
value = float64(v) | ||
case uint32: | ||
value = float64(v) | ||
case uint64: | ||
value = float64(v) | ||
case int8: | ||
value = float64(v) | ||
case int16: | ||
value = float64(v) | ||
case int32: | ||
value = float64(v) | ||
case int64: | ||
value = float64(v) | ||
case float32: | ||
value = float64(v) | ||
case float64: | ||
value = v | ||
default: | ||
return value, fmt.Errorf("unsupported field type: %T", v) | ||
} | ||
return value, nil | ||
} | ||
|
||
func (c *Clarify) Write(metrics []telegraf.Metric) error { | ||
signals := make(map[string]views.SignalSave) | ||
frame := views.DataFrame{} | ||
|
||
for _, m := range metrics { | ||
for _, f := range m.FieldList() { | ||
if value, err := verifyValue(f.Value); err == nil { | ||
id := c.generateID(m, f) | ||
ts := fields.AsTimestamp(m.Time()) | ||
|
||
if _, ok := frame[id]; ok { | ||
frame[id][ts] = value | ||
} else { | ||
frame[id] = views.DataSeries{ts: value} | ||
} | ||
|
||
s := views.SignalSave{} | ||
s.Name = fmt.Sprintf("%s.%s", m.Name(), f.Key) | ||
|
||
for _, t := range m.TagList() { | ||
labelName := strings.ReplaceAll(t.Key, " ", "-") | ||
labelName = strings.ReplaceAll(labelName, "_", "-") | ||
labelName = strings.ToLower(labelName) | ||
s.Labels.Add(labelName, t.Value) | ||
} | ||
|
||
signals[id] = s | ||
} else { | ||
c.Log.Infof("Unable to add field `%s` for metric `%s` due to error '%v', skipping", f.Key, m.Name(), err) | ||
} | ||
} | ||
} | ||
|
||
if _, err := c.client.Insert(frame).Do(context.Background()); err != nil { | ||
return err | ||
} | ||
|
||
if _, err := c.client.SaveSignals(signals).Do(context.Background()); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (c *Clarify) generateID(m telegraf.Metric, f *telegraf.Field) string { | ||
var id string | ||
cid, exist := m.GetTag("clarify_input_id") | ||
if exist && len(m.FieldList()) == 1 { | ||
id = cid | ||
} else { | ||
id = fmt.Sprintf("%s.%s", m.Name(), f.Key) | ||
for _, idTag := range c.IDTags { | ||
if m.HasTag(idTag) { | ||
id = fmt.Sprintf("%s.%s", id, m.Tags()[idTag]) | ||
} | ||
} | ||
} | ||
return strings.ToLower(id) | ||
} | ||
|
||
func (c *Clarify) SampleConfig() string { | ||
return sampleConfig | ||
} | ||
|
||
func (c *Clarify) Close() error { | ||
c.client = nil | ||
return nil | ||
} | ||
|
||
func init() { | ||
outputs.Add("clarify", func() telegraf.Output { | ||
return &Clarify{} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
## Credentials File (Oauth2 from Clarify Integration) | ||
credentials_file = "/path/to/clarify/credentials.json" | ||
|
||
## Clarify username password (Basic Auth from Clarify Integration) | ||
username = "i-am-bob" | ||
password = "secret-password" | ||
|
||
## Tags to be included when generating the unique ID for a signal in Clarify | ||
id_tags = ['sensor'] |