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 Prometheus metrics reader factory and config #3049

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions pkg/prometheus/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2021 The Jaeger 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 config

import "time"

// Configuration describes the options to customize the storage behavior
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Configuration describes the options to customize the storage behavior
// Configuration describes the options to customize the storage behavior.

type Configuration struct {
HostPort string `validate:"nonzero" mapstructure:"server"`
ConnectTimeout time.Duration `validate:"nonzero" mapstructure:"timeout"`
}
15 changes: 15 additions & 0 deletions pkg/prometheus/config/empty_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2021 The Jaeger 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 config
101 changes: 101 additions & 0 deletions plugin/metrics/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) 2021 The Jaeger 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 metrics

import (
"flag"
"fmt"

"github.com/spf13/viper"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/plugin"
"github.com/jaegertracing/jaeger/plugin/metrics/prometheus"
"github.com/jaegertracing/jaeger/storage"
"github.com/jaegertracing/jaeger/storage/metricsstore"
)

const (
prometheusStorageType = "prometheus"
)

// AllStorageTypes defines all available storage backends.
var AllStorageTypes = []string{prometheusStorageType}

// Factory implements storage.Factory interface as a meta-factory for storage components.
albertteoh marked this conversation as resolved.
Show resolved Hide resolved
type Factory struct {
FactoryConfig
factories map[string]storage.MetricsFactory
}

// NewFactory creates the meta-factory.
func NewFactory(config FactoryConfig) (*Factory, error) {
f := &Factory{FactoryConfig: config}
uniqueTypes := map[string]struct{}{
f.MetricsStorageType: {},
}
f.factories = make(map[string]storage.MetricsFactory)
for t := range uniqueTypes {
ff, err := f.getFactoryOfType(t)
if err != nil {
return nil, err
}
f.factories[t] = ff
}
return f, nil
}

