Skip to content

Commit 2d4841c

Browse files
authored
feat: add login product metrics (#764)
* feat: add login product metrics * add environment configuration to posthog * address comment * return nil
1 parent e1d98b1 commit 2d4841c

9 files changed

Lines changed: 89 additions & 1 deletion

File tree

cmd/gateway/main.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/SwissDataScienceCenter/renku-gateway/internal/config"
1616
"github.com/SwissDataScienceCenter/renku-gateway/internal/db"
1717
"github.com/SwissDataScienceCenter/renku-gateway/internal/login"
18+
"github.com/SwissDataScienceCenter/renku-gateway/internal/metrics"
1819
"github.com/SwissDataScienceCenter/renku-gateway/internal/revproxy"
1920
"github.com/SwissDataScienceCenter/renku-gateway/internal/sessions"
2021
"github.com/SwissDataScienceCenter/renku-gateway/internal/tokenstore"
@@ -124,7 +125,20 @@ func main() {
124125
}
125126
revproxy.RegisterHandlers(e, gwMiddlewares...)
126127
// Initialize login server
127-
loginServer, err := login.NewLoginServer(login.WithConfig(gwConfig.Login), login.WithSessionStore(sessionStore), login.WithTokenStore(tokenStore))
128+
metricsClient, err := metrics.NewPosthogClient(gwConfig.Posthog)
129+
if err != nil {
130+
slog.Error("posthog client initializtion failed", "error", err)
131+
os.Exit(1)
132+
}
133+
if metricsClient != nil {
134+
defer metricsClient.Close()
135+
}
136+
loginServer, err := login.NewLoginServer(
137+
login.WithConfig(gwConfig.Login),
138+
login.WithSessionStore(sessionStore),
139+
login.WithTokenStore(tokenStore),
140+
login.WithMetricsClient(metricsClient),
141+
)
128142
if err != nil {
129143
slog.Error("login handlers initialization failed", "error", err)
130144
os.Exit(1)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ require (
1515
github.com/mitchellh/mapstructure v1.5.0
1616
github.com/oapi-codegen/runtime v1.1.1
1717
github.com/oklog/ulid/v2 v2.1.0
18+
github.com/posthog/posthog-go v1.3.3
1819
github.com/redis/go-redis/v9 v9.4.0
1920
github.com/spf13/viper v1.18.2
2021
github.com/stretchr/testify v1.8.4

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
151151
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
152152
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
153153
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
154+
github.com/posthog/posthog-go v1.3.3 h1:0b1JlfPDMKXGRgbTtqkSG6hsrlj8yW2tv2HS6Dn7wFk=
155+
github.com/posthog/posthog-go v1.3.3/go.mod h1:uYC2l1Yktc8E+9FAHJ9QZG4vQf/NHJPD800Hsm7DzoM=
154156
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
155157
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
156158
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=

internal/config/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type Config struct {
88
Revproxy RevproxyConfig
99
Login LoginConfig
1010
Redis RedisConfig
11+
Posthog PosthogConfig
1112
Monitoring MonitoringConfig
1213
}
1314

internal/config/other.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,10 @@ type RateLimits struct {
2929
Rate float64
3030
Burst int
3131
}
32+
33+
type PosthogConfig struct {
34+
Enabled bool
35+
ApiKey RedactedString
36+
Host string
37+
Environment string
38+
}

internal/login/login_server.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type LoginServer struct {
1515
providerStore oidc.ClientStore
1616
sessions *sessions.SessionStore
1717
tokenStore models.TokenStoreInterface
18+
metricsClient models.MetricsClientInterface
1819
}
1920

2021
func (l *LoginServer) RegisterHandlers(server *echo.Echo, commonMiddlewares ...echo.MiddlewareFunc) {
@@ -58,6 +59,13 @@ func WithTokenStore(store models.TokenStoreInterface) LoginServerOption {
5859
}
5960
}
6061

62+
func WithMetricsClient(client models.MetricsClientInterface) LoginServerOption {
63+
return func(l *LoginServer) error {
64+
l.metricsClient = client
65+
return nil
66+
}
67+
}
68+
6169
// NewLoginServer creates a new LoginServer that handles the callbacks from oauth2
6270
// and initiates the login flow for users.
6371
func NewLoginServer(options ...LoginServerOption) (*LoginServer, error) {

internal/login/login_server_routes.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,12 @@ func (l *LoginServer) nextAuthStep(
218218
slog.Info("login completed", "requestID", utils.GetRequestID(c), "appRedirectURL", url)
219219
// Save the session: ensure we save the session before sending redirects
220220
l.sessions.Save(c)
221+
// send product metrics
222+
if l.metricsClient != nil {
223+
if session, err := l.sessions.Get(c); err == nil {
224+
l.metricsClient.UserLoggedIn(session.UserID)
225+
}
226+
}
221227
return c.Redirect(http.StatusFound, url)
222228
}
223229
providerID := session.LoginSequence[0]

internal/metrics/posthog.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package metrics
2+
3+
import (
4+
"crypto/md5"
5+
"encoding/hex"
6+
7+
"github.com/SwissDataScienceCenter/renku-gateway/internal/config"
8+
"github.com/posthog/posthog-go"
9+
)
10+
11+
type PosthogMetricsClient struct {
12+
posthogClient posthog.Client
13+
}
14+
15+
func (p *PosthogMetricsClient) anonymizeUser(userId string) string {
16+
hash := md5.Sum([]byte(userId))
17+
return hex.EncodeToString(hash[:])
18+
}
19+
20+
func (p *PosthogMetricsClient) UserLoggedIn(userId string) error {
21+
return p.posthogClient.Enqueue(posthog.Capture{DistinctId: p.anonymizeUser(userId), Event: "user_logged_in"})
22+
}
23+
24+
func (p *PosthogMetricsClient) Close() {
25+
p.posthogClient.Close()
26+
}
27+
28+
func NewPosthogClient(c config.PosthogConfig) (*PosthogMetricsClient, error) {
29+
if !c.Enabled {
30+
return nil, nil
31+
}
32+
client, err := posthog.NewWithConfig(
33+
string(c.ApiKey),
34+
posthog.Config{
35+
Endpoint: c.Host,
36+
DefaultEventProperties: posthog.Properties{"environment": c.Environment},
37+
},
38+
)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
return &PosthogMetricsClient{posthogClient: client}, nil
44+
}

internal/models/metrics.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package models
2+
3+
type MetricsClientInterface interface {
4+
UserLoggedIn(userId string) error
5+
}

0 commit comments

Comments
 (0)