You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-11-29 23:07:45 +02:00
New stdoutmetric encoder with ignore timestamp (#3828)
* new stdoutmetric encoder with ignore timestamp Signed-off-by: Peter Liu <lpfvip2008@gmail.com> * refactor to avoid using a dedicated encoder Signed-off-by: Peter Liu <lpfvip2008@gmail.com> * remove useless encoder code Signed-off-by: Peter Liu <lpfvip2008@gmail.com> * dont't change the original data Signed-off-by: Peter Liu <lpfvip2008@gmail.com> * Update exporters/stdout/stdoutmetric/exporter.go Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com> * complete test data and add changelog entry Signed-off-by: Peter Liu <lpfvip2008@gmail.com> * Move changelog entry to unreleased --------- Signed-off-by: Peter Liu <lpfvip2008@gmail.com> Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com> Co-authored-by: Tyler Yahn <codingalias@gmail.com>
This commit is contained in:
@@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- The `WithoutTimestamps` option to `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` to sets all timestamps to zero. (#3828)
|
||||
|
||||
### Changed
|
||||
|
||||
- Avoid creating new objects on all calls to `WithDeferredSetup` and `SkipContextSetup` in OpenTracing bridge. (#3833)
|
||||
|
||||
@@ -27,6 +27,7 @@ type config struct {
|
||||
encoder *encoderHolder
|
||||
temporalitySelector metric.TemporalitySelector
|
||||
aggregationSelector metric.AggregationSelector
|
||||
redactTimestamps bool
|
||||
}
|
||||
|
||||
// newConfig creates a validated config configured with options.
|
||||
@@ -125,3 +126,11 @@ func (t aggregationSelectorOption) apply(c config) config {
|
||||
c.aggregationSelector = t.selector
|
||||
return c
|
||||
}
|
||||
|
||||
// WithoutTimestamps sets all timestamps to zero in the output stream.
|
||||
func WithoutTimestamps() Option {
|
||||
return optionFunc(func(c config) config {
|
||||
c.redactTimestamps = true
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
|
||||
package stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Encoder encodes and outputs OpenTelemetry metric data-types as human
|
||||
// readable text.
|
||||
|
||||
@@ -60,6 +60,23 @@ var (
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "system.cpu.time",
|
||||
Description: "Accumulated CPU time spent",
|
||||
Unit: "s",
|
||||
Data: metricdata.Sum[float64]{
|
||||
IsMonotonic: true,
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[float64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(attribute.String("state", "user")),
|
||||
StartTime: now,
|
||||
Time: now.Add(1 * time.Second),
|
||||
Value: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "latency",
|
||||
Description: "Time spend processing received requests",
|
||||
@@ -79,6 +96,20 @@ var (
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "system.memory.usage",
|
||||
Description: "Memory usage",
|
||||
Unit: "By",
|
||||
Data: metricdata.Gauge[int64]{
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(attribute.String("state", "used")),
|
||||
Time: now.Add(1 * time.Second),
|
||||
Value: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "temperature",
|
||||
Description: "CPU global temperature",
|
||||
@@ -103,7 +134,11 @@ func Example() {
|
||||
// Print with a JSON encoder that indents with two spaces.
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
exp, err := stdoutmetric.New(stdoutmetric.WithEncoder(enc))
|
||||
|
||||
exp, err := stdoutmetric.New(
|
||||
stdoutmetric.WithEncoder(enc),
|
||||
stdoutmetric.WithoutTimestamps(),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -116,10 +151,180 @@ func Example() {
|
||||
|
||||
ctx := context.Background()
|
||||
// This is where the sdk would be used to create a Meter and from that
|
||||
// instruments that would make measurments of your code. To simulate that
|
||||
// instruments that would make measurements of your code. To simulate that
|
||||
// behavior, call export directly with mocked data.
|
||||
_ = exp.Export(ctx, mockData)
|
||||
|
||||
// Ensure the periodic reader is cleaned up by shutting down the sdk.
|
||||
_ = sdk.Shutdown(ctx)
|
||||
|
||||
//Output:
|
||||
// {
|
||||
// "Resource": [
|
||||
// {
|
||||
// "Key": "service.name",
|
||||
// "Value": {
|
||||
// "Type": "STRING",
|
||||
// "Value": "stdoutmetric-example"
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// "ScopeMetrics": [
|
||||
// {
|
||||
// "Scope": {
|
||||
// "Name": "example",
|
||||
// "Version": "v0.0.1",
|
||||
// "SchemaURL": ""
|
||||
// },
|
||||
// "Metrics": [
|
||||
// {
|
||||
// "Name": "requests",
|
||||
// "Description": "Number of requests received",
|
||||
// "Unit": "1",
|
||||
// "Data": {
|
||||
// "DataPoints": [
|
||||
// {
|
||||
// "Attributes": [
|
||||
// {
|
||||
// "Key": "server",
|
||||
// "Value": {
|
||||
// "Type": "STRING",
|
||||
// "Value": "central"
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// "StartTime": "0001-01-01T00:00:00Z",
|
||||
// "Time": "0001-01-01T00:00:00Z",
|
||||
// "Value": 5
|
||||
// }
|
||||
// ],
|
||||
// "Temporality": "DeltaTemporality",
|
||||
// "IsMonotonic": true
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "Name": "system.cpu.time",
|
||||
// "Description": "Accumulated CPU time spent",
|
||||
// "Unit": "s",
|
||||
// "Data": {
|
||||
// "DataPoints": [
|
||||
// {
|
||||
// "Attributes": [
|
||||
// {
|
||||
// "Key": "state",
|
||||
// "Value": {
|
||||
// "Type": "STRING",
|
||||
// "Value": "user"
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// "StartTime": "0001-01-01T00:00:00Z",
|
||||
// "Time": "0001-01-01T00:00:00Z",
|
||||
// "Value": 0.5
|
||||
// }
|
||||
// ],
|
||||
// "Temporality": "CumulativeTemporality",
|
||||
// "IsMonotonic": true
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "Name": "latency",
|
||||
// "Description": "Time spend processing received requests",
|
||||
// "Unit": "ms",
|
||||
// "Data": {
|
||||
// "DataPoints": [
|
||||
// {
|
||||
// "Attributes": [
|
||||
// {
|
||||
// "Key": "server",
|
||||
// "Value": {
|
||||
// "Type": "STRING",
|
||||
// "Value": "central"
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// "StartTime": "0001-01-01T00:00:00Z",
|
||||
// "Time": "0001-01-01T00:00:00Z",
|
||||
// "Count": 10,
|
||||
// "Bounds": [
|
||||
// 1,
|
||||
// 5,
|
||||
// 10
|
||||
// ],
|
||||
// "BucketCounts": [
|
||||
// 1,
|
||||
// 3,
|
||||
// 6,
|
||||
// 0
|
||||
// ],
|
||||
// "Min": {},
|
||||
// "Max": {},
|
||||
// "Sum": 57
|
||||
// }
|
||||
// ],
|
||||
// "Temporality": "DeltaTemporality"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "Name": "system.memory.usage",
|
||||
// "Description": "Memory usage",
|
||||
// "Unit": "By",
|
||||
// "Data": {
|
||||
// "DataPoints": [
|
||||
// {
|
||||
// "Attributes": [
|
||||
// {
|
||||
// "Key": "state",
|
||||
// "Value": {
|
||||
// "Type": "STRING",
|
||||
// "Value": "used"
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// "StartTime": "0001-01-01T00:00:00Z",
|
||||
// "Time": "0001-01-01T00:00:00Z",
|
||||
// "Value": 100
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "Name": "temperature",
|
||||
// "Description": "CPU global temperature",
|
||||
// "Unit": "cel(1 K)",
|
||||
// "Data": {
|
||||
// "DataPoints": [
|
||||
// {
|
||||
// "Attributes": [
|
||||
// {
|
||||
// "Key": "server",
|
||||
// "Value": {
|
||||
// "Type": "STRING",
|
||||
// "Value": "central"
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// "StartTime": "0001-01-01T00:00:00Z",
|
||||
// "Time": "0001-01-01T00:00:00Z",
|
||||
// "Value": 32.4
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// {
|
||||
// "Resource": [
|
||||
// {
|
||||
// "Key": "service.name",
|
||||
// "Value": {
|
||||
// "Type": "STRING",
|
||||
// "Value": "stdoutmetric-example"
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// "ScopeMetrics": []
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -16,9 +16,12 @@ package stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdout
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregation"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
@@ -32,6 +35,8 @@ type exporter struct {
|
||||
|
||||
temporalitySelector metric.TemporalitySelector
|
||||
aggregationSelector metric.AggregationSelector
|
||||
|
||||
redactTimestamps bool
|
||||
}
|
||||
|
||||
// New returns a configured metric exporter.
|
||||
@@ -43,6 +48,7 @@ func New(options ...Option) (metric.Exporter, error) {
|
||||
exp := &exporter{
|
||||
temporalitySelector: cfg.temporalitySelector,
|
||||
aggregationSelector: cfg.aggregationSelector,
|
||||
redactTimestamps: cfg.redactTimestamps,
|
||||
}
|
||||
exp.encVal.Store(*cfg.encoder)
|
||||
return exp, nil
|
||||
@@ -64,7 +70,9 @@ func (e *exporter) Export(ctx context.Context, data metricdata.ResourceMetrics)
|
||||
default:
|
||||
// Context is still valid, continue.
|
||||
}
|
||||
|
||||
if e.redactTimestamps {
|
||||
data = redactTimestamps(data)
|
||||
}
|
||||
return e.encVal.Load().(encoderHolder).Encode(data)
|
||||
}
|
||||
|
||||
@@ -81,3 +89,89 @@ func (e *exporter) Shutdown(ctx context.Context) error {
|
||||
})
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
func redactTimestamps(orig metricdata.ResourceMetrics) metricdata.ResourceMetrics {
|
||||
rm := metricdata.ResourceMetrics{
|
||||
Resource: orig.Resource,
|
||||
ScopeMetrics: make([]metricdata.ScopeMetrics, len(orig.ScopeMetrics)),
|
||||
}
|
||||
for i, sm := range orig.ScopeMetrics {
|
||||
rm.ScopeMetrics[i] = metricdata.ScopeMetrics{
|
||||
Scope: sm.Scope,
|
||||
Metrics: make([]metricdata.Metrics, len(sm.Metrics)),
|
||||
}
|
||||
for j, m := range sm.Metrics {
|
||||
rm.ScopeMetrics[i].Metrics[j] = metricdata.Metrics{
|
||||
Name: m.Name,
|
||||
Description: m.Description,
|
||||
Unit: m.Unit,
|
||||
Data: redactAggregationTimestamps(m.Data),
|
||||
}
|
||||
}
|
||||
}
|
||||
return rm
|
||||
}
|
||||
|
||||
var (
|
||||
errUnknownAggType = errors.New("unknown aggregation type")
|
||||
)
|
||||
|
||||
func redactAggregationTimestamps(orig metricdata.Aggregation) metricdata.Aggregation {
|
||||
switch a := orig.(type) {
|
||||
case metricdata.Sum[float64]:
|
||||
return metricdata.Sum[float64]{
|
||||
Temporality: a.Temporality,
|
||||
DataPoints: redactDataPointTimestamps(a.DataPoints),
|
||||
IsMonotonic: a.IsMonotonic,
|
||||
}
|
||||
case metricdata.Sum[int64]:
|
||||
return metricdata.Sum[int64]{
|
||||
Temporality: a.Temporality,
|
||||
DataPoints: redactDataPointTimestamps(a.DataPoints),
|
||||
IsMonotonic: a.IsMonotonic,
|
||||
}
|
||||
case metricdata.Gauge[float64]:
|
||||
return metricdata.Gauge[float64]{
|
||||
DataPoints: redactDataPointTimestamps(a.DataPoints),
|
||||
}
|
||||
case metricdata.Gauge[int64]:
|
||||
return metricdata.Gauge[int64]{
|
||||
DataPoints: redactDataPointTimestamps(a.DataPoints),
|
||||
}
|
||||
case metricdata.Histogram:
|
||||
return metricdata.Histogram{
|
||||
Temporality: a.Temporality,
|
||||
DataPoints: redactHistogramTimestamps(a.DataPoints),
|
||||
}
|
||||
default:
|
||||
global.Error(errUnknownAggType, fmt.Sprintf("%T", a))
|
||||
return orig
|
||||
}
|
||||
}
|
||||
|
||||
func redactHistogramTimestamps(hdp []metricdata.HistogramDataPoint) []metricdata.HistogramDataPoint {
|
||||
out := make([]metricdata.HistogramDataPoint, len(hdp))
|
||||
for i, dp := range hdp {
|
||||
out[i] = metricdata.HistogramDataPoint{
|
||||
Attributes: dp.Attributes,
|
||||
Count: dp.Count,
|
||||
Sum: dp.Sum,
|
||||
Bounds: dp.Bounds,
|
||||
BucketCounts: dp.BucketCounts,
|
||||
Min: dp.Min,
|
||||
Max: dp.Max,
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func redactDataPointTimestamps[T int64 | float64](sdp []metricdata.DataPoint[T]) []metricdata.DataPoint[T] {
|
||||
out := make([]metricdata.DataPoint[T], len(sdp))
|
||||
for i, dp := range sdp {
|
||||
out[i] = metricdata.DataPoint[T]{
|
||||
Attributes: dp.Attributes,
|
||||
Value: dp.Value,
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user