-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #27 from technologize/http-metrics
Added Http Client metrics Recording
- Loading branch information
Showing
10 changed files
with
349 additions
and
28 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package otelhttpmetrics | ||
|
||
import ( | ||
"net/http" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
semconv "go.opentelemetry.io/otel/semconv/v1.7.0" | ||
) | ||
|
||
type config struct { | ||
recordInFlight bool | ||
recordSize bool | ||
recordDuration bool | ||
groupedStatus bool | ||
recorder Recorder | ||
attributes func(*http.Request) []attribute.KeyValue | ||
shouldRecord func(*http.Request) bool | ||
} | ||
|
||
func defaultConfig() *config { | ||
return &config{ | ||
recordInFlight: true, | ||
recordDuration: true, | ||
recordSize: true, | ||
groupedStatus: true, | ||
attributes: DefaultAttributes, | ||
shouldRecord: func(_ *http.Request) bool { | ||
return true | ||
}, | ||
} | ||
} | ||
|
||
var DefaultAttributes = func(request *http.Request) []attribute.KeyValue { | ||
attrs := []attribute.KeyValue{ | ||
semconv.HTTPMethodKey.String(request.Method), | ||
} | ||
origin := request.Host | ||
target := request.URL.Path | ||
if origin != "" { | ||
attrs = append(attrs, semconv.HTTPHostKey.String(origin)) | ||
} | ||
if target != "" { | ||
attrs = append(attrs, semconv.HTTPTargetKey.String(target)) | ||
} | ||
return attrs | ||
} |
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,74 @@ | ||
package otelhttpmetrics | ||
|
||
import ( | ||
"net/http" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
) | ||
|
||
// Option applies a configuration to the given config | ||
type Option interface { | ||
apply(cfg *config) | ||
} | ||
|
||
type optionFunc func(cfg *config) | ||
|
||
func (fn optionFunc) apply(cfg *config) { | ||
fn(cfg) | ||
} | ||
|
||
// WithAttributes sets a func using which what attributes to be recorded can be specified. | ||
// By default the DefaultAttributes is used | ||
func WithAttributes(attributes func(*http.Request) []attribute.KeyValue) Option { | ||
return optionFunc(func(cfg *config) { | ||
cfg.attributes = attributes | ||
}) | ||
} | ||
|
||
// WithRecordInFlight determines whether to record In Flight Requests or not | ||
// By default the recordInFlight is true | ||
func WithRecordInFlightDisabled() Option { | ||
return optionFunc(func(cfg *config) { | ||
cfg.recordInFlight = false | ||
}) | ||
} | ||
|
||
// WithRecordDuration determines whether to record Duration of Requests or not | ||
// By default the recordDuration is true | ||
func WithRecordDurationDisabled() Option { | ||
return optionFunc(func(cfg *config) { | ||
cfg.recordDuration = false | ||
}) | ||
} | ||
|
||
// WithRecordSize determines whether to record Size of Requests and Responses or not | ||
// By default the recordSize is true | ||
func WithRecordSizeDisabled() Option { | ||
return optionFunc(func(cfg *config) { | ||
cfg.recordSize = false | ||
}) | ||
} | ||
|
||
// WithGroupedStatus determines whether to group the response status codes or not. If true 2xx, 3xx will be stored | ||
// By default the groupedStatus is true | ||
func WithGroupedStatusDisabled() Option { | ||
return optionFunc(func(cfg *config) { | ||
cfg.groupedStatus = false | ||
}) | ||
} | ||
|
||
// WithRecorder sets a recorder for recording requests | ||
// By default the open telemetry recorder is used | ||
func WithRecorder(recorder Recorder) Option { | ||
return optionFunc(func(cfg *config) { | ||
cfg.recorder = recorder | ||
}) | ||
} | ||
|
||
// WithShouldRecordFunc sets a func using which whether a record should be recorded | ||
// By default the all api calls are recorded | ||
func WithShouldRecordFunc(shouldRecord func(*http.Request) bool) Option { | ||
return optionFunc(func(cfg *config) { | ||
cfg.shouldRecord = shouldRecord | ||
}) | ||
} |
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,73 @@ | ||
package otelhttpmetrics | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/metric" | ||
"go.opentelemetry.io/otel/metric/global" | ||
"go.opentelemetry.io/otel/metric/instrument" | ||
"go.opentelemetry.io/otel/metric/instrument/syncint64" | ||
"go.opentelemetry.io/otel/metric/unit" | ||
) | ||
|
||
const instrumentationName = "github.com/technologize/otel-go-contrib/otelhttpmetrics" | ||
|
||
// Recorder knows how to record and measure the metrics. This | ||
// has the required methods to be used with the HTTP | ||
// middlewares. | ||
type otelRecorder struct { | ||
attemptsCounter syncint64.UpDownCounter | ||
totalDuration syncint64.Histogram | ||
activeRequestsCounter syncint64.UpDownCounter | ||
requestSize syncint64.Histogram | ||
responseSize syncint64.Histogram | ||
} | ||
|
||
func GetRecorder(metricsPrefix string) Recorder { | ||
metricName := func(metricName string) string { | ||
if len(metricsPrefix) > 0 { | ||
return metricsPrefix + "." + metricName | ||
} | ||
return metricName | ||
} | ||
meter := global.MeterProvider().Meter(instrumentationName, metric.WithInstrumentationVersion(SemVersion())) | ||
attemptsCounter, _ := meter.SyncInt64().UpDownCounter(metricName("http.client.request_count"), instrument.WithDescription("Number of Requests"), instrument.WithUnit(unit.Dimensionless)) | ||
totalDuration, _ := meter.SyncInt64().Histogram(metricName("http.client.duration"), instrument.WithDescription("Time Taken by request"), instrument.WithUnit(unit.Milliseconds)) | ||
activeRequestsCounter, _ := meter.SyncInt64().UpDownCounter(metricName("http.client.active_requests"), instrument.WithDescription("Number of requests inflight"), instrument.WithUnit(unit.Dimensionless)) | ||
requestSize, _ := meter.SyncInt64().Histogram(metricName("http.client.request_content_length"), instrument.WithDescription("Request Size"), instrument.WithUnit(unit.Bytes)) | ||
responseSize, _ := meter.SyncInt64().Histogram(metricName("http.client.response_content_length"), instrument.WithDescription("Response Size"), instrument.WithUnit(unit.Bytes)) | ||
return &otelRecorder{ | ||
attemptsCounter: attemptsCounter, | ||
totalDuration: totalDuration, | ||
activeRequestsCounter: activeRequestsCounter, | ||
requestSize: requestSize, | ||
responseSize: responseSize, | ||
} | ||
} | ||
|
||
// AddRequests increments the number of requests being processed. | ||
func (r *otelRecorder) AddRequests(ctx context.Context, quantity int64, attributes []attribute.KeyValue) { | ||
r.attemptsCounter.Add(ctx, quantity, attributes...) | ||
} | ||
|
||
// ObserveHTTPRequestDuration measures the duration of an HTTP request. | ||
func (r *otelRecorder) ObserveHTTPRequestDuration(ctx context.Context, duration time.Duration, attributes []attribute.KeyValue) { | ||
r.totalDuration.Record(ctx, int64(duration/time.Millisecond), attributes...) | ||
} | ||
|
||
// ObserveHTTPRequestSize measures the size of an HTTP request in bytes. | ||
func (r *otelRecorder) ObserveHTTPRequestSize(ctx context.Context, sizeBytes int64, attributes []attribute.KeyValue) { | ||
r.requestSize.Record(ctx, sizeBytes, attributes...) | ||
} | ||
|
||
// ObserveHTTPResponseSize measures the size of an HTTP response in bytes. | ||
func (r *otelRecorder) ObserveHTTPResponseSize(ctx context.Context, sizeBytes int64, attributes []attribute.KeyValue) { | ||
r.responseSize.Record(ctx, sizeBytes, attributes...) | ||
} | ||
|
||
// AddInflightRequests increments and decrements the number of inflight request being processed. | ||
func (r *otelRecorder) AddInflightRequests(ctx context.Context, quantity int64, attributes []attribute.KeyValue) { | ||
r.activeRequestsCounter.Add(ctx, quantity, attributes...) | ||
} |
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,25 @@ | ||
package otelhttpmetrics | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
) | ||
|
||
type Recorder interface { | ||
// AddRequests increments the number of requests being processed. | ||
AddRequests(ctx context.Context, quantity int64, attributes []attribute.KeyValue) | ||
|
||
// ObserveHTTPRequestDuration measures the duration of an HTTP request. | ||
ObserveHTTPRequestDuration(ctx context.Context, duration time.Duration, attributes []attribute.KeyValue) | ||
|
||
// ObserveHTTPRequestSize measures the size of an HTTP request in bytes. | ||
ObserveHTTPRequestSize(ctx context.Context, sizeBytes int64, attributes []attribute.KeyValue) | ||
|
||
// ObserveHTTPResponseSize measures the size of an HTTP response in bytes. | ||
ObserveHTTPResponseSize(ctx context.Context, sizeBytes int64, attributes []attribute.KeyValue) | ||
|
||
// AddInflightRequests increments and decrements the number of inflight request being processed. | ||
AddInflightRequests(ctx context.Context, quantity int64, attributes []attribute.KeyValue) | ||
} |
Oops, something went wrong.