func (f *Factory) getFactoryOfType(factoryType string) (storage.MetricsFactory, error) {
switch factoryType {
case prometheusStorageType:
return prometheus.NewFactory(), nil
}
return nil, fmt.Errorf("unknown metrics type %s. Valid types are %v", factoryType, AllStorageTypes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nil, fmt.Errorf("unknown metrics type %s. Valid types are %v", factoryType, AllStorageTypes)
return nil, fmt.Errorf("unknown metrics type %q. Valid types are %v", factoryType, AllStorageTypes)

}

// Initialize implements storage.MetricsFactory.
func (f *Factory) Initialize(logger *zap.Logger) error {
for _, factory := range f.factories {
factory.Initialize(logger)
}
return nil
}

// CreateMetricsReader implements storage.MetricsFactory.
func (f *Factory) CreateMetricsReader() (metricsstore.Reader, error) {
factory, ok := f.factories[f.MetricsStorageType]
if !ok {
return nil, fmt.Errorf("no %s backend registered for metrics store", f.MetricsStorageType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nil, fmt.Errorf("no %s backend registered for metrics store", f.MetricsStorageType)
return nil, fmt.Errorf("no %q backend registered for metrics store", f.MetricsStorageType)

}
return factory.CreateMetricsReader()
}

// AddFlags implements plugin.Configurable.
func (f *Factory) AddFlags(flagSet *flag.FlagSet) {
for _, factory := range f.factories {
if conf, ok := factory.(plugin.Configurable); ok {
conf.AddFlags(flagSet)
}
}
}

// InitFromViper implements plugin.Configurable.
func (f *Factory) InitFromViper(v *viper.Viper) {
for _, factory := range f.factories {
if conf, ok := factory.(plugin.Configurable); ok {
conf.InitFromViper(v)
}
}
}
36 changes: 36 additions & 0 deletions plugin/metrics/factory_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) 2021 The Jaeger 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 metrics

import (
"os"
)

const (
// StorageTypeEnvVar is the name of the env var that defines the type of backend used for metrics storage.
StorageTypeEnvVar = "METRICS_STORAGE_TYPE"
)

// FactoryConfig tells the Factory which types of backends it needs to create for different storage types.
type FactoryConfig struct {
MetricsStorageType string
}

// FactoryConfigFromEnv reads the desired types of storage backends from METRICS_STORAGE_TYPE.
func FactoryConfigFromEnv() FactoryConfig {
return FactoryConfig{
MetricsStorageType: os.Getenv(StorageTypeEnvVar),
}
}
42 changes: 42 additions & 0 deletions plugin/metrics/factory_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2021 The Jaeger 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 metrics

import (
"os"
"testing"

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

func clearEnv(t *testing.T) {
err := os.Setenv(StorageTypeEnvVar, "")
require.NoError(t, err)
}

func TestFactoryConfigFromEnv(t *testing.T) {
clearEnv(t)
defer clearEnv(t)

fc := FactoryConfigFromEnv()
assert.Empty(t, fc.MetricsStorageType)

err := os.Setenv(StorageTypeEnvVar, prometheusStorageType)
require.NoError(t, err)

fc = FactoryConfigFromEnv()
assert.Equal(t, prometheusStorageType, fc.MetricsStorageType)
}
108 changes: 108 additions & 0 deletions plugin/metrics/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) 2021 The Jaeger 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 metrics

import (
"flag"
"testing"

"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/storage"
"github.com/jaegertracing/jaeger/storage/mocks"
)

var _ storage.MetricsFactory = new(Factory)
albertteoh marked this conversation as resolved.
Show resolved Hide resolved

func defaultCfg() FactoryConfig {
return FactoryConfig{
MetricsStorageType: prometheusStorageType,
}
}

func TestNewFactory(t *testing.T) {
f, err := NewFactory(defaultCfg())
require.NoError(t, err)
assert.NotEmpty(t, f.factories)
assert.NotEmpty(t, f.factories[prometheusStorageType])
assert.Equal(t, prometheusStorageType, f.MetricsStorageType)
}

func TestUnsupportedMetricsStorageType(t *testing.T) {
f, err := NewFactory(FactoryConfig{MetricsStorageType: "foo"})
require.Error(t, err)
assert.Nil(t, f)
assert.EqualError(t, err, "unknown metrics type foo. Valid types are [prometheus]")
}

func TestCreateMetricsReader(t *testing.T) {
f, err := NewFactory(defaultCfg())
require.NoError(t, err)
require.NotNil(t, f)

require.NoError(t, f.Initialize(zap.NewNop()))

reader, err := f.CreateMetricsReader()
require.NoError(t, err)
require.NotNil(t, reader)

f.MetricsStorageType = "foo"
reader, err = f.CreateMetricsReader()
require.Error(t, err)
require.Nil(t, reader)

assert.EqualError(t, err, "no foo backend registered for metrics store")
}

type configurable struct {
mocks.MetricsFactory
flagSet *flag.FlagSet
viper *viper.Viper
}

// AddFlags implements plugin.Configurable.
func (f *configurable) AddFlags(flagSet *flag.FlagSet) {
f.flagSet = flagSet
}

// InitFromViper implements plugin.Configurable.
func (f *configurable) InitFromViper(v *viper.Viper) {
f.viper = v
}

func TestConfigurable(t *testing.T) {
clearEnv(t)
defer clearEnv(t)

f, err := NewFactory(defaultCfg())
require.NoError(t, err)
assert.NotEmpty(t, f.factories)
assert.NotEmpty(t, f.factories[prometheusStorageType])

mock := new(configurable)
f.factories[prometheusStorageType] = mock

fs := new(flag.FlagSet)
v := viper.New()

f.AddFlags(fs)
f.InitFromViper(v)

assert.Equal(t, fs, mock.flagSet)
assert.Equal(t, v, mock.viper)
}
59 changes: 59 additions & 0 deletions plugin/metrics/prometheus/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2021 The Jaeger 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 prometheus

import (
"flag"

"github.com/spf13/viper"
"go.uber.org/zap"

prometheusstore "github.com/jaegertracing/jaeger/plugin/metrics/prometheus/metricsstore"
"github.com/jaegertracing/jaeger/storage/metricsstore"
)

// Factory implements storage.Factory and creates storage components backed by memory store.
type Factory struct {
options *Options
logger *zap.Logger
}

// NewFactory creates a new Factory.
func NewFactory() *Factory {
return &Factory{
options: NewOptions("prometheus"),
}
}

// AddFlags implements plugin.Configurable.
func (f *Factory) AddFlags(flagSet *flag.FlagSet) {
f.options.AddFlags(flagSet)
}

// InitFromViper implements plugin.Configurable.
func (f *Factory) InitFromViper(v *viper.Viper) {
f.options.InitFromViper(v)
}

// Initialize implements storage.MetricsFactory.
func (f *Factory) Initialize(logger *zap.Logger) error {
f.logger = logger
return nil
}

// CreateMetricsReader implements storage.MetricsFactory.
func (f *Factory) CreateMetricsReader() (metricsstore.Reader, error) {
return prometheusstore.NewMetricsReader(f.logger, f.options.Primary.HostPort, f.options.Primary.ConnectTimeout)
}
Loading