Add tracing

This commit is contained in:
2023-10-23 14:23:19 +02:00
parent 1510a746ec
commit 746fb73d2f
7 changed files with 152 additions and 14 deletions

View File

@@ -1,10 +1,13 @@
package server
import (
"fmt"
"net/http"
"time"
"github.com/go-chi/chi/v5/middleware"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/trace"
)
func (s *Server) MiddlewareLogging(next http.Handler) http.Handler {
@@ -28,3 +31,14 @@ func (s *Server) MiddlewareLogging(next http.Handler) http.Handler {
}
return http.HandlerFunc(fn)
}
func (s *Server) MiddlewareTracing(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
span := trace.SpanFromContext(r.Context())
span.AddEvent("event")
h := otelhttp.NewHandler(next, fmt.Sprintf("%s %s", r.Method, r.URL.Path))
h.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}

View File

@@ -1,6 +1,7 @@
package server
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@@ -19,6 +20,13 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/nats-io/nats.go"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
"go.opentelemetry.io/otel/trace"
)
const DefaultTokenDuration time.Duration = 24 * time.Hour
@@ -38,10 +46,17 @@ type Server struct {
func NewServer(config *Config) (*Server, error) {
srv := &Server{}
tp, err := tracerProvider("jaeger:4318")
if err != nil {
return nil, err
}
otel.SetTracerProvider(tp)
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(srv.MiddlewareLogging)
r.Use(srv.MiddlewareTracing)
r.Get("/key", srv.PubkeyHandler)
r.Post("/{id}/token", srv.TokenHandler)
@@ -79,6 +94,26 @@ func NewServer(config *Config) (*Server, error) {
return srv, nil
}
func tracerProvider(url string) (*sdktrace.TracerProvider, error) {
exp, err := otlptracehttp.New(context.Background(), otlptracehttp.WithEndpoint(url), otlptracehttp.WithInsecure())
if err != nil {
return nil, err
}
res := resource.NewWithAttributes(semconv.SchemaURL,
semconv.ServiceName("mf-auth"),
semconv.ServiceVersion(auth.Version),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exp, sdktrace.WithBatchTimeout(time.Second)),
sdktrace.WithResource(res),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
return tp, nil
}
func InfoHandler(w http.ResponseWriter, r *http.Request) {
enc := json.NewEncoder(w)
@@ -96,6 +131,9 @@ func WriteError(w http.ResponseWriter, response auth.ErrorResponse) {
}
func (s *Server) PubkeyHandler(w http.ResponseWriter, r *http.Request) {
span := trace.SpanFromContext(r.Context())
span.AddEvent("Start marshalling public key.")
enc := json.NewEncoder(w)
key, err := x509.MarshalPKIXPublicKey(s.signingKey.Public())
if err != nil {
@@ -106,6 +144,7 @@ func (s *Server) PubkeyHandler(w http.ResponseWriter, r *http.Request) {
})
return
}
span.AddEvent("Finished marshalling public key.")
response := auth.PubkeyResponse{
PubKey: key,
}
@@ -114,6 +153,7 @@ func (s *Server) PubkeyHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) TokenHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
decoder := json.NewDecoder(r.Body)
defer r.Body.Close()
@@ -135,7 +175,7 @@ func (s *Server) TokenHandler(w http.ResponseWriter, r *http.Request) {
return
}
if err := s.userClient.VerifyUserPassword(userIdentifier, request.Password); err != nil {
if err := s.userClient.VerifyUserPassword(ctx, userIdentifier, request.Password); err != nil {
WriteError(w, auth.ErrorResponse{
Status: http.StatusUnauthorized,
Message: fmt.Sprintf("Unable to verify password: %s", err),
@@ -143,7 +183,7 @@ func (s *Server) TokenHandler(w http.ResponseWriter, r *http.Request) {
return
}
u, err := s.userClient.GetUser(userIdentifier)
u, err := s.userClient.GetUser(ctx, userIdentifier)
if err != nil {
WriteError(w, auth.ErrorResponse{
Status: http.StatusUnauthorized,

View File

@@ -9,20 +9,31 @@ import (
"time"
"git.t-juice.club/microfilm/users"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
)
type UserClient struct {
BaseURL string
client *http.Client
}
const defaultTimeout time.Duration = 5 * time.Second
func NewUserClient(baseurl string) *UserClient {
return &UserClient{BaseURL: baseurl}
return &UserClient{
BaseURL: baseurl,
client: &http.Client{
Transport: otelhttp.NewTransport(http.DefaultTransport),
},
}
}
func (c *UserClient) VerifyUserPassword(username, password string) error {
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
func (c *UserClient) VerifyUserPassword(ctx context.Context, username, password string) error {
ctx, span := otel.GetTracerProvider().Tracer("").Start(ctx, "verify-user-password")
defer span.End()
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
url := fmt.Sprintf("%s/%s/verify", c.BaseURL, username)
@@ -42,12 +53,11 @@ func (c *UserClient) VerifyUserPassword(username, password string) error {
return err
}
client := http.Client{}
resp, err := client.Do(req)
resp, err := c.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("authentication failed")
@@ -56,10 +66,13 @@ func (c *UserClient) VerifyUserPassword(username, password string) error {
return nil
}
func (c *UserClient) GetUser(identifier string) (users.User, error) {
func (c *UserClient) GetUser(ctx context.Context, identifier string) (users.User, error) {
var u users.User
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
ctx, span := otel.GetTracerProvider().Tracer("").Start(ctx, "get-user")
defer span.End()
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
url := fmt.Sprintf("%s/%s", c.BaseURL, identifier)
@@ -69,9 +82,7 @@ func (c *UserClient) GetUser(identifier string) (users.User, error) {
return u, err
}
client := http.Client{}
resp, err := client.Do(req)
resp, err := c.client.Do(req)
if err != nil {
return u, err
}