~james/core

core/deliberi/analytics/analytics.go -rw-r--r-- 1.9 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 16 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// Package analytics sends best-effort product events to posthog. A nil *Client is
// valid and drops every event, so callers don't check whether analytics is on.
package analytics

import (
	"log/slog"
	"time"

	"github.com/google/uuid"
	"github.com/posthog/posthog-go"
	"tangled.org/core/deliberi/config"
)

// the same namespace for every event, so an event's uuid only depends on its key
var eventNamespace = uuid.MustParse("3f6b2c8e-1d4a-4f7e-9b2a-6c5d8e9f0a1b")

type Client struct {
	ph     posthog.Client
	env    string
	logger *slog.Logger
}

// New returns nil when no api key is configured
func New(cfg config.PosthogConfig, logger *slog.Logger) *Client {
	if cfg.ApiKey == "" {
		return nil
	}
	ph, err := posthog.NewWithConfig(cfg.ApiKey, posthog.Config{Endpoint: cfg.Endpoint})
	if err != nil {
		logger.Warn("posthog client failed to start, analytics off", "err", err)
		return nil
	}
	logger.Info("posthog analytics enabled", "endpoint", cfg.Endpoint, "env", cfg.Env)
	return &Client{ph: ph, env: cfg.Env, logger: logger}
}

// Capture queues an event. key identifies the occurrence (a record version, an
// account), so a replayed event keeps its uuid and posthog drops the duplicate
func (c *Client) Capture(event, distinctID, key string, properties posthog.Properties) {
	if c == nil {
		return
	}
	if properties == nil {
		properties = posthog.NewProperties()
	}
	properties.Set("environment", c.env)
	err := c.ph.Enqueue(posthog.Capture{
		Uuid:       uuid.NewSHA1(eventNamespace, []byte(event+" "+key)).String(),
		DistinctId: distinctID,
		Event:      event,
		Timestamp:  time.Now().UTC(),
		Properties: properties,
	})
	if err != nil {
		c.logger.Warn("failed to enqueue posthog event", "event", event, "err", err)
	}
}

// Close flushes queued events
func (c *Client) Close() {
	if c == nil {
		return
	}
	if err := c.ph.Close(); err != nil {
		c.logger.Warn("closing posthog client", "err", err)
	}
}