Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ GOLANGCI_VERSION = 1.64.5
GOTESTSUM_VERSION ?= 1.12.0

PROTOC_VERSION = 29.3
PROTOC_GEN_GO_VERSION = 1.36.5
PROTOC_GEN_GO_VERSION = 1.36.6
PROTOC_GEN_GO_GRPC_VERSION = 1.5.1

KIND_VERSION = 0.22.0
Expand Down
256 changes: 91 additions & 165 deletions api/api.pb.go

Large diffs are not rendered by default.

429 changes: 145 additions & 284 deletions api/v2/api.pb.go

Large diffs are not rendered by default.

34 changes: 24 additions & 10 deletions cmd/dex/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ import (

// Config is the config format for the main application.
type Config struct {
Issuer string `json:"issuer"`
Storage Storage `json:"storage"`
Web Web `json:"web"`
Telemetry Telemetry `json:"telemetry"`
OAuth2 OAuth2 `json:"oauth2"`
GRPC GRPC `json:"grpc"`
Expiry Expiry `json:"expiry"`
Logger Logger `json:"logger"`
Issuer string `json:"issuer"`
Storage Storage `json:"storage"`
Web Web `json:"web"`
Health Health `json:"health"`
OpenTelemetry OpenTelemetry `json:"opentelemetry"`
OAuth2 OAuth2 `json:"oauth2"`
GRPC GRPC `json:"grpc"`
Expiry Expiry `json:"expiry"`
Logger Logger `json:"logger"`

Frontend server.WebConfig `json:"frontend"`

Expand Down Expand Up @@ -233,13 +234,26 @@ func (h *Headers) ToHTTPHeader() http.Header {
return header
}

// Telemetry is the config format for telemetry including the HTTP server config.
type Telemetry struct {
// Health is the config format for health checks, readiness/liveness probes, profiling, and legacy metrics (e.g., Prometheus endpoint).
type Health struct {
HTTP string `json:"http"`
// EnableProfiling makes profiling endpoints available via web interface host:port/debug/pprof/
EnableProfiling bool `json:"enableProfiling"`
}

// OpenTelemetry is the config format for OpenTelemetry instrumentation (traces, metrics, logs).
type OpenTelemetry struct {
// Enabled toggles OpenTelemetry instrumentation on/off. Defaults to false to avoid breaking changes.
Enabled bool `json:"enabled"`
// ExporterEndpoint is the OTLP collector endpoint (e.g., "localhost:4317" for gRPC).
ExporterEndpoint string `json:"exporter_endpoint"`
// ServiceName overrides the default service name ("dex").
ServiceName string `json:"service_name"`
// Sampler configures the trace sampling strategy (e.g., "always_on", "traceidratio:0.5").
Sampler string `json:"sampler"`
// Additional fields can be added here as needed, e.g., for headers, timeouts, or component-specific settings.
}

// GRPC is the config for the gRPC API.
type GRPC struct {
// The port to listen on.
Expand Down
25 changes: 20 additions & 5 deletions cmd/dex/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ func TestInvalidConfiguration(t *testing.T) {
t.Fatal("this configuration should be invalid")
}
got := err.Error()
wanted := `invalid Config:
- no issuer specified in config file
- no storage supplied in config file
- must supply a HTTP/HTTPS address to listen on`
wanted := "invalid Config:\n\t-\tno issuer specified in config file\n\t-\tno storage supplied in config file\n\t-\tmust supply a HTTP/HTTPS address to listen on"
if got != wanted {
t.Fatalf("Expected error message to be %q, got %q", wanted, got)
}
Expand All @@ -79,6 +76,15 @@ web:
headers:
Strict-Transport-Security: "max-age=31536000; includeSubDomains"

health:
http: 127.0.0.1:5557
enableProfiling: true

opentelemetry:
enabled: true
exporter_endpoint: localhost:4317
sampler: always_on

frontend:
dir: ./web
extra:
Expand Down Expand Up @@ -134,7 +140,7 @@ logger:
format: "json"

additionalFeatures: [
"ConnectorsCRUD"
"ConnectorsCRUD"
]
`)

Expand All @@ -161,6 +167,15 @@ additionalFeatures: [
StrictTransportSecurity: "max-age=31536000; includeSubDomains",
},
},
Health: Health{
HTTP: "127.0.0.1:5557",
EnableProfiling: true,
},
OpenTelemetry: OpenTelemetry{
Enabled: true,
ExporterEndpoint: "localhost:4317",
Sampler: "always_on",
},
Frontend: server.WebConfig{
Dir: "./web",
Extra: map[string]string{
Expand Down
32 changes: 23 additions & 9 deletions cmd/dex/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"os"
"strings"

"github.com/go-slog/otelslog"
otel "go.opentelemetry.io/contrib/bridges/otelslog"

"github.com/dexidp/dex/server"
)

Expand All @@ -26,24 +29,26 @@ func newLogger(level slog.Level, format string) (*slog.Logger, error) {
default:
return nil, fmt.Errorf("log format is not one of the supported values (%s): %s", strings.Join(logFormats, ", "), format)
}

return slog.New(newRequestContextHandler(handler)), nil
handler = otelslog.NewHandler(handler)
return slog.New(newRequestContextHandler(handler, otel.NewHandler("github.com/dexidp/dex/server"))), nil
}

var _ slog.Handler = requestContextHandler{}

type requestContextHandler struct {
handler slog.Handler
handler slog.Handler
otelHandler slog.Handler
}

func newRequestContextHandler(handler slog.Handler) slog.Handler {
func newRequestContextHandler(handler, otelHandler slog.Handler) slog.Handler {
return requestContextHandler{
handler: handler,
handler: handler,
otelHandler: otelHandler,
}
}

func (h requestContextHandler) Enabled(ctx context.Context, level slog.Level) bool {
return h.handler.Enabled(ctx, level)
return h.handler.Enabled(ctx, level) && h.otelHandler.Enabled(ctx, level)
}

func (h requestContextHandler) Handle(ctx context.Context, record slog.Record) error {
Expand All @@ -55,13 +60,22 @@ func (h requestContextHandler) Handle(ctx context.Context, record slog.Record) e
record.AddAttrs(slog.String(string(server.RequestKeyRequestID), v))
}

return h.handler.Handle(ctx, record)
err := h.handler.Handle(ctx, record)
if err != nil {
return err
}

err = h.otelHandler.Handle(ctx, record)
if err != nil {
return err
}
return nil
}

func (h requestContextHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return requestContextHandler{h.handler.WithAttrs(attrs)}
return requestContextHandler{h.handler.WithAttrs(attrs), h.otelHandler.WithAttrs(attrs)}
}

func (h requestContextHandler) WithGroup(name string) slog.Handler {
return requestContextHandler{h.handler.WithGroup(name)}
return requestContextHandler{h.handler.WithGroup(name), h.otelHandler.WithGroup(name)}
}
29 changes: 29 additions & 0 deletions cmd/dex/logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package main

import (
"log/slog"
"testing"

"github.com/stretchr/testify/require"
)

func TestNewLogger(t *testing.T) {
t.Run("JSON", func(t *testing.T) {
logger, err := newLogger(slog.LevelInfo, "json")
require.NoError(t, err)
require.NotEqual(t, (*slog.Logger)(nil), logger)
})

t.Run("Text", func(t *testing.T) {
logger, err := newLogger(slog.LevelError, "text")
require.NoError(t, err)
require.NotEqual(t, (*slog.Logger)(nil), logger)
})

t.Run("Unknown", func(t *testing.T) {
logger, err := newLogger(slog.LevelError, "gofmt")
require.Error(t, err)
require.Equal(t, "log format is not one of the supported values (json, text): gofmt", err.Error())
require.Equal(t, (*slog.Logger)(nil), logger)
})
}
Loading
Loading