mirror of
https://github.com/hanzoai/telemetry.git
synced 2026-10-30 20:01:49 +00:00
#### Description [gofumpt](https://golangci-lint.run/usage/linters/#gofumpt) is a stricter format than gofmt, while being backwards compatible. Signed-off-by: Matthieu MOREL <[email protected]>
215 lines
5.7 KiB
Go
215 lines
5.7 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package exportertest // import "go.opentelemetry.io/collector/exporter/exportertest"
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/rand/v2"
|
|
"sync"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
|
|
"go.opentelemetry.io/collector/consumer"
|
|
"go.opentelemetry.io/collector/consumer/consumererror"
|
|
"go.opentelemetry.io/collector/pdata/pcommon"
|
|
"go.opentelemetry.io/collector/pdata/plog"
|
|
"go.opentelemetry.io/collector/pdata/pmetric"
|
|
"go.opentelemetry.io/collector/pdata/ptrace"
|
|
)
|
|
|
|
var (
|
|
errNonPermanent = status.Error(codes.DeadlineExceeded, "non Permanent error")
|
|
errPermanent = status.Error(codes.Internal, "Permanent error")
|
|
)
|
|
|
|
// // randomNonPermanentErrorConsumeDecision is a decision function that succeeds approximately
|
|
// // half of the time and fails with a non-permanent error the rest of the time.
|
|
func randomNonPermanentErrorConsumeDecision() error {
|
|
if rand.Float32() < 0.5 {
|
|
return errNonPermanent
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// randomPermanentErrorConsumeDecision is a decision function that succeeds approximately
|
|
// half of the time and fails with a permanent error the rest of the time.
|
|
func randomPermanentErrorConsumeDecision() error {
|
|
if rand.Float32() < 0.5 {
|
|
return consumererror.NewPermanent(errPermanent)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// randomErrorsConsumeDecision is a decision function that succeeds approximately
|
|
// a third of the time, fails with a permanent error the third of the time and fails with
|
|
// a non-permanent error the rest of the time.
|
|
func randomErrorsConsumeDecision() error {
|
|
r := rand.Float64()
|
|
third := 1.0 / 3.0
|
|
if r < third {
|
|
return consumererror.NewPermanent(errPermanent)
|
|
}
|
|
if r < 2*third {
|
|
return errNonPermanent
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type mockConsumer struct {
|
|
consumer.Traces
|
|
consumer.Logs
|
|
consumer.Metrics
|
|
reqCounter *requestCounter
|
|
mux sync.Mutex
|
|
exportErrorFunction func() error
|
|
receivedTraces []ptrace.Traces
|
|
receivedMetrics []pmetric.Metrics
|
|
receivedLogs []plog.Logs
|
|
}
|
|
|
|
func newMockConsumer(decisionFunc func() error) mockConsumer {
|
|
return mockConsumer{
|
|
reqCounter: newRequestCounter(),
|
|
mux: sync.Mutex{},
|
|
exportErrorFunction: decisionFunc,
|
|
receivedTraces: nil,
|
|
receivedMetrics: nil,
|
|
receivedLogs: nil,
|
|
}
|
|
}
|
|
|
|
func (r *mockConsumer) ConsumeLogs(_ context.Context, ld plog.Logs) error {
|
|
r.mux.Lock()
|
|
defer r.mux.Unlock()
|
|
r.reqCounter.total++
|
|
generatedError := r.exportErrorFunction()
|
|
if generatedError != nil {
|
|
r.processError(generatedError)
|
|
return generatedError
|
|
}
|
|
r.reqCounter.success++
|
|
r.receivedLogs = append(r.receivedLogs, ld)
|
|
return nil
|
|
}
|
|
|
|
func (r *mockConsumer) ConsumeTraces(_ context.Context, td ptrace.Traces) error {
|
|
r.mux.Lock()
|
|
defer r.mux.Unlock()
|
|
r.reqCounter.total++
|
|
generatedError := r.exportErrorFunction()
|
|
if generatedError != nil {
|
|
r.processError(generatedError)
|
|
return generatedError
|
|
}
|
|
r.reqCounter.success++
|
|
r.receivedTraces = append(r.receivedTraces, td)
|
|
return nil
|
|
}
|
|
|
|
func (r *mockConsumer) ConsumeMetrics(_ context.Context, md pmetric.Metrics) error {
|
|
r.mux.Lock()
|
|
defer r.mux.Unlock()
|
|
r.reqCounter.total++
|
|
generatedError := r.exportErrorFunction()
|
|
if generatedError != nil {
|
|
r.processError(generatedError)
|
|
return generatedError
|
|
}
|
|
r.reqCounter.success++
|
|
r.receivedMetrics = append(r.receivedMetrics, md)
|
|
return nil
|
|
}
|
|
|
|
func (r *mockConsumer) Capabilities() consumer.Capabilities {
|
|
return consumer.Capabilities{}
|
|
}
|
|
|
|
func (r *mockConsumer) processError(err error) {
|
|
if consumererror.IsPermanent(err) {
|
|
r.reqCounter.error.permanent++
|
|
} else {
|
|
r.reqCounter.error.nonpermanent++
|
|
}
|
|
}
|
|
|
|
func (r *mockConsumer) clear() {
|
|
r.mux.Lock()
|
|
defer r.mux.Unlock()
|
|
r.reqCounter = newRequestCounter()
|
|
}
|
|
|
|
func (r *mockConsumer) getRequestCounter() *requestCounter {
|
|
return r.reqCounter
|
|
}
|
|
|
|
type requestCounter struct {
|
|
success int
|
|
error errorCounter
|
|
total int
|
|
}
|
|
|
|
type errorCounter struct {
|
|
permanent int
|
|
nonpermanent int
|
|
}
|
|
|
|
func newErrorCounter() errorCounter {
|
|
return errorCounter{
|
|
permanent: 0,
|
|
nonpermanent: 0,
|
|
}
|
|
}
|
|
|
|
func newRequestCounter() *requestCounter {
|
|
return &requestCounter{
|
|
success: 0,
|
|
error: newErrorCounter(),
|
|
total: 0,
|
|
}
|
|
}
|
|
|
|
func idFromLogs(data plog.Logs) (string, error) {
|
|
var logID string
|
|
rss := data.ResourceLogs()
|
|
key, exists := rss.At(0).ScopeLogs().At(0).LogRecords().At(0).Attributes().Get(uniqueIDAttrName)
|
|
if !exists {
|
|
return "", fmt.Errorf("invalid data element, attribute %q is missing", uniqueIDAttrName)
|
|
}
|
|
if key.Type() != pcommon.ValueTypeStr {
|
|
return "", fmt.Errorf("invalid data element, attribute %q is wrong type %v", uniqueIDAttrName, key.Type())
|
|
}
|
|
logID = key.Str()
|
|
return logID, nil
|
|
}
|
|
|
|
func idFromTraces(data ptrace.Traces) (string, error) {
|
|
var traceID string
|
|
rss := data.ResourceSpans()
|
|
key, exists := rss.At(0).ScopeSpans().At(0).Spans().At(0).Attributes().Get(uniqueIDAttrName)
|
|
if !exists {
|
|
return "", fmt.Errorf("invalid data element, attribute %q is missing", uniqueIDAttrName)
|
|
}
|
|
if key.Type() != pcommon.ValueTypeStr {
|
|
return "", fmt.Errorf("invalid data element, attribute %q is wrong type %v", uniqueIDAttrName, key.Type())
|
|
}
|
|
traceID = key.Str()
|
|
return traceID, nil
|
|
}
|
|
|
|
func idFromMetrics(data pmetric.Metrics) (string, error) {
|
|
var metricID string
|
|
rss := data.ResourceMetrics()
|
|
key, exists := rss.At(0).ScopeMetrics().At(0).Metrics().At(0).Histogram().DataPoints().At(0).Attributes().Get(
|
|
uniqueIDAttrName)
|
|
if !exists {
|
|
return "", fmt.Errorf("invalid data element, attribute %q is missing", uniqueIDAttrName)
|
|
}
|
|
if key.Type() != pcommon.ValueTypeStr {
|
|
return "", fmt.Errorf("invalid data element, attribute %q is wrong type %v", uniqueIDAttrName, key.Type())
|
|
}
|
|
metricID = key.Str()
|
|
return metricID, nil
|
|
}
|