mirror of
https://github.com/hanzo-fi/stack.git
synced 2026-10-30 20:34:43 +00:00
127 lines
2.8 KiB
Go
127 lines
2.8 KiB
Go
package analytics
|
|
|
|
import (
|
|
"context"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/formancehq/stack/libs/go-libs/logging"
|
|
"github.com/pbnjay/memory"
|
|
"github.com/segmentio/analytics-go"
|
|
)
|
|
|
|
const (
|
|
ApplicationStats = "Application stats"
|
|
|
|
VersionProperty = "version"
|
|
OSProperty = "os"
|
|
ArchProperty = "arch"
|
|
TimeZoneProperty = "tz"
|
|
CPUCountProperty = "cpuCount"
|
|
TotalMemoryProperty = "totalMemory"
|
|
)
|
|
|
|
type AppIdProvider interface {
|
|
AppID(ctx context.Context) (string, error)
|
|
}
|
|
type AppIdProviderFn func(ctx context.Context) (string, error)
|
|
|
|
func (fn AppIdProviderFn) AppID(ctx context.Context) (string, error) {
|
|
return fn(ctx)
|
|
}
|
|
|
|
type PropertiesEnricher interface {
|
|
Enrich(ctx context.Context, p analytics.Properties) error
|
|
}
|
|
type PropertiesEnricherFn func(ctx context.Context, p analytics.Properties) error
|
|
|
|
func (fn PropertiesEnricherFn) Enrich(ctx context.Context, p analytics.Properties) error {
|
|
return fn(ctx, p)
|
|
}
|
|
|
|
type heartbeat struct {
|
|
version string
|
|
interval time.Duration
|
|
client analytics.Client
|
|
stopChan chan chan struct{}
|
|
appIdProvider AppIdProvider
|
|
enrichers []PropertiesEnricher
|
|
}
|
|
|
|
func (m *heartbeat) Run(ctx context.Context) error {
|
|
|
|
enqueue := func() {
|
|
err := m.enqueue(ctx)
|
|
if err != nil {
|
|
logging.FromContext(ctx).WithFields(map[string]interface{}{
|
|
"error": err,
|
|
}).Error("enqueuing analytics")
|
|
}
|
|
}
|
|
|
|
enqueue()
|
|
for {
|
|
select {
|
|
case ch := <-m.stopChan:
|
|
ch <- struct{}{}
|
|
return nil
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(m.interval):
|
|
enqueue()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *heartbeat) Stop(ctx context.Context) error {
|
|
ch := make(chan struct{})
|
|
m.stopChan <- ch
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-ch:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (m *heartbeat) enqueue(ctx context.Context) error {
|
|
|
|
appId, err := m.appIdProvider.AppID(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tz, _ := time.Now().Local().Zone()
|
|
|
|
properties := analytics.NewProperties().
|
|
Set(VersionProperty, m.version).
|
|
Set(OSProperty, runtime.GOOS).
|
|
Set(ArchProperty, runtime.GOARCH).
|
|
Set(TimeZoneProperty, tz).
|
|
Set(CPUCountProperty, runtime.NumCPU()).
|
|
Set(TotalMemoryProperty, memory.TotalMemory()/1024/1024)
|
|
|
|
for _, enricher := range m.enrichers {
|
|
if err := enricher.Enrich(ctx, properties); err != nil {
|
|
logging.FromContext(ctx).Errorf("Enricher return error: %s", err)
|
|
}
|
|
}
|
|
|
|
return m.client.Enqueue(&analytics.Track{
|
|
AnonymousId: appId,
|
|
Event: ApplicationStats,
|
|
Properties: properties,
|
|
})
|
|
}
|
|
|
|
func newHeartbeat(appIdProvider AppIdProvider, client analytics.Client, version string, interval time.Duration, enrichers ...PropertiesEnricher) *heartbeat {
|
|
return &heartbeat{
|
|
version: version,
|
|
interval: interval,
|
|
client: client,
|
|
appIdProvider: appIdProvider,
|
|
stopChan: make(chan chan struct{}, 1),
|
|
enrichers: enrichers,
|
|
}
|
|
}
|