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

Fix Collector Metrics Prefix #530

Merged
merged 3 commits into from Feb 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/service-extensions.md
@@ -1,6 +1,6 @@
# OpenTelemetry Collector: Extensions

Besides the pipeline elements (receivers, processors, and exporters) the OTelSvc
Besides the pipeline elements (receivers, processors, and exporters) the Collector
uses various service extensions (e.g.: healthcheck, z-pages, etc).
This document describes the “extensions” design and how they are implemented.

Expand Down Expand Up @@ -46,7 +46,7 @@ these sections would look like in the configuration file:

```yaml

# Example of the extensions provided in OTelSvc core. The list below
# Example of the extensions available with the core Collector. The list below
# includes all configurable options and their respective default value.
extensions:
health_check:
Expand Down
2 changes: 1 addition & 1 deletion exporter/README.md
Expand Up @@ -86,7 +86,7 @@ Exports traces and/or metrics to the console via zap.Logger
* `loglevel`: the log level of the logging export (debug|info|warn|error). Default is `info`.

## <a name="opencensus"></a>OpenCensus
Exports traces and/or metrics to another OTel-Svc endpoint via gRPC.
Exports traces and/or metrics to another Collector via gRPC using OpenCensus format.

### <a name="opencensus-configuration"></a>Configuration

Expand Down
4 changes: 2 additions & 2 deletions observability/observability.go
Expand Up @@ -131,7 +131,7 @@ var AllViews = []*view.View{
ViewExporterDroppedTimeSeries,
}

// ContextWithReceiverName adds the tag "otelsvc_receiver" and the name of the receiver as the value,
// ContextWithReceiverName adds the tag "receiver" and the name of the receiver as the value,
// and returns the newly created context. For receivers that can receive multiple signals it is
// recommended to encode the signal as suffix (e.g. "oc_trace" and "oc_metrics").
func ContextWithReceiverName(ctx context.Context, receiverName string) context.Context {
Expand All @@ -151,7 +151,7 @@ func RecordMetricsForMetricsReceiver(ctxWithTraceReceiverName context.Context, r
stats.Record(ctxWithTraceReceiverName, mReceiverReceivedTimeSeries.M(int64(receivedTimeSeries)), mReceiverDroppedTimeSeries.M(int64(droppedTimeSeries)))
}

// ContextWithExporterName adds the tag "otelsvc_exporter" and the name of the exporter as the value,
// ContextWithExporterName adds the tag "exporter" and the name of the exporter as the value,
// and returns the newly created context. For exporters that can export multiple signals it is
// recommended to encode the signal as suffix (e.g. "oc_trace" and "oc_metrics").
func ContextWithExporterName(ctx context.Context, exporterName string) context.Context {
Expand Down
53 changes: 42 additions & 11 deletions service/service_test.go
Expand Up @@ -16,8 +16,12 @@
package service

import (
"bufio"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -38,27 +42,25 @@ func TestApplication_Start(t *testing.T) {
app, err := New(factories, ApplicationStartInfo{})
require.NoError(t, err)

const testPrefix = "a_test"
metricsPort := testutils.GetAvailablePort(t)
app.rootCmd.SetArgs([]string{
"--config=testdata/otelcol-config.yaml",
"--metrics-port=" + strconv.FormatUint(uint64(metricsPort), 10),
"--metrics-prefix=" + testPrefix,
})

appDone := make(chan struct{})
go func() {
defer close(appDone)
if err := app.Start(); err != nil {
t.Errorf("app.Start() got %v, want nil", err)
return
}
assert.NoError(t, app.Start())
}()

<-app.readyChan

// TODO: Add a way to change configuration files so we can get the ports dynamically
if !isAppAvailable(t, "http://localhost:13133") {
t.Fatalf("app didn't reach ready state")
}
require.True(t, isAppAvailable(t, "http://localhost:13133"))

assertMetricsPrefix(t, testPrefix, metricsPort)

close(app.stopTestChan)
<-appDone
Expand All @@ -69,13 +71,42 @@ func TestApplication_Start(t *testing.T) {
func isAppAvailable(t *testing.T, healthCheckEndPoint string) bool {
client := &http.Client{}
resp, err := client.Get(healthCheckEndPoint)
if err != nil {
t.Fatalf("failed to get a response from health probe: %v", err)
}
require.NoError(t, err)

defer resp.Body.Close()
return resp.StatusCode == http.StatusOK
}

func assertMetricsPrefix(t *testing.T, prefix string, metricsPort uint16) {
client := &http.Client{}
resp, err := client.Get(fmt.Sprintf("http://localhost:%d/metrics", metricsPort))
require.NoError(t, err)

defer resp.Body.Close()
reader := bufio.NewReader(resp.Body)

for {
s, err := reader.ReadString('\n')
if err == io.EOF {
break
}

require.NoError(t, err)
if len(s) == 0 || s[0] == '#' {
// Skip this line since it is not a metric.
continue
}

// require is used here so test fails with a single message.
require.True(
t,
strings.HasPrefix(s, prefix),
"expected prefix %q but string starts with %q",
prefix,
s[:len(prefix)+1]+"...")
}
}

func TestApplication_setupExtensions(t *testing.T) {
exampleExtensionFactory := &config.ExampleExtensionFactory{}
exampleExtensionConfig := &config.ExampleExtension{
Expand Down
31 changes: 23 additions & 8 deletions service/telemetry.go
Expand Up @@ -33,27 +33,42 @@ import (
)

const (
metricsPortCfg = "metrics-port"
metricsLevelCfg = "metrics-level"
metricsPortCfg = "metrics-port"
metricsLevelCfg = "metrics-level"
metricsPrefixCfg = "metrics-prefix"
)

var (
// AppTelemetry is application's own telemetry.
AppTelemetry = &appTelemetry{}

// Command-line flags that control publication of telemetry data.
metricsLevelPtr *string
metricsPortPtr *uint
metricsLevelPtr *string
metricsPortPtr *uint
metricsPrefixPtr *string
)

type appTelemetry struct {
views []*view.View
}

func telemetryFlags(flags *flag.FlagSet) {
metricsLevelPtr = flags.String(metricsLevelCfg, "BASIC", "Output level of telemetry metrics (NONE, BASIC, NORMAL, DETAILED)")
// At least until we can use a generic, i.e.: OpenCensus, metrics exporter we default to Prometheus at port 8888, if not otherwise specified.
metricsPortPtr = flags.Uint(metricsPortCfg, 8888, "Port exposing collector telemetry.")
metricsLevelPtr = flags.String(
metricsLevelCfg,
"BASIC",
"Output level of telemetry metrics (NONE, BASIC, NORMAL, DETAILED)")

// At least until we can use a generic, i.e.: OpenCensus, metrics exporter
// we default to Prometheus at port 8888, if not otherwise specified.
metricsPortPtr = flags.Uint(
metricsPortCfg,
8888,
"Port exposing collector telemetry.")

metricsPrefixPtr = flags.String(
metricsPrefixCfg,
"otelcol",
"Prefix to the metrics generated by the collector.")
}

func (tel *appTelemetry) init(asyncErrorChannel chan<- error, ballastSizeBytes uint64, logger *zap.Logger) error {
Expand Down Expand Up @@ -84,7 +99,7 @@ func (tel *appTelemetry) init(asyncErrorChannel chan<- error, ballastSizeBytes u

// Until we can use a generic metrics exporter, default to Prometheus.
opts := prometheus.Options{
Namespace: "oc_collector",
Namespace: *metricsPrefixPtr,
}
pe, err := prometheus.NewExporter(opts)
if err != nil {
Expand Down