Skip to content

Commit

Permalink
Implement Prometheus factory and config V2 (#89)
Browse files Browse the repository at this point in the history
Github issue: open-telemetry/opentelemetry-collector#38

Testing done:

- make && make otelsvc
- Run otelsvc with the following config and make sure Prometheus can scrape :

receivers:
  opencensus:
    port: 55678

  prometheus:
    config:
      scrape_configs:
        - job_name: 'demo'
          scrape_interval: 5s

zpages:
  port: 55679

exporters:
  prometheus:
    endpoint: "127.0.0.1:8889"

pipelines:
  metrics:
    receivers: [prometheus]
    exporters: [prometheus]
  • Loading branch information
tigrannajaryan authored Jul 3, 2019
1 parent c50224d commit 886e62f
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 46 deletions.
2 changes: 1 addition & 1 deletion cmd/occollector/app/builder/receivers_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func (rb *ReceiversBuilder) attachReceiverToPipelines(
dataType.GetString(),
dataType.GetString())
}
return fmt.Errorf("cannot create receiver %s", config.Name())
return fmt.Errorf("cannot create receiver %s: %s", config.Name(), err.Error())
}

rb.logger.Info("Receiver is enabled.",
Expand Down
35 changes: 35 additions & 0 deletions exporter/prometheusexporter/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package prometheusexporter

import (
prometheus_golang "github.com/prometheus/client_golang/prometheus"

"github.com/open-telemetry/opentelemetry-service/models"
)

// ConfigV2 defines configuration for Prometheus exporter.
type ConfigV2 struct {
models.ExporterSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct.

// The address on which the Prometheus scrape handler will be run on.
Endpoint string `mapstructure:"endpoint"`

// Namespace if set, exports metrics under the provided value.
Namespace string `mapstructure:"namespace"`

// ConstLabels are values that are applied for every exported metric.
ConstLabels prometheus_golang.Labels `mapstructure:"const_labels"`
}
57 changes: 57 additions & 0 deletions exporter/prometheusexporter/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package prometheusexporter

import (
"path"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/open-telemetry/opentelemetry-service/configv2"
"github.com/open-telemetry/opentelemetry-service/factories"
"github.com/open-telemetry/opentelemetry-service/models"
)

var _ = configv2.RegisterTestFactories()

func TestLoadConfig(t *testing.T) {
factory := factories.GetExporterFactory(typeStr)

config, err := configv2.LoadConfigFile(t, path.Join(".", "testdata", "config.yaml"))

require.NoError(t, err)
require.NotNil(t, config)

e0 := config.Exporters["prometheus"]
assert.Equal(t, e0, factory.CreateDefaultConfig())

e1 := config.Exporters["prometheus/2"]
assert.Equal(t, e1,
&ConfigV2{
ExporterSettings: models.ExporterSettings{
NameVal: "prometheus/2",
TypeVal: "prometheus",
Enabled: true,
},
Endpoint: "1.2.3.4:1234",
Namespace: "test-space",
ConstLabels: map[string]string{
"label1": "value1",
"another label": "spaced value",
},
})
}
98 changes: 98 additions & 0 deletions exporter/prometheusexporter/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package prometheusexporter

import (
"net"
"net/http"
"strings"

"go.uber.org/zap"

"github.com/open-telemetry/opentelemetry-service/consumer"
"github.com/open-telemetry/opentelemetry-service/factories"
"github.com/open-telemetry/opentelemetry-service/models"
"github.com/orijtech/prometheus-go-metrics-exporter"
)

var _ = factories.RegisterExporterFactory(&exporterFactory{})

const (
// The value of "type" key in configuration.
typeStr = "prometheus"
)

// exporterFactory is the factory for Prometheus exporter.
type exporterFactory struct {
}

// Type gets the type of the Exporter config created by this factory.
func (f *exporterFactory) Type() string {
return typeStr
}

// CreateDefaultConfig creates the default configuration for exporter.
func (f *exporterFactory) CreateDefaultConfig() models.Exporter {
return &ConfigV2{
ExporterSettings: models.ExporterSettings{
TypeVal: typeStr,
NameVal: typeStr,
},
ConstLabels: map[string]string{},
}
}

// CreateTraceExporter creates a trace exporter based on this config.
func (f *exporterFactory) CreateTraceExporter(logger *zap.Logger, config models.Exporter) (consumer.TraceConsumer, factories.StopFunc, error) {
return nil, nil, models.ErrDataTypeIsNotSupported
}

// CreateMetricsExporter creates a metrics exporter based on this config.
func (f *exporterFactory) CreateMetricsExporter(logger *zap.Logger, cfg models.Exporter) (consumer.MetricsConsumer, factories.StopFunc, error) {
pcfg := cfg.(*ConfigV2)

addr := strings.TrimSpace(pcfg.Endpoint)
if addr == "" {
return nil, nil, errBlankPrometheusAddress
}

opts := prometheus.Options{
Namespace: pcfg.Namespace,
ConstLabels: pcfg.ConstLabels,
}
pe, err := prometheus.New(opts)
if err != nil {
return nil, nil, err
}

ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, nil, err
}

// The Prometheus metrics exporter has to run on the provided address
// as a server that'll be scraped by Prometheus.
mux := http.NewServeMux()
mux.Handle("/metrics", pe)

srv := &http.Server{Handler: mux}
go func() {
_ = srv.Serve(ln)
}()

pexp := &prometheusExporter{exporter: pe}

return pexp, ln.Close, nil
}
79 changes: 79 additions & 0 deletions exporter/prometheusexporter/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package prometheusexporter

import (
"testing"

"github.com/open-telemetry/opentelemetry-service/models"

"go.uber.org/zap"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/open-telemetry/opentelemetry-service/factories"
)

func TestCreateDefaultConfig(t *testing.T) {
factory := factories.GetExporterFactory(typeStr)
require.NotNil(t, factory)

cfg := factory.CreateDefaultConfig()
assert.NotNil(t, cfg, "failed to create default config")
}

func TestCreateTraceExporter(t *testing.T) {
factory := factories.GetExporterFactory(typeStr)
cfg := factory.CreateDefaultConfig()

_, _, err := factory.CreateTraceExporter(zap.NewNop(), cfg)
assert.Error(t, err, models.ErrDataTypeIsNotSupported)
}

func TestCreateMetricsExporter(t *testing.T) {
const defaultTestEndPoint = "127.0.0.1:55678"
tests := []struct {
name string
config ConfigV2
mustFail bool
}{
{
name: "NoEndpoint",
config: ConfigV2{
Endpoint: "",
},
mustFail: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
factory := factories.GetExporterFactory(typeStr)
consumer, stopFunc, err := factory.CreateMetricsExporter(zap.NewNop(), &tt.config)

if tt.mustFail {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
assert.NotNil(t, consumer)
assert.NotNil(t, stopFunc)

err = stopFunc()
assert.Nil(t, err)
}
})
}
}
Loading

0 comments on commit 886e62f

Please sign in to comment.