forked from raystack/meteor
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add open-telemetry metrics to http clients
- Loading branch information
Shivaprasad
committed
Jul 28, 2023
1 parent
1f39c3a
commit 31804ba
Showing
12 changed files
with
168 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package telemetry | ||
|
||
import ( | ||
"io" | ||
"net/http" | ||
"sync/atomic" | ||
"time" | ||
|
||
"go.opentelemetry.io/otel" | ||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/metric" | ||
) | ||
|
||
// Refer OpenTelemetry Semantic Conventions for HTTP Client. | ||
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-metrics.md#http-client | ||
const ( | ||
metricClientDuration = "http.client.duration" | ||
metricClientRequestSize = "http.client.request.size" | ||
metricClientResponseSize = "http.client.response.size" | ||
|
||
attributeServerPort = "server.port" | ||
attributeServerAddress = "server.address" | ||
attributeRequestMethod = "http.request.method" | ||
attributeResponseStatusCode = "http.response.status_code" | ||
) | ||
|
||
func NewHTTPTransport(baseTransport http.RoundTripper) http.RoundTripper { | ||
if _, ok := baseTransport.(*httpTransport); ok { | ||
return baseTransport | ||
} | ||
|
||
if baseTransport == nil { | ||
baseTransport = http.DefaultTransport | ||
} | ||
|
||
icl := &httpTransport{roundTripper: baseTransport} | ||
icl.createMeasures(otel.Meter("github.com/goto/meteor/plugins/internal/telemetry")) | ||
|
||
return icl | ||
} | ||
|
||
type httpTransport struct { | ||
roundTripper http.RoundTripper | ||
|
||
counters map[string]metric.Int64Counter | ||
valueRecorders map[string]metric.Float64Histogram | ||
} | ||
|
||
func (tr *httpTransport) RoundTrip(req *http.Request) (*http.Response, error) { | ||
ctx := req.Context() | ||
startAt := time.Now() | ||
|
||
var bw bodyWrapper | ||
if req.Body != nil && req.Body != http.NoBody { | ||
bw.ReadCloser = req.Body | ||
req.Body = &bw | ||
} | ||
|
||
attribs := metric.WithAttributes( | ||
attribute.String(attributeRequestMethod, req.Method), | ||
attribute.String(attributeServerAddress, req.URL.Hostname()), | ||
attribute.String(attributeServerPort, req.URL.Port()), | ||
) | ||
|
||
tr.counters[metricClientRequestSize].Add(ctx, bw.read.Load(), attribs) | ||
|
||
resp, err := tr.roundTripper.RoundTrip(req) | ||
elapsedTime := float64(time.Since(startAt)) / float64(time.Millisecond) | ||
tr.valueRecorders[metricClientDuration].Record(ctx, elapsedTime, attribs) | ||
|
||
if err == nil { | ||
tr.counters[metricClientResponseSize].Add(ctx, resp.ContentLength, attribs, | ||
metric.WithAttributes(attribute.Int(attributeResponseStatusCode, resp.StatusCode))) | ||
} | ||
|
||
return resp, err | ||
} | ||
|
||
func (tr *httpTransport) createMeasures(meter metric.Meter) { | ||
tr.counters = make(map[string]metric.Int64Counter) | ||
tr.valueRecorders = make(map[string]metric.Float64Histogram) | ||
|
||
requestBytesCounter, err := meter.Int64Counter(metricClientRequestSize) | ||
handleErr(err) | ||
|
||
responseBytesCounter, err := meter.Int64Counter(metricClientResponseSize) | ||
handleErr(err) | ||
|
||
clientLatencyMeasure, err := meter.Float64Histogram(metricClientDuration) | ||
handleErr(err) | ||
|
||
tr.counters[metricClientRequestSize] = requestBytesCounter | ||
tr.counters[metricClientResponseSize] = responseBytesCounter | ||
tr.valueRecorders[metricClientDuration] = clientLatencyMeasure | ||
} | ||
|
||
func handleErr(err error) { | ||
if err != nil { | ||
otel.Handle(err) | ||
} | ||
} | ||
|
||
// bodyWrapper wraps a http.Request.Body (an io.ReadCloser) to track the number | ||
// of bytes read and the last error. | ||
type bodyWrapper struct { | ||
io.ReadCloser | ||
|
||
read atomic.Int64 | ||
err error | ||
} | ||
|
||
func (w *bodyWrapper) Read(b []byte) (int, error) { | ||
n, err := w.ReadCloser.Read(b) | ||
w.read.Add(int64(n)) | ||
w.err = err | ||
return n, err | ||
} | ||
|
||
func (w *bodyWrapper) Close() error { | ||
return w.ReadCloser.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package telemetry_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/goto/meteor/plugins/internal/telemetry" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestNewHTTPTransport(t *testing.T) { | ||
tr := telemetry.NewHTTPTransport(nil) | ||
assert.NotNil(t, tr) | ||
} | ||
|
||
func TestHTTPTransport_RoundTrip(t *testing.T) { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters