Skip to content

Commit

Permalink
Add options, and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
MadVikingGod authored Aug 2, 2022
1 parent 8992526 commit 961c66b
Show file tree
Hide file tree
Showing 6 changed files with 420 additions and 57 deletions.
78 changes: 78 additions & 0 deletions exporters/stdout/stdoutmetric/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// 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 stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"

import (
"io"
"os"
)

var (
defaultWriter = os.Stdout
defaultPrettyPrint = false
)

// config contains options for the STDOUT exporter.
type config struct {
// Writer is the destination. If not set, os.Stdout is used.
Writer io.Writer

// PrettyPrint will encode the output into readable JSON. Default is
// false.
PrettyPrint bool
}

// newConfig creates a validated Config configured with options.
func newConfig(options ...Option) (config, error) {
cfg := config{
Writer: defaultWriter,
PrettyPrint: defaultPrettyPrint,
}
for _, opt := range options {
cfg = opt.apply(cfg)
}
return cfg, nil
}

// Option sets the value of an option for a Config.
type Option interface {
apply(config) config
}

// WithWriter sets the export stream destination.
func WithWriter(w io.Writer) Option {
return writerOption{w}
}

type writerOption struct {
W io.Writer
}

func (o writerOption) apply(cfg config) config {
cfg.Writer = o.W
return cfg
}

// WithPrettyPrint sets the export stream format to use JSON.
func WithPrettyPrint() Option {
return prettyPrintOption(true)
}

type prettyPrintOption bool

func (o prettyPrintOption) apply(cfg config) config {
cfg.PrettyPrint = bool(o)
return cfg
}
275 changes: 275 additions & 0 deletions exporters/stdout/stdoutmetric/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
// 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.

//go:build go1.18
// +build go1.18

package stdoutmetric

import (
"bytes"
"context"
"testing"
"time"

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

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/unit"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
"go.opentelemetry.io/otel/sdk/resource"
)

func TestExporterExport(t *testing.T) {
now := time.Unix(1558033527, 0)

exampleData := metricdata.ResourceMetrics{
Resource: resource.NewWithAttributes("example", attribute.String("resource-foo", "bar")),
ScopeMetrics: []metricdata.ScopeMetrics{
{
Scope: instrumentation.Scope{
Name: "scope1",
Version: "v1",
SchemaURL: "anotherExample",
},
Metrics: []metricdata.Metrics{
{
Name: "gauge",
Description: "Gauge's description",
Unit: unit.Dimensionless,
Data: metricdata.Gauge[int64]{
DataPoints: []metricdata.DataPoint[int64]{
{
Attributes: attribute.NewSet(),
StartTime: now.Add(-1 * time.Minute),
Time: now,
Value: 3,
},
},
},
},
{
Name: "sum",
Description: "Sum's description",
Unit: unit.Bytes,
Data: metricdata.Sum[float64]{
Temporality: metricdata.DeltaTemporality,
IsMonotonic: false,
DataPoints: []metricdata.DataPoint[float64]{
{
Attributes: attribute.NewSet(),
StartTime: now.Add(-1 * time.Minute),
Time: now,
Value: 3,
},
{
Attributes: attribute.NewSet(attribute.String("foo", "bar")),
StartTime: now.Add(-1 * time.Minute),
Time: now,
Value: 3,
},
},
},
},
{
Name: "histogram",
Description: "Histogram's description",
Unit: unit.Bytes,
Data: metricdata.Histogram{
Temporality: metricdata.CumulativeTemporality,
DataPoints: []metricdata.HistogramDataPoint{
{
Attributes: attribute.NewSet(),
StartTime: now.Add(-1 * time.Minute),
Time: now,
Count: 6,
Bounds: []float64{
1.0,
5.0,
},
BucketCounts: []uint64{
1, 2, 3,
},
Min: nil,
Max: nil,
Sum: 200,
},
},
},
},
},
},
{
Scope: instrumentation.Scope{
Name: "Scope2",
Version: "v2",
SchemaURL: "aDifferentExample",
},
Metrics: []metricdata.Metrics{
{
Name: "gauge",
Description: "Gauge's description",
Unit: unit.Dimensionless,
Data: metricdata.Gauge[int64]{
DataPoints: []metricdata.DataPoint[int64]{
{
Attributes: attribute.NewSet(),
StartTime: now.Add(-1 * time.Minute),
Time: now,
Value: 3,
},
},
},
},
},
},
},
}

buf := &bytes.Buffer{}
exp, err := New(
WithWriter(buf),
WithPrettyPrint(),
)
require.NoError(t, err)

err = exp.Export(context.Background(), exampleData)

require.NoError(t, err)
assert.Equal(t, expectedOutput, buf.String())
}

var expectedOutput = `{
"Resource": [
{
"Key": "resource-foo",
"Value": {
"Type": "STRING",
"Value": "bar"
}
}
],
"ScopeMetrics": [
{
"Scope": {
"Name": "scope1",
"Version": "v1",
"SchemaURL": "anotherExample"
},
"Metrics": [
{
"Name": "gauge",
"Description": "Gauge's description",
"Unit": "1",
"Data": {
"DataPoints": [
{
"Attributes": [],
"StartTime": "2019-05-16T19:04:27Z",
"Time": "2019-05-16T19:05:27Z",
"Value": 3
}
]
}
},
{
"Name": "sum",
"Description": "Sum's description",
"Unit": "By",
"Data": {
"DataPoints": [
{
"Attributes": [],
"StartTime": "2019-05-16T19:04:27Z",
"Time": "2019-05-16T19:05:27Z",
"Value": 3
},
{
"Attributes": [
{
"Key": "foo",
"Value": {
"Type": "STRING",
"Value": "bar"
}
}
],
"StartTime": "2019-05-16T19:04:27Z",
"Time": "2019-05-16T19:05:27Z",
"Value": 3
}
],
"Temporality": 2,
"IsMonotonic": false
}
},
{
"Name": "histogram",
"Description": "Histogram's description",
"Unit": "By",
"Data": {
"DataPoints": [
{
"Attributes": [],
"StartTime": "2019-05-16T19:04:27Z",
"Time": "2019-05-16T19:05:27Z",
"Count": 6,
"Bounds": [
1,
5
],
"BucketCounts": [
1,
2,
3
],
"Min": null,
"Max": null,
"Sum": 200
}
],
"Temporality": 1
}
}
]
},
{
"Scope": {
"Name": "Scope2",
"Version": "v2",
"SchemaURL": "aDifferentExample"
},
"Metrics": [
{
"Name": "gauge",
"Description": "Gauge's description",
"Unit": "1",
"Data": {
"DataPoints": [
{
"Attributes": [],
"StartTime": "2019-05-16T19:04:27Z",
"Time": "2019-05-16T19:05:27Z",
"Value": 3
}
]
}
}
]
}
]
}
`
16 changes: 12 additions & 4 deletions exporters/stdout/stdoutmetric/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,30 @@ package stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdout
import (
"context"
"encoding/json"
"os"
"sync"

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

func New() (*Exporter, error) {
// New creates an Exporter with the passed options.
func New(options ...Option) (*Exporter, error) {
cfg, err := newConfig(options...)
if err != nil {
return nil, err
}

enc := json.NewEncoder(cfg.Writer)
if cfg.PrettyPrint {
enc.SetIndent("", "\t")
}

enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", "\t")
return &Exporter{
encoder: enc,
}, nil
}

// Exporter is an implementation of metric.Exporter that writes ResourceMetrics to stdout.
type Exporter struct {
encoder *json.Encoder
encoderMu sync.Mutex
Expand Down
Loading

0 comments on commit 961c66b

Please sign in to comment.