Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metric metadata syncer to SignalFx exporter #231

Merged
merged 16 commits into from
May 26, 2020
Merged
9 changes: 8 additions & 1 deletion exporter/signalfxexporter/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# SignalFx Metrics Exporter

How to send metrics to SignalFx.
This exporter can be used to send metrics to SignalFx.

Apart from metrics, the exporter is also capable of sending metric metadata (properties and tags)
to SignalFx. Currently, only metric metadata updates from the [k8s_cluster receiver](../../receiver/k8sclusterreceiver/README.md)
are supported.

The following configuration options are required:

Expand All @@ -15,6 +19,9 @@ The following configuration options can also be configured:
- `ingest_url` (default = https://ingest.`realm`.signalfx.com/v2/datapoint): Destination
where SignalFx metrics are sent. If this option is specified, `realm` is ignored.
If path is not specified, `/v2/datapoint` is used.
- `api_url` (default = https://api.`realm`.signalfx.com/): Destination to which SignalFx
[properties and tags](https://docs.signalfx.com/en/latest/metrics-metadata/metrics-metadata.html#metrics-metadata) are sent.
- `log_dimension_updates` (default = `false`): Whether or not to log dimension updates.

Example:

Expand Down
35 changes: 31 additions & 4 deletions exporter/signalfxexporter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ type Config struct {
// If a path is specified it will use the one set by the config.
IngestURL string `mapstructure:"ingest_url"`

// APIURL is the destination to where SignalFx metadata will be sent. This
// value takes precedence over the value of Realm
APIURL string `mapstructure:"api_url"`

// Timeout is the maximum timeout for HTTP request sending trace data. The
// default value is 5 seconds.
Timeout time.Duration `mapstructure:"timeout"`
Expand All @@ -51,6 +55,9 @@ type Config struct {
// exporter, eg: "User-Agent" can be set to a custom value if specified
// here.
Headers map[string]string `mapstructure:"headers"`

// Whether to log dimension updates being sent to SignalFx.
LogDimensionUpdates bool `mapstructure:"log_dimension_updates"`
}

func (cfg *Config) getOptionsFromConfig() (*exporterOptions, error) {
Expand All @@ -63,19 +70,32 @@ func (cfg *Config) getOptionsFromConfig() (*exporterOptions, error) {
return nil, fmt.Errorf("invalid \"ingest_url\": %v", err)
}

apiURL, err := cfg.getAPIURL()
if err != nil {
return nil, fmt.Errorf("invalid \"api_url\": %v", err)
}

if cfg.Timeout == 0 {
cfg.Timeout = 5 * time.Second
}

return &exporterOptions{
ingestURL: ingestURL,
httpTimeout: cfg.Timeout,
ingestURL: ingestURL,
apiURL: apiURL,
httpTimeout: cfg.Timeout,
token: cfg.AccessToken,
logDimUpdate: cfg.LogDimensionUpdates,
}, nil
}

func (cfg *Config) validateConfig() error {
if cfg.Realm == "" && cfg.IngestURL == "" {
return errors.New("requires a non-empty \"realm\" or \"ingest_url\"")
if cfg.AccessToken == "" {
return errors.New("requires a non-empty \"access_token\"")
}

if cfg.Realm == "" && (cfg.IngestURL == "" || cfg.APIURL == "") {
return errors.New("requires a non-empty \"realm\", or" +
" \"ingest_url\" and \"api_url\" should be explicitly set")
}

if cfg.Timeout < 0 {
Expand Down Expand Up @@ -104,3 +124,10 @@ func (cfg *Config) getIngestURL() (out *url.URL, err error) {

return out, err
}

func (cfg *Config) getAPIURL() (*url.URL, error) {
if cfg.APIURL == "" {
return url.Parse(fmt.Sprintf("https://api.%s.signalfx.com", cfg.Realm))
}
return url.Parse(cfg.APIURL)
}
24 changes: 20 additions & 4 deletions exporter/signalfxexporter/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func TestConfig_getOptionsFromConfig(t *testing.T) {
AccessToken string
Realm string
IngestURL string
APIURL string
Timeout time.Duration
Headers map[string]string
}
Expand All @@ -86,32 +87,46 @@ func TestConfig_getOptionsFromConfig(t *testing.T) {
{
name: "Test URL overrides",
fields: fields{
Realm: "us0",
IngestURL: "https://ingest.us1.signalfx.com/",
Realm: "us0",
AccessToken: "access_token",
IngestURL: "https://ingest.us1.signalfx.com/",
APIURL: "https://api.us1.signalfx.com/",
},
want: &exporterOptions{
ingestURL: &url.URL{
Scheme: "https",
Host: "ingest.us1.signalfx.com",
Path: "/v2/datapoint",
},
apiURL: &url.URL{
Scheme: "https",
Host: "api.us1.signalfx.com",
Path: "/",
},
httpTimeout: 5 * time.Second,
token: "access_token",
},
wantErr: false,
},
{
name: "Test URL from Realm",
fields: fields{
Realm: "us0",
Timeout: 10 * time.Second,
Realm: "us0",
AccessToken: "access_token",
Timeout: 10 * time.Second,
},
want: &exporterOptions{
ingestURL: &url.URL{
Scheme: "https",
Host: "ingest.us0.signalfx.com",
Path: "/v2/datapoint",
},
apiURL: &url.URL{
Scheme: "https",
Host: "api.us0.signalfx.com",
},
httpTimeout: 10 * time.Second,
token: "access_token",
},
wantErr: false,
},
Expand All @@ -128,6 +143,7 @@ func TestConfig_getOptionsFromConfig(t *testing.T) {
AccessToken: tt.fields.AccessToken,
Realm: tt.fields.Realm,
IngestURL: tt.fields.IngestURL,
APIURL: tt.fields.APIURL,
Timeout: tt.fields.Timeout,
Headers: tt.fields.Headers,
}
Expand Down
Loading