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

Remove special validation error handling #250

Merged
merged 5 commits into from Dec 9, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 0 additions & 20 deletions README.md
Expand Up @@ -138,26 +138,6 @@ synchronous and asynchronous UpDownCounter instruments are specified
to use cumulative temporality in OpenTelemetry metrics SDKs
independent of the temporality preference.

### Metrics validation errors

Lightstep performs a number of validation steps over metrics data
before accepting it. When an OTLP Metrics export request is
successful but data is completely or partially rejected for any
reason, the outcome is detailed using Lightstep-specific response
headers.

These headers predate [work in OpenTelemetry on returning partial
success](https://github.com/open-telemetry/opentelemetry-proto/pull/390).
Lightstep expects to use standard OTLP fields to convey these
partially-successful outcomes in the future.

Validation errors are generally repetitive. Lightstep limits the size
of each partial-success response to lower the overhead associated with
these responses using random selection.

The launcher contains special code to interpret these headers and
direct them to the standard OpenTelemetry-Go error handler.

------

*Made with*
Expand Down
79 changes: 0 additions & 79 deletions pipelines/metrics.go
Expand Up @@ -16,9 +16,7 @@ package pipelines

import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"

Expand All @@ -30,7 +28,6 @@ import (

hostMetrics "go.opentelemetry.io/contrib/instrumentation/host"
runtimeMetrics "go.opentelemetry.io/contrib/instrumentation/runtime"
"go.opentelemetry.io/otel"

"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/metric"
Expand All @@ -43,9 +40,7 @@ import (
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
selector "go.opentelemetry.io/otel/sdk/metric/selector/simple"

"google.golang.org/grpc"
"google.golang.org/grpc/encoding/gzip"
"google.golang.org/grpc/metadata"
)

func NewMetricsPipeline(c PipelineConfig) (func() error, error) {
Expand Down Expand Up @@ -128,86 +123,12 @@ func NewMetricsPipeline(c PipelineConfig) (func() error, error) {
return shutdown, nil
}

var errNoSingleCount = fmt.Errorf("no count")

func singleCount(values []string) (int, error) {
if len(values) != 1 {
return 0, errNoSingleCount
}
return strconv.Atoi(values[0])
}

type dropExample struct {
Reason string `json:"reason"`
Names []string `json:"names"`
}

type dropSummary struct {
Dropped struct {
Points int `json:"points,omitempty"`
Metrics int `json:"metrics,omitempty"`
} `json:"dropped,omitempty"`
Examples []dropExample `json:"examples,omitempty"`
}

func (ds *dropSummary) Empty() bool {
return len(ds.Examples) == 0 && ds.Dropped.Points == 0 && ds.Dropped.Metrics == 0
}

func interceptor(
ctx context.Context,
method string,
req, reply interface{},
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
const invalidTrailerPrefix = "otlp-invalid-"

var md metadata.MD
err := invoker(ctx, method, req, reply, cc, append(opts, grpc.Trailer(&md))...)
if err == nil && md != nil {
var ds dropSummary
for key, values := range md {
key = strings.ToLower(key)
if !strings.HasPrefix(key, "otlp-") {
continue
}

if key == "otlp-points-dropped" {
if points, err := singleCount(values); err == nil {
ds.Dropped.Points = points
}
} else if key == "otlp-metrics-dropped" {
if metrics, err := singleCount(values); err == nil {
ds.Dropped.Metrics = metrics
}
} else if strings.HasPrefix(key, invalidTrailerPrefix) {
key = key[len(invalidTrailerPrefix):]
key = strings.ReplaceAll(key, "-", " ")
ds.Examples = append(ds.Examples, dropExample{
Reason: key,
Names: values,
})
}
}
if !ds.Empty() {
data, _ := json.Marshal(ds)
otel.Handle(fmt.Errorf("metrics partial failure: %v", string(data)))
}
}
return err
}

func (c PipelineConfig) newClient() otlpmetric.Client {
return otlpmetricgrpc.NewClient(
c.secureMetricOption(),
otlpmetricgrpc.WithEndpoint(c.Endpoint),
otlpmetricgrpc.WithHeaders(c.Headers),
otlpmetricgrpc.WithCompressor(gzip.Name),
otlpmetricgrpc.WithDialOption(
grpc.WithUnaryInterceptor(interceptor),
),
)
}

Expand Down