Skip to content
10 changes: 10 additions & 0 deletions router/core/engine_loader_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"slices"
"sync/atomic"
"time"

rcontext "github.com/wundergraph/cosmo/router/internal/context"
Expand Down Expand Up @@ -93,6 +94,9 @@ func (f *engineLoaderHooks) OnLoad(ctx context.Context, ds resolve.DataSourceInf

ctx = context.WithValue(ctx, rcontext.CurrentSubgraphContextKey{}, ds.Name)

duration := atomic.Int64{}
ctx = context.WithValue(ctx, rcontext.FetchTimingKey, &duration)

reqContext := getRequestContext(ctx)
if reqContext == nil {
return ctx
Expand Down Expand Up @@ -153,6 +157,12 @@ func (f *engineLoaderHooks) OnFinished(ctx context.Context, ds resolve.DataSourc
exprCtx.Subgraph.Name = ds.Name
exprCtx.Subgraph.Request.Error = WrapExprError(responseInfo.Err)

if value := ctx.Value(rcontext.FetchTimingKey); value != nil {
Comment thread
StarpTech marked this conversation as resolved.
if fetchTiming, ok := value.(*atomic.Int64); ok {
exprCtx.Subgraph.Request.ClientTrace.FetchDuration = time.Duration(fetchTiming.Load())
}
}

if f.storeSubgraphResponseBody {
exprCtx.Subgraph.Response.Body.Raw = responseInfo.GetResponseBody()
}
Expand Down
1 change: 1 addition & 0 deletions router/internal/context/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ const (
RequestContextKey ContextKey = iota
SubgraphResolverContextKey
EngineLoaderHooksContextKey
FetchTimingKey
)
4 changes: 3 additions & 1 deletion router/internal/expr/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"net/url"
"time"

"github.com/expr-lang/expr/file"
"github.com/wundergraph/cosmo/router/pkg/authentication"
Expand Down Expand Up @@ -132,7 +133,8 @@ type SubgraphResponse struct {
}

type ClientTrace struct {
ConnectionAcquireDuration float64 `expr:"connAcquireDuration"`
FetchDuration time.Duration `expr:"fetchDuration"`
ConnectionAcquireDuration float64 `expr:"connAcquireDuration"`
}

// Subgraph Related
Expand Down
13 changes: 13 additions & 0 deletions router/pkg/grpcconnector/grpccommon/grpc_plugin_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package grpccommon
import (
"context"
"errors"
rcontext "github.com/wundergraph/cosmo/router/internal/context"
"go.opentelemetry.io/otel/trace"
"io"

Expand Down Expand Up @@ -147,9 +148,21 @@ func (g *GRPCPluginClient) SetClients(pluginClient *plugin.Client, clientConn gr
// Invoke implements grpc.ClientConnInterface.
func (g *GRPCPluginClient) Invoke(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error {
spanName, traceAttributes := g.getTraceAttributes(ctx)

ctx, span := g.tracer.Start(ctx, spanName, traceAttributes)
defer span.End()

startTime := time.Now()
defer func() {
// In case of GRPC Clients there will be multiple invocations
// so adding is required
if value := ctx.Value(rcontext.FetchTimingKey); value != nil {
if fetchTiming, ok := value.(*atomic.Int64); ok {
fetchTiming.Add(int64(time.Since(startTime)))
}
}
}()

if g.IsPluginProcessExited() {
if err := g.waitForPluginToBeActive(); err != nil {
span.RecordError(err)
Expand Down
14 changes: 12 additions & 2 deletions router/pkg/trace/transport.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package trace

import (
"net/http"

"github.com/wundergraph/cosmo/router/internal/context"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"net/http"
"sync/atomic"
"time"
)

type TransportOption func(svr *transport)
Expand Down Expand Up @@ -36,8 +38,16 @@ func (t *transport) RoundTrip(r *http.Request) (*http.Response, error) {
t.handler(r)
}

startTime := time.Now()

res, err := t.rt.RoundTrip(r)

if value := r.Context().Value(context.FetchTimingKey); value != nil {
if fetchTiming, ok := value.(*atomic.Int64); ok {
fetchTiming.Add(int64(time.Since(startTime)))
}
}

// In case of a roundtrip error the span status is set to error by the otelhttp.RoundTrip function.
// Also, status code >= 500 is considered an error

Expand Down
Loading