Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: track external client logs #183

Merged
merged 15 commits into from
Nov 1, 2024
4 changes: 2 additions & 2 deletions core/appeal/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -1578,9 +1578,9 @@ func (s *Service) populateAppealMetadata(ctx context.Context, a *domain.Appeal,
if err != nil {
return err
}
clientCreator := &http.HttpClientCreatorStruct{}
metadataCl, err := http.NewHTTPClient(&cfg.HTTPClientConfig, clientCreator)

clientCreator := &http.HttpClientCreatorStruct{}
metadataCl, err := http.NewHTTPClient(&cfg.HTTPClientConfig, clientCreator, "AppealMetadata")
if err != nil {
return fmt.Errorf("key: %s, %w", key, err)
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.45.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0
go.opentelemetry.io/otel/metric v1.29.0
go.opentelemetry.io/otel/sdk v1.29.0
go.opentelemetry.io/otel/sdk/metric v1.27.0
golang.org/x/net v0.22.0
Expand Down Expand Up @@ -170,7 +171,6 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
Expand Down
4 changes: 3 additions & 1 deletion pkg/gate/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"net/http"
"net/url"
"strconv"

"github.com/goto/guardian/pkg/opentelemetry/otelhttpclient"
)

type Client struct {
Expand All @@ -25,7 +27,7 @@ func NewClient(baseURL string, opts ...ClientOption) (*Client, error) {
client := &Client{
baseURL: url,
options: &options{
httpClient: http.DefaultClient,
httpClient: otelhttpclient.New("GateClient", nil),
},
}
for _, o := range opts {
Expand Down
6 changes: 5 additions & 1 deletion pkg/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http"

validator "github.com/go-playground/validator/v10"
"github.com/goto/guardian/pkg/opentelemetry/otelhttpclient"
defaults "github.com/mcuadros/go-defaults"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
Expand Down Expand Up @@ -61,7 +62,7 @@ type HttpClientCreator interface {
}

// NewHTTPClient returns *iam.Client
func NewHTTPClient(config *HTTPClientConfig, clientCreator HttpClientCreator) (*HTTPClient, error) {
func NewHTTPClient(config *HTTPClientConfig, clientCreator HttpClientCreator, serviceName string) (*HTTPClient, error) {
defaults.SetDefaults(config)
if err := validator.New().Struct(config); err != nil {
return nil, err
Expand All @@ -70,6 +71,9 @@ func NewHTTPClient(config *HTTPClientConfig, clientCreator HttpClientCreator) (*
if httpClient == nil {
httpClient = http.DefaultClient
}
if serviceName != "" {
httpClient = otelhttpclient.New(serviceName, nil)
}

if config.Auth != nil && (config.Auth.Type == "google_idtoken" || config.Auth.Type == "google_oauth2") {
var creds []byte
Expand Down
24 changes: 12 additions & 12 deletions pkg/http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestMakeRequestForGet(t *testing.T) {
Value: "test_value",
},
Method: "GET",
}, nil)
}, nil, "metabase")
if err != nil {
t.Fatalf("Failed to create HTTPClient: %v", err)
}
Expand Down Expand Up @@ -78,7 +78,7 @@ func TestMakeRequestForPostWithPayload(t *testing.T) {
},
Method: "POST",
Body: "test",
}, nil)
}, nil, "metabase")
if err != nil {
t.Fatalf("Failed to create HTTPClient: %v", err)
}
Expand Down Expand Up @@ -121,7 +121,7 @@ func TestMakeRequestForPostWithoutPayload(t *testing.T) {
Token: "test_token",
},
Method: "POST",
}, nil)
}, nil, "metabase")
if err != nil {
t.Fatalf("Failed to create HTTPClient: %v", err)
}
Expand Down Expand Up @@ -167,7 +167,7 @@ func TestMakeRequestForPostWithPayloadForHeaderAuth(t *testing.T) {
},
Method: "POST",
Body: "test",
}, nil)
}, nil, "metabase")
if err != nil {
t.Fatalf("Failed to create HTTPClient: %v", err)
}
Expand Down Expand Up @@ -210,7 +210,7 @@ func TestMakeRequestForPostWithPayloadForBasicAuth(t *testing.T) {
},
Method: "POST",
Body: "test",
}, nil)
}, nil, "metabase")
if err != nil {
t.Fatalf("Failed to create HTTPClient: %v", err)
}
Expand Down Expand Up @@ -260,7 +260,7 @@ func TestNewHTTPClient_GoogleOAuth2(t *testing.T) {
expectedClient := &http.Client{}

mockCreator := new(MockHttpClientCreator)
mockCreator.On("GetHttpClientForGoogleOAuth2", mock.Anything, []byte(credsJSON)).Return(expectedClient, nil)
mockCreator.On("GetHttpClientForGoogleOAuth2", mock.Anything, []byte(credsJSON)).Return(expectedClient, nil, "metabase")

config := &HTTPClientConfig{
URL: "https://example.com",
Expand All @@ -270,7 +270,7 @@ func TestNewHTTPClient_GoogleOAuth2(t *testing.T) {
},
}

client, err := NewHTTPClient(config, mockCreator)
client, err := NewHTTPClient(config, mockCreator, "metabase")

assert.NoError(t, err)
assert.NotNil(t, client)
Expand All @@ -288,7 +288,7 @@ func TestNewHTTPClient_GoogleOAuth2WithEmptyCredentialJson(t *testing.T) {
},
}

client, err := NewHTTPClient(config, mockCreator)
client, err := NewHTTPClient(config, mockCreator, "metabase")

