Skip to content

Commit 9a740c1

Browse files
feat: add option to send forwarded metrics to Observe via OTLP (#212)
### Description Add option to send forwarded metrics to Observe via OTLP. Going forward, we want to be able to send all custom metrics to Observe in the format the user prefers (between OTLP or Prometheus)
1 parent 74ea7a2 commit 9a740c1

File tree

18 files changed

+289
-14
lines changed

18 files changed

+289
-14
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ require (
1717
go.opentelemetry.io/collector/otelcol v0.124.0
1818
go.uber.org/zap v1.27.0
1919
golang.org/x/sys v0.32.0
20-
gopkg.in/yaml.v2 v2.4.0
2120
gopkg.in/yaml.v3 v3.0.1
2221
)
2322

@@ -442,6 +441,7 @@ require (
442441
gopkg.in/inf.v0 v0.9.1 // indirect
443442
gopkg.in/ini.v1 v1.67.0 // indirect
444443
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
444+
gopkg.in/yaml.v2 v2.4.0 // indirect
445445
k8s.io/api v0.32.3 // indirect
446446
k8s.io/apimachinery v0.32.3 // indirect
447447
k8s.io/client-go v0.32.3 // indirect

integration/scripts/snapshots/docker.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ exporters:
1414
sending_queue:
1515
num_consumers: 4
1616
queue_size: 100
17+
otlphttp/observemetrics:
18+
compression: zstd
19+
endpoint: https://123456789.collect.observe-eng.com/v2/otel
20+
headers:
21+
authorization: Bearer abcdefghijklmnopqrst:OWt0SXV4YTlqYUhpSHZjSlhXUDVkRXpl
22+
x-observe-target-package: Metrics
23+
retry_on_failure:
24+
enabled: true
25+
sending_queue:
26+
num_consumers: 4
27+
queue_size: 100
1728
otlphttp/observetracing:
1829
compression: zstd
1930
endpoint: https://123456789.collect.observe-eng.com/v2/otel
@@ -279,10 +290,13 @@ service:
279290
- otlphttp/observe
280291
- count
281292
processors:
293+
- memory_limiter
294+
- transform/truncate
282295
- resourcedetection
283296
- resourcedetection/cloud
284297
- attributes/observe_global_attributes
285298
- resource/observe_global_resource_attributes
299+
- batch
286300
receivers:
287301
- otlp
288302
logs/host_monitoring-file:
@@ -354,6 +368,7 @@ service:
354368
exporters:
355369
- prometheusremotewrite/observe
356370
processors:
371+
- memory_limiter
357372
- resourcedetection
358373
- resourcedetection/cloud
359374
- attributes/observe_global_attributes
@@ -390,10 +405,13 @@ service:
390405
exporters:
391406
- otlphttp/observetracing
392407
processors:
408+
- memory_limiter
409+
- transform/truncate
393410
- resourcedetection
394411
- resourcedetection/cloud
395412
- attributes/observe_global_attributes
396413
- resource/observe_global_resource_attributes
414+
- batch
397415
receivers:
398416
- otlp
399417
telemetry:

integration/scripts/snapshots/linux.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ exporters:
1414
sending_queue:
1515
num_consumers: 4
1616
queue_size: 100
17+
otlphttp/observemetrics:
18+
compression: zstd
19+
endpoint: https://123456789.collect.observe-eng.com/v2/otel
20+
headers:
21+
authorization: Bearer abcdefghijklmnopqrst:OWt0SXV4YTlqYUhpSHZjSlhXUDVkRXpl
22+
x-observe-target-package: Metrics
23+
retry_on_failure:
24+
enabled: true
25+
sending_queue:
26+
num_consumers: 4
27+
queue_size: 100
1728
otlphttp/observetracing:
1829
compression: zstd
1930
endpoint: https://123456789.collect.observe-eng.com/v2/otel
@@ -273,10 +284,13 @@ service:
273284
- otlphttp/observe
274285
- count
275286
processors:
287+
- memory_limiter
288+
- transform/truncate
276289
- resourcedetection
277290
- resourcedetection/cloud
278291
- attributes/observe_global_attributes
279292
- resource/observe_global_resource_attributes
293+
- batch
280294
receivers:
281295
- otlp
282296
logs/host_monitoring-file:
@@ -348,6 +362,7 @@ service:
348362
exporters:
349363
- prometheusremotewrite/observe
350364
processors:
365+
- memory_limiter
351366
- resourcedetection
352367
- resourcedetection/cloud
353368
- attributes/observe_global_attributes
@@ -384,10 +399,13 @@ service:
384399
exporters:
385400
- otlphttp/observetracing
386401
processors:
402+
- memory_limiter
403+
- transform/truncate
387404
- resourcedetection
388405
- resourcedetection/cloud
389406
- attributes/observe_global_attributes
390407
- resource/observe_global_resource_attributes
408+
- batch
391409
receivers:
392410
- otlp
393411
telemetry:

integration/scripts/snapshots/windows.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ exporters:
1414
sending_queue:
1515
num_consumers: 4
1616
queue_size: 100
17+
otlphttp/observemetrics:
18+
compression: zstd
19+
endpoint: https://123456789.collect.observe-eng.com/v2/otel
20+
headers:
21+
authorization: Bearer abcdefghijklmnopqrst:OWt0SXV4YTlqYUhpSHZjSlhXUDVkRXpl
22+
x-observe-target-package: Metrics
23+
retry_on_failure:
24+
enabled: true
25+
sending_queue:
26+
num_consumers: 4
27+
queue_size: 100
1728
otlphttp/observetracing:
1829
compression: zstd
1930
endpoint: https://123456789.collect.observe-eng.com/v2/otel
@@ -277,7 +288,6 @@ service:
277288
- prometheusremotewrite/observe
278289
processors:
279290
- memory_limiter
280-
- transform/truncate
281291
- resourcedetection
282292
- resourcedetection/cloud
283293
- attributes/observe_global_attributes

internal/commands/diagnose/configcheck.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ package diagnose
22

33
import (
44
"embed"
5+
"errors"
56
"fmt"
67
"os"
78

89
"github.com/observeinc/observe-agent/internal/config"
910
"github.com/spf13/viper"
10-
"gopkg.in/yaml.v2"
11+
"gopkg.in/yaml.v3"
1112
)
1213

1314
type ConfigTestResult struct {
@@ -18,31 +19,52 @@ type ConfigTestResult struct {
1819
}
1920

2021
func checkConfig(v *viper.Viper) (bool, any, error) {
22+
// Ensure there is an observe-agent config file.
2123
configFile := v.ConfigFileUsed()
2224
if configFile == "" {
2325
return false, nil, fmt.Errorf("no config file defined")
2426
}
27+
if _, err := os.Stat(configFile); err != nil && errors.Is(err, os.ErrNotExist) {
28+
return false, nil, fmt.Errorf("config file %s does not exist", configFile)
29+
}
30+
31+
// Ensure the file is valid yaml.
2532
contents, err := os.ReadFile(configFile)
2633
if err != nil {
27-
return false, nil, err
34+
return false, nil, fmt.Errorf("error reading config file %s: %w", configFile, err)
2835
}
29-
var conf config.AgentConfig
30-
if err = yaml.Unmarshal(contents, &conf); err != nil {
36+
var yamlMap map[string]any
37+
if err = yaml.Unmarshal(contents, &yamlMap); err != nil {
3138
return false, ConfigTestResult{
3239
ConfigFile: configFile,
3340
ParseSucceeded: false,
3441
IsValid: false,
3542
Error: err.Error(),
3643
}, nil
3744
}
38-
if err = conf.Validate(); err != nil {
45+
46+
// Ensure the agent config can be loaded via viper.
47+
agentConfig, err := config.AgentConfigFromViper(v)
48+
if err != nil {
49+
return false, ConfigTestResult{
50+
ConfigFile: configFile,
51+
ParseSucceeded: false,
52+
IsValid: false,
53+
Error: err.Error(),
54+
}, nil
55+
}
56+
57+
// Ensure the agent config is valid.
58+
if err = agentConfig.Validate(); err != nil {
3959
return false, ConfigTestResult{
4060
ConfigFile: configFile,
4161
ParseSucceeded: true,
4262
IsValid: false,
4363
Error: err.Error(),
4464
}, nil
4565
}
66+
67+
// All checks passed.
4668
return true, ConfigTestResult{
4769
ConfigFile: configFile,
4870
ParseSucceeded: true,

internal/commands/diagnose/configcheck_test.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ observe_url: "https://collect.observeinc.com"
1818
# Debug mode - Sets agent log level to debug
1919
debug: false
2020
21+
forwarding:
22+
enabled: true
23+
metrics:
24+
output_format: otel
25+
2126
# collect metrics and logs pertaining to the agent itself
2227
self_monitoring:
2328
enabled: true
@@ -62,10 +67,15 @@ func Test_checkConfig(t *testing.T) {
6267
f, err := os.CreateTemp("", "test-config-*.yaml")
6368
assert.NoError(t, err)
6469
defer os.Remove(f.Name())
65-
f.Write([]byte(tc.confStr))
70+
_, err = f.Write([]byte(tc.confStr))
71+
assert.NoError(t, err)
6672

6773
v := viper.New()
6874
v.SetConfigFile(f.Name())
75+
err = v.ReadInConfig()
76+
if tc.shouldParse {
77+
assert.NoError(t, err)
78+
}
6979
success, resultAny, err := checkConfig(v)
7080
assert.NoError(t, err)
7181
result, ok := resultAny.(ConfigTestResult)

internal/commands/initconfig/initconfig.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ var (
1919
observe_url string
2020
cloud_resource_detectors []string
2121
resource_attributes map[string]string
22+
forwarding_metrics_format string
2223
self_monitoring_enabled bool
2324
host_monitoring_enabled bool
2425
host_monitoring_logs_enabled bool
@@ -87,6 +88,9 @@ func RegisterConfigFlags(cmd *cobra.Command, v *viper.Viper) {
8788
cmd.PersistentFlags().StringToStringVar(&resource_attributes, "resource_attributes", map[string]string{}, "The cloud environments from which to detect resources")
8889
v.BindPFlag("resource_attributes", cmd.PersistentFlags().Lookup("resource_attributes"))
8990

91+
cmd.PersistentFlags().StringVar(&forwarding_metrics_format, "forwarding::metrics::output_format", "", "Format for sending app metrics to Observe, valid options are 'prometheus' and 'otel'")
92+
v.BindPFlag("forwarding::metrics::output_format", cmd.PersistentFlags().Lookup("forwarding::metrics::output_format"))
93+
9094
cmd.PersistentFlags().BoolVar(&self_monitoring_enabled, "self_monitoring::enabled", true, "Enable self monitoring")
9195
v.BindPFlag("self_monitoring::enabled", cmd.PersistentFlags().Lookup("self_monitoring::enabled"))
9296
v.SetDefault("self_monitoring::enabled", true)

internal/commands/initconfig/initconfig_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,33 @@ func Test_InitConfigCommand(t *testing.T) {
7474
}),
7575
expectErr: "",
7676
},
77+
{
78+
args: []string{"--config_path=./test-config.yaml", "--token=test-token", "--observe_url=test-url", "--forwarding::metrics::output_format=otel"},
79+
expectedConfig: setConfigDefaults(config.AgentConfig{
80+
Token: "test-token",
81+
ObserveURL: "test-url",
82+
Forwarding: config.ForwardingConfig{
83+
Metrics: config.ForwardingMetricsConfig{
84+
OutputFormat: "otel",
85+
},
86+
},
87+
SelfMonitoring: config.SelfMonitoringConfig{
88+
Enabled: true,
89+
},
90+
HostMonitoring: config.HostMonitoringConfig{
91+
Enabled: true,
92+
Logs: config.HostMonitoringLogsConfig{
93+
Enabled: true,
94+
},
95+
Metrics: config.HostMonitoringMetricsConfig{
96+
Host: config.HostMonitoringHostMetricsConfig{
97+
Enabled: true,
98+
},
99+
},
100+
},
101+
}),
102+
expectErr: "",
103+
},
77104
}
78105
for _, tc := range testcases {
79106
v := viper.NewWithOptions(viper.KeyDelimiter("::"))

internal/config/configschema.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,17 @@ type HealthCheckConfig struct {
4646
Path string `yaml:"path" mapstructure:"path" default:"/status"`
4747
}
4848

49+
type ForwardingMetricsConfig struct {
50+
OutputFormat string `yaml:"output_format,omitempty" mapstructure:"output_format" default:"prometheus"`
51+
}
52+
53+
func (config *ForwardingMetricsConfig) OtlpMetrics() bool {
54+
return config.OutputFormat == "otel"
55+
}
56+
4957
type ForwardingConfig struct {
50-
Enabled bool `yaml:"enabled" mapstructure:"enabled" default:"true"`
58+
Enabled bool `yaml:"enabled" mapstructure:"enabled" default:"true"`
59+
Metrics ForwardingMetricsConfig `yaml:"metrics,omitempty" mapstructure:"metrics"`
5160
}
5261

5362
type InternalTelemetryMetricsConfig struct {
@@ -145,5 +154,10 @@ func (config *AgentConfig) Validate() error {
145154
if !strings.Contains(config.Token, ":") {
146155
return errors.New("invalid Token, the provided value may be the token ID instead of the token itself")
147156
}
157+
158+
if config.Forwarding.Metrics.OutputFormat != "prometheus" && config.Forwarding.Metrics.OutputFormat != "otel" {
159+
return fmt.Errorf("invalid metrics forwarding output format '%s' - valid options are 'prometheus' and 'otel'", config.Forwarding.Metrics.OutputFormat)
160+
}
161+
148162
return nil
149163
}

0 commit comments

Comments
 (0)