Skip to content

Commit c7f4d78

Browse files
authored
Merge branch 'main' into issue-5161
2 parents 0f99c8a + 014c6fc commit c7f4d78

File tree

8 files changed

+428
-4
lines changed

8 files changed

+428
-4
lines changed

exporters/otlp/otlplog/otlploghttp/config.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"net/http"
99
"net/url"
1010
"time"
11+
12+
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/retry"
1113
)
1214

1315
// Option applies an option to the Exporter.
@@ -165,9 +167,7 @@ func WithTimeout(duration time.Duration) Option {
165167

166168
// RetryConfig defines configuration for retrying the export of log data that
167169
// failed.
168-
type RetryConfig struct {
169-
// TODO: implement.
170-
}
170+
type RetryConfig retry.Config
171171

172172
// WithRetry sets the retry policy for transient retryable errors that are
173173
// returned by the target endpoint.

exporters/otlp/otlplog/otlploghttp/exporter.go

-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,5 @@ func (e *Exporter) Shutdown(ctx context.Context) error {
4848

4949
// ForceFlush does nothing. The Exporter holds no state.
5050
func (e *Exporter) ForceFlush(ctx context.Context) error {
51-
// TODO: implement.
5251
return nil
5352
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package otlploghttp
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestExporterForceFlush(t *testing.T) {
15+
ctx := context.Background()
16+
e, err := New(ctx)
17+
require.NoError(t, err, "New")
18+
19+
assert.NoError(t, e.ForceFlush(ctx), "ForceFlush")
20+
}

exporters/otlp/otlplog/otlploghttp/go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp
33
go 1.21
44

55
require (
6+
github.com/cenkalti/backoff/v4 v4.3.0
67
github.com/stretchr/testify v1.9.0
78
go.opentelemetry.io/otel/sdk/log v0.0.0-20240403115316-6c6e1e7416e9
89
)

exporters/otlp/otlplog/otlploghttp/go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
2+
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
13
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
24
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
35
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal"
5+
6+
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry.go.tmpl "--data={}" --out=retry/retry.go
7+
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry_test.go.tmpl "--data={}" --out=retry/retry_test.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Code created by gotmpl. DO NOT MODIFY.
2+
// source: internal/shared/otlp/retry/retry.go.tmpl
3+
4+
// Copyright The OpenTelemetry Authors
5+
// SPDX-License-Identifier: Apache-2.0
6+
7+
// Package retry provides request retry functionality that can perform
8+
// configurable exponential backoff for transient errors and honor any
9+
// explicit throttle responses received.
10+
package retry // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/retry"
11+
12+
import (
13+
"context"
14+
"fmt"
15+
"time"
16+
17+
"github.com/cenkalti/backoff/v4"
18+
)
19+
20+
// DefaultConfig are the recommended defaults to use.
21+
var DefaultConfig = Config{
22+
Enabled: true,
23+
InitialInterval: 5 * time.Second,
24+
MaxInterval: 30 * time.Second,
25+
MaxElapsedTime: time.Minute,
26+
}
27+
28+
// Config defines configuration for retrying batches in case of export failure
29+
// using an exponential backoff.
30+
type Config struct {
31+
// Enabled indicates whether to not retry sending batches in case of
32+
// export failure.
33+
Enabled bool
34+
// InitialInterval the time to wait after the first failure before
35+
// retrying.
36+
InitialInterval time.Duration
37+
// MaxInterval is the upper bound on backoff interval. Once this value is
38+
// reached the delay between consecutive retries will always be
39+
// `MaxInterval`.
40+
MaxInterval time.Duration
41+
// MaxElapsedTime is the maximum amount of time (including retries) spent
42+
// trying to send a request/batch. Once this value is reached, the data
43+
// is discarded.
44+
MaxElapsedTime time.Duration
45+
}
46+
47+
// RequestFunc wraps a request with retry logic.
48+
type RequestFunc func(context.Context, func(context.Context) error) error
49+
50+
// EvaluateFunc returns if an error is retry-able and if an explicit throttle
51+
// duration should be honored that was included in the error.
52+
//
53+
// The function must return true if the error argument is retry-able,
54+
// otherwise it must return false for the first return parameter.
55+
//
56+
// The function must return a non-zero time.Duration if the error contains
57+
// explicit throttle duration that should be honored, otherwise it must return
58+
// a zero valued time.Duration.
59+
type EvaluateFunc func(error) (bool, time.Duration)
60+
61+
// RequestFunc returns a RequestFunc using the evaluate function to determine
62+
// if requests can be retried and based on the exponential backoff
63+
// configuration of c.
64+
func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
65+
if !c.Enabled {
66+
return func(ctx context.Context, fn func(context.Context) error) error {
67+
return fn(ctx)
68+
}
69+
}
70+
71+
return func(ctx context.Context, fn func(context.Context) error) error {
72+
// Do not use NewExponentialBackOff since it calls Reset and the code here
73+
// must call Reset after changing the InitialInterval (this saves an
74+
// unnecessary call to Now).
75+
b := &backoff.ExponentialBackOff{
76+
InitialInterval: c.InitialInterval,
77+
RandomizationFactor: backoff.DefaultRandomizationFactor,
78+
Multiplier: backoff.DefaultMultiplier,
79+
MaxInterval: c.MaxInterval,
80+
MaxElapsedTime: c.MaxElapsedTime,
81+
Stop: backoff.Stop,
82+
Clock: backoff.SystemClock,
83+
}
84+
b.Reset()
85+
86+
for {
87+
err := fn(ctx)
88+
if err == nil {
89+
return nil
90+
}
91+
92+
retryable, throttle := evaluate(err)
93+
if !retryable {
94+
return err
95+
}
96+
97+
bOff := b.NextBackOff()
98+
if bOff == backoff.Stop {
99+
return fmt.Errorf("max retry time elapsed: %w", err)
100+
}
101+
102+
// Wait for the greater of the backoff or throttle delay.
103+
var delay time.Duration
104+
if bOff > throttle {
105+
delay = bOff
106+
} else {
107+
elapsed := b.GetElapsedTime()
108+
if b.MaxElapsedTime != 0 && elapsed+throttle > b.MaxElapsedTime {
109+
return fmt.Errorf("max retry time would elapse: %w", err)
110+
}
111+
delay = throttle
112+
}
113+
114+
if ctxErr := waitFunc(ctx, delay); ctxErr != nil {
115+
return fmt.Errorf("%w: %s", ctxErr, err)
116+
}
117+
}
118+
}
119+
}
120+
121+
// Allow override for testing.
122+
var waitFunc = wait
123+
124+
// wait takes the caller's context, and the amount of time to wait. It will
125+
// return nil if the timer fires before or at the same time as the context's
126+
// deadline. This indicates that the call can be retried.
127+
func wait(ctx context.Context, delay time.Duration) error {
128+
timer := time.NewTimer(delay)
129+
defer timer.Stop()
130+
131+
select {
132+
case <-ctx.Done():
133+
// Handle the case where the timer and context deadline end
134+
// simultaneously by prioritizing the timer expiration nil value
135+
// response.
136+
select {
137+
case <-timer.C:
138+
default:
139+
return ctx.Err()
140+
}
141+
case <-timer.C:
142+
}
143+
144+
return nil
145+
}

0 commit comments

Comments
 (0)