assert.Error(t, err, "missing credentials for google_idtoken or google_oauth2 auth")
assert.Nil(t, client)
Expand All @@ -301,7 +301,7 @@ func TestNewHTTPClient_GoogleIdToken(t *testing.T) {
expectedClient := &http.Client{}

mockCreator := new(MockHttpClientCreator)
mockCreator.On("GetHttpClientForGoogleIdToken", mock.Anything, []byte(credsJSON), "audience").Return(expectedClient, nil)
mockCreator.On("GetHttpClientForGoogleIdToken", mock.Anything, []byte(credsJSON), "audience").Return(expectedClient, nil, "metabase")

config := &HTTPClientConfig{
URL: "https://example.com",
Expand All @@ -312,7 +312,7 @@ func TestNewHTTPClient_GoogleIdToken(t *testing.T) {
},
}

client, err := NewHTTPClient(config, mockCreator)
client, err := NewHTTPClient(config, mockCreator, "metabase")

assert.NoError(t, err)
assert.NotNil(t, client)
Expand All @@ -336,7 +336,7 @@ func TestNewHTTPClient_GoogleIdTokenErrorScenario(t *testing.T) {
},
}

_, err := NewHTTPClient(config, mockCreator)
_, err := NewHTTPClient(config, mockCreator, "metabase")

assert.Equal(t, err.Error(), "error creating http client for google_idtoken")
mockCreator.AssertExpectations(t)
Expand All @@ -358,7 +358,7 @@ func TestNewHTTPClient_GoogleOAuth2ErrorScenario(t *testing.T) {
},
}

_, err := NewHTTPClient(config, mockCreator)
_, err := NewHTTPClient(config, mockCreator, "metabase")

assert.Equal(t, err.Error(), "error creating http client for google_oauth2")
mockCreator.AssertExpectations(t)
Expand Down
2 changes: 1 addition & 1 deletion pkg/opentelemetry/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ type Config struct {
Endpoint string `mapstructure:"endpoint" default:"127.0.0.1:4317"`
} `mapstructure:"otlp"`
SamplingFraction int `mapstructure:"sampling_fraction"`
MetricInterval time.Duration `mapstructure:"metric_interval"`
MetricInterval time.Duration `mapstructure:"metric_interval" default:"15s"`
}
24 changes: 8 additions & 16 deletions pkg/opentelemetry/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ package opentelemetry
import (
"context"
"fmt"
"net/http"
"time"

"go.opentelemetry.io/contrib/instrumentation/host"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/contrib/instrumentation/runtime"
"go.opentelemetry.io/otel"

"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/metric"
sdkMetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
Expand Down Expand Up @@ -83,8 +82,8 @@ func initGlobalMetrics(ctx context.Context, res *resource.Resource, cfg Config)
return nil, fmt.Errorf("create metric exporter: %w", err)
}

reader := metric.NewPeriodicReader(exporter, metric.WithInterval(cfg.MetricInterval))
provider := metric.NewMeterProvider(metric.WithReader(reader), metric.WithResource(res))
reader := sdkMetric.NewPeriodicReader(exporter, sdkMetric.WithInterval(cfg.MetricInterval))
provider := sdkMetric.NewMeterProvider(sdkMetric.WithReader(reader), sdkMetric.WithResource(res))
otel.SetMeterProvider(provider)

return func() error {
Expand All @@ -107,6 +106,10 @@ func initGlobalTracer(ctx context.Context, res *resource.Resource, cfg Config) (
return nil, fmt.Errorf("create trace exporter: %w", err)
}

if err != nil {
return nil, fmt.Errorf("create stdout trace exporter: %w", err)
}

tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithResource(res),
Expand All @@ -127,14 +130,3 @@ func initGlobalTracer(ctx context.Context, res *resource.Resource, cfg Config) (
return nil
}, nil
}

func NewHttpClient(name string) *http.Client {
opts := []otelhttp.Option{
otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {
return fmt.Sprintf("%s %s", name, operation)
}),
}
return &http.Client{
Transport: otelhttp.NewTransport(http.DefaultTransport, opts...),
}
}
35 changes: 35 additions & 0 deletions pkg/opentelemetry/otelhttpclient/annotations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package otelhttpclient

import (
"context"
"net/http"

"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/attribute"
)

type labelerContextKeyType int

const lablelerContextKey labelerContextKeyType = 0

// AnnotateRequest adds telemetry related annotations to request context and returns.
// The request context on the returned request should be retained.
// Ensure `route` is a route template and not actual URL to prevent high cardinality
// on the metrics.
func AnnotateRequest(req *http.Request, route string) *http.Request {
ctx := req.Context()

l := &otelhttp.Labeler{}
l.Add(attribute.String(attributeHTTPRoute, route))

return req.WithContext(context.WithValue(ctx, lablelerContextKey, l))
}

// LabelerFromContext returns the labeler annotation from the context if exists.
func LabelerFromContext(ctx context.Context) (*otelhttp.Labeler, bool) {
l, ok := ctx.Value(lablelerContextKey).(*otelhttp.Labeler)
if !ok {
l = &otelhttp.Labeler{}
}
return l, ok
}
23 changes: 23 additions & 0 deletions pkg/opentelemetry/otelhttpclient/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package otelhttpclient

import (
"net/http"
)

func New(name string, client *http.Client) *http.Client {
if client == nil {
return NewClient(name)
}
return NewFromClient(client, name)
}

func NewClient(name string) *http.Client {
return &http.Client{
Transport: NewHTTPTransport(nil, name),
}
}

func NewFromClient(httpClient *http.Client, name string) *http.Client {
lifosmin marked this conversation as resolved.
Show resolved Hide resolved
httpClient.Transport = NewHTTPTransport(httpClient.Transport, name)
return httpClient
}
Loading
Loading