Skip to content

Commit

Permalink
Define the reader interface, and create a manual reader (#2885)
Browse files Browse the repository at this point in the history
* Add the manual reader to the sdk.

* Incoperate feedback from PR.

* additional PR comments

* Fix lint

* Fixes for PR.

* Unexport ManualReader
fix a few comments
  • Loading branch information
MadVikingGod authored May 13, 2022
1 parent ac0b13f commit a3253d0
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 1 deletion.
22 changes: 22 additions & 0 deletions sdk/metric/export/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright The 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.

// TODO: NOTE this is a temporary space, it may be moved following the
// discussion of #2813, or #2841

package export // import "go.opentelemetry.io/otel/sdk/metric/export"

// Metrics is the result of a single collection.
type Metrics struct { /* TODO: implement #2889 */
}
5 changes: 4 additions & 1 deletion sdk/metric/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ module go.opentelemetry.io/otel/sdk/metric

go 1.16

require go.opentelemetry.io/otel/metric v0.0.0-00010101000000-000000000000
require (
github.com/stretchr/testify v1.7.1
go.opentelemetry.io/otel/metric v0.0.0-00010101000000-000000000000
)

replace go.opentelemetry.io/otel => ../..

Expand Down
1 change: 1 addition & 0 deletions sdk/metric/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
85 changes: 85 additions & 0 deletions sdk/metric/manual_reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright The 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 metric // import "go.opentelemetry.io/otel/sdk/metric"

import (
"context"
"fmt"
"sync"

"go.opentelemetry.io/otel/sdk/metric/export"
)

// manualReader is a a simple Reader that allows an application to
// read metrics on demand.
type manualReader struct {
lock sync.Mutex
producer producer
shutdown bool
}

// Compile time check the manualReader implements Reader.
var _ Reader = &manualReader{}

// NewManualReader returns a Reader which is directly called to collect metrics.
func NewManualReader() Reader {
return &manualReader{}
}

// register stores the Producer which enables the caller to read
// metrics on demand.
func (mr *manualReader) register(p producer) {
mr.lock.Lock()
defer mr.lock.Unlock()
mr.producer = p
}

// ForceFlush is a no-op, it always returns nil.
func (mr *manualReader) ForceFlush(context.Context) error {
return nil
}

// Shutdown closes any connections and frees any resources used by the reader.
func (mr *manualReader) Shutdown(context.Context) error {
mr.lock.Lock()
defer mr.lock.Unlock()
if mr.shutdown {
return ErrReaderShutdown
}
mr.shutdown = true
return nil
}

// Collect gathers all metrics from the SDK, calling any callbacks necessary.
// Collect will return an error if called after shutdown.
func (mr *manualReader) Collect(ctx context.Context) (export.Metrics, error) {
mr.lock.Lock()
defer mr.lock.Unlock()
if mr.producer == nil {
return export.Metrics{}, ErrReaderNotRegistered
}
if mr.shutdown {
return export.Metrics{}, ErrReaderShutdown
}
return mr.producer.produce(ctx)
}

// ErrReaderNotRegistered is returned if Collect or Shutdown are called before
// the reader is registered with a MeterProvider
var ErrReaderNotRegistered = fmt.Errorf("reader is not registered")

// ErrReaderShutdown is returned if Collect or Shutdown are called after a
// reader has been Shutdown once.
var ErrReaderShutdown = fmt.Errorf("reader is shutdown")
74 changes: 74 additions & 0 deletions sdk/metric/manual_reader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright The 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 metric // import "go.opentelemetry.io/otel/sdk/metric/reader"

import (
"context"
"testing"

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

"go.opentelemetry.io/otel/sdk/metric/export"
)

func TestManualReaderNotRegistered(t *testing.T) {
rdr := &manualReader{}

_, err := rdr.Collect(context.Background())
require.ErrorIs(t, err, ErrReaderNotRegistered)
}

type testProducer struct{}

var testMetrics = export.Metrics{
// TODO: test with actual data.
}

func (p testProducer) produce(context.Context) (export.Metrics, error) {
return testMetrics, nil
}

func TestManualReaderProducer(t *testing.T) {
rdr := &manualReader{}
rdr.register(testProducer{})

m, err := rdr.Collect(context.Background())
assert.NoError(t, err)
assert.Equal(t, testMetrics, m)
}

func TestManualReaderCollectAfterShutdown(t *testing.T) {
rdr := &manualReader{}
rdr.register(testProducer{})
err := rdr.Shutdown(context.Background())
require.NoError(t, err)

m, err := rdr.Collect(context.Background())
assert.ErrorIs(t, err, ErrReaderShutdown)
assert.Equal(t, export.Metrics{}, m)
}

func TestManualReaderShutdown(t *testing.T) {
rdr := &manualReader{}
rdr.register(testProducer{})

err := rdr.Shutdown(context.Background())
require.NoError(t, err)

err = rdr.Shutdown(context.Background())
assert.ErrorIs(t, err, ErrReaderShutdown)

}
74 changes: 74 additions & 0 deletions sdk/metric/reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright The 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 metric // import "go.opentelemetry.io/otel/sdk/metric"

import (
"context"

"go.opentelemetry.io/otel/sdk/metric/export"
)

// Reader is the interface used between the SDK and an
// exporter. Control flow is bi-directional through the
// Reader, since the SDK initiates ForceFlush and Shutdown
// while the initiates collection. The Register() method here
// informs the Reader that it can begin reading, signaling the
// start of bi-directional control flow.
//
// Typically, push-based exporters that are periodic will
// implement PeroidicExporter themselves and construct a
// PeriodicReader to satisfy this interface.
//
// Pull-based exporters will typically implement Register
// themselves, since they read on demand.
type Reader interface {
// register registers a Reader with a MeterProvider.
// The producer argument allows the Reader to signal the sdk to collect
// and send aggregated metric measurements.
register(producer)

// Collect gathers all metrics from the SDK, calling any callbacks necessary.
// TODO: How does this impact Push exporters?
// Collect will return an error if called after shutdown.
Collect(context.Context) (export.Metrics, error)

// ForceFlush flushes all metric measurements held in an export pipeline.
//
// This deadline or cancellation of the passed context are honored. An appropriate
// error will be returned in these situations. There is no guaranteed that all
// telemetry be flushed or all resources have been released in these
// situations.
ForceFlush(context.Context) error

// Shutdown flushes all metric measurements held in an export pipeline and releases any
// held computational resources.
//
// This deadline or cancellation of the passed context are honored. An appropriate
// error will be returned in these situations. There is no guaranteed that all
// telemetry be flushed or all resources have been released in these
// situations.
//
// After Shutdown is called, calls to Collect will perform no operation and instead will return
// an error indicating the shutdown state.
Shutdown(context.Context) error
}

// producer produces metrics for a Reader.
type producer interface {
// produce returns aggregated metrics from a single collection.
//
// This method is safe to call concurrently.
produce(context.Context) (export.Metrics, error)
}

0 comments on commit a3253d0

Please sign in to comment.