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 all commits
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## Unreleased

- Update to OTel-Go 1.11.x and OTel-Go metrics 0.34.
[#333](https://github.com/lightstep/otel-launcher-go/pull/333)
- Remove Lightstep-specific partial-success error handling, now using upstream.
[#250](https://github.com/lightstep/otel-launcher-go/pull/250)

## [1.11.1](https://github.com/lightstep/otel-launcher-go/releases/tag/v1.11.0) - 2022-10-05

### Bug fixes
Expand Down
20 changes: 0 additions & 20 deletions README.md
Expand Up @@ -195,26 +195,6 @@ Lightstep users are recommended to select either the "cumulative" or
"stateless" preference. The OpenTelemetry-specified "delta"
temporality preference is not recommended for Lightstep users.

### 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
82 changes: 1 addition & 81 deletions pipelines/metrics.go
Expand Up @@ -16,9 +16,7 @@ package pipelines

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

Expand All @@ -39,6 +37,7 @@ import (
contribHost "go.opentelemetry.io/contrib/instrumentation/host"
contribRuntime "go.opentelemetry.io/contrib/instrumentation/runtime"

// OTel APIs
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
metricglobal "go.opentelemetry.io/otel/metric/global"
Expand All @@ -47,9 +46,7 @@ import (
otelotlpmetricgrpc "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
otelsdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding/gzip"
"google.golang.org/grpc/metadata"
)

func prestableHostMetrics(provider metric.MeterProvider) error {
Expand Down Expand Up @@ -193,87 +190,13 @@ 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(secure otlpmetricgrpc.Option) (otlpmetric.Client, error) {
return otlpmetricgrpc.NewClient(
context.Background(),
secure,
otlpmetricgrpc.WithEndpoint(c.Endpoint),
otlpmetricgrpc.WithHeaders(c.Headers),
otlpmetricgrpc.WithCompressor(gzip.Name),
otlpmetricgrpc.WithDialOption(
grpc.WithUnaryInterceptor(interceptor),
),
)
}

Expand All @@ -293,9 +216,6 @@ func (c PipelineConfig) newOtelMetricsExporter(temporality otelsdkmetric.Tempora
otelotlpmetricgrpc.WithEndpoint(c.Endpoint),
otelotlpmetricgrpc.WithHeaders(c.Headers),
otelotlpmetricgrpc.WithCompressor(gzip.Name),
otelotlpmetricgrpc.WithDialOption(
grpc.WithUnaryInterceptor(interceptor),
),
)
}

Expand Down