2023-03-15 08:07:44 +00:00
|
|
|
package utils
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2023-06-09 06:39:01 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
2023-03-15 08:07:44 +00:00
|
|
|
"github.com/valyala/fasthttp"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
|
|
"go.opentelemetry.io/otel/codes"
|
|
|
|
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
|
|
|
"go.opentelemetry.io/otel/trace"
|
|
|
|
)
|
|
|
|
|
|
|
|
type httpCarrier struct {
|
|
|
|
r *fasthttp.RequestCtx
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *httpCarrier) Get(key string) string {
|
|
|
|
bytes := c.r.Request.Header.Peek(key)
|
|
|
|
if len(bytes) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return string(bytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *httpCarrier) Set(key string, value string) {
|
|
|
|
c.r.Response.Header.Set(key, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *httpCarrier) Keys() []string {
|
|
|
|
dict := make(map[string]interface{})
|
|
|
|
c.r.Request.Header.VisitAll(
|
|
|
|
func(key, value []byte) {
|
|
|
|
dict[string(key)] = true
|
|
|
|
},
|
|
|
|
)
|
|
|
|
c.r.Response.Header.VisitAll(
|
|
|
|
func(key, value []byte) {
|
|
|
|
dict[string(key)] = true
|
|
|
|
},
|
|
|
|
)
|
|
|
|
result := make([]string, 0, len(dict))
|
|
|
|
for key := range dict {
|
|
|
|
result = append(result, key)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func extractHTTPTraceInfo(ctx context.Context, req *fasthttp.RequestCtx) context.Context {
|
|
|
|
if req == nil {
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
carrier := &httpCarrier{r: req}
|
|
|
|
return tracing.Propagator.Extract(ctx, carrier)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetHTTPTraceInfo saves trace headers to response.
|
|
|
|
func SetHTTPTraceInfo(ctx context.Context, span trace.Span, req *fasthttp.RequestCtx) {
|
|
|
|
if req == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := req.Err(); err != nil {
|
|
|
|
span.SetStatus(codes.Error, err.Error())
|
|
|
|
}
|
|
|
|
span.SetAttributes(
|
|
|
|
semconv.HTTPStatusCode(req.Response.StatusCode()),
|
|
|
|
)
|
|
|
|
carrier := &httpCarrier{r: req}
|
|
|
|
tracing.Propagator.Inject(ctx, carrier)
|
|
|
|
}
|
|
|
|
|
|
|
|
// StartHTTPServerSpan starts root HTTP server span.
|
|
|
|
func StartHTTPServerSpan(ctx context.Context, req *fasthttp.RequestCtx, operationName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
|
|
|
|
ctx = extractHTTPTraceInfo(ctx, req)
|
|
|
|
opts = append(opts, trace.WithAttributes(
|
|
|
|
attribute.String("http.client_address", req.RemoteAddr().String()),
|
|
|
|
attribute.String("http.path", string(req.Path())),
|
|
|
|
semconv.HTTPMethod(string(req.Method())),
|
|
|
|
semconv.RPCService("frostfs-http-gw"),
|
2023-05-30 14:01:20 +00:00
|
|
|
attribute.String("http.query", req.QueryArgs().String()),
|
2023-03-15 08:07:44 +00:00
|
|
|
), trace.WithSpanKind(trace.SpanKindServer))
|
|
|
|
return tracing.StartSpanFromContext(ctx, operationName, opts...)
|
|
|
|
}
|