Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
151 lines
5.7 KiB
Go
151 lines
5.7 KiB
Go
package telemetry
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/splitio/go-split-commons/v7/dtos"
|
|
"github.com/splitio/go-split-commons/v7/service"
|
|
"github.com/splitio/go-split-commons/v7/storage"
|
|
"github.com/splitio/go-toolkit/v5/logging"
|
|
)
|
|
|
|
// RecorderSingle struct for telemetry sync
|
|
type RecorderSingle struct {
|
|
telemetryStorage storage.TelemetryStorageConsumer
|
|
telemetryRecorder service.TelemetryRecorder
|
|
splitStorage storage.SplitStorageConsumer
|
|
segmentStorage storage.SegmentStorageConsumer
|
|
logger logging.LoggerInterface
|
|
metadata dtos.Metadata
|
|
runtimeTelemetry storage.TelemetryRuntimeProducer
|
|
}
|
|
|
|
// NewTelemetrySynchronizer creates new event synchronizer for posting events
|
|
func NewTelemetrySynchronizer(
|
|
telemetryStorage storage.TelemetryStorageConsumer,
|
|
telemetryRecorder service.TelemetryRecorder,
|
|
splitStorage storage.SplitStorageConsumer,
|
|
segmentStorage storage.SegmentStorageConsumer,
|
|
logger logging.LoggerInterface,
|
|
metadata dtos.Metadata,
|
|
runtimeTelemetry storage.TelemetryRuntimeProducer,
|
|
) TelemetrySynchronizer {
|
|
return &RecorderSingle{
|
|
telemetryStorage: telemetryStorage,
|
|
telemetryRecorder: telemetryRecorder,
|
|
splitStorage: splitStorage,
|
|
segmentStorage: segmentStorage,
|
|
logger: logger,
|
|
metadata: metadata,
|
|
runtimeTelemetry: runtimeTelemetry,
|
|
}
|
|
}
|
|
|
|
func (e *RecorderSingle) buildStats() dtos.Stats {
|
|
methodLatencies := e.telemetryStorage.PopLatencies()
|
|
methodExceptions := e.telemetryStorage.PopExceptions()
|
|
lastSynchronization := e.telemetryStorage.GetLastSynchronization()
|
|
httpErrors := e.telemetryStorage.PopHTTPErrors()
|
|
httpLatencies := e.telemetryStorage.PopHTTPLatencies()
|
|
return dtos.Stats{
|
|
MethodLatencies: &methodLatencies,
|
|
MethodExceptions: &methodExceptions,
|
|
ImpressionsDropped: e.telemetryStorage.GetImpressionsStats(ImpressionsDropped),
|
|
ImpressionsDeduped: e.telemetryStorage.GetImpressionsStats(ImpressionsDeduped),
|
|
ImpressionsQueued: e.telemetryStorage.GetImpressionsStats(ImpressionsQueued),
|
|
EventsQueued: e.telemetryStorage.GetEventsStats(EventsQueued),
|
|
EventsDropped: e.telemetryStorage.GetEventsStats(EventsDropped),
|
|
LastSynchronizations: &lastSynchronization,
|
|
HTTPErrors: &httpErrors,
|
|
HTTPLatencies: &httpLatencies,
|
|
SplitCount: int64(len(e.splitStorage.SplitNames())),
|
|
SegmentCount: int64(e.splitStorage.SegmentNames().Size()),
|
|
SegmentKeyCount: e.segmentStorage.SegmentKeysCount(),
|
|
TokenRefreshes: e.telemetryStorage.PopTokenRefreshes(),
|
|
AuthRejections: e.telemetryStorage.PopAuthRejections(),
|
|
StreamingEvents: e.telemetryStorage.PopStreamingEvents(),
|
|
SessionLengthMs: e.telemetryStorage.GetSessionLength(),
|
|
Tags: e.telemetryStorage.PopTags(),
|
|
UpdatesFromSSE: e.telemetryStorage.PopUpdatesFromSSE(),
|
|
}
|
|
}
|
|
|
|
// SynchronizeStats syncs telemetry stats
|
|
func (e *RecorderSingle) SynchronizeStats() error {
|
|
stats := e.buildStats()
|
|
|
|
before := time.Now()
|
|
err := e.telemetryRecorder.RecordStats(stats, e.metadata)
|
|
if err != nil {
|
|
if httpError, ok := err.(*dtos.HTTPError); ok {
|
|
e.runtimeTelemetry.RecordSyncError(TelemetrySync, httpError.Code)
|
|
}
|
|
return err
|
|
}
|
|
e.runtimeTelemetry.RecordSyncLatency(TelemetrySync, time.Since(before))
|
|
e.runtimeTelemetry.RecordSuccessfulSync(TelemetrySync, time.Now().UTC())
|
|
return nil
|
|
}
|
|
|
|
// SynchronizeConfig syncs telemetry config
|
|
func (e *RecorderSingle) SynchronizeConfig(cfg InitConfig, timedUntilReady int64, factoryInstances map[string]int64, tags []string) {
|
|
urlOverrides := getURLOverrides(cfg.AdvancedConfig)
|
|
impressionsMode := getImpressionMode(cfg)
|
|
|
|
before := time.Now()
|
|
err := e.telemetryRecorder.RecordConfig(dtos.Config{
|
|
OperationMode: Standalone, // TODO(mredolatti: Update this! Should not be hardcoded
|
|
Storage: Memory,
|
|
ActiveFactories: int64(len(factoryInstances)),
|
|
RedundantFactories: getRedudantActiveFactories(factoryInstances),
|
|
Tags: tags,
|
|
FlagSetsTotal: cfg.FlagSetsTotal,
|
|
FlagSetsInvalid: cfg.FlagSetsInvalid,
|
|
StreamingEnabled: cfg.AdvancedConfig.StreamingEnabled,
|
|
Rates: &dtos.Rates{
|
|
Splits: int64(cfg.TaskPeriods.SplitSync),
|
|
Segments: int64(cfg.TaskPeriods.SegmentSync),
|
|
Impressions: int64(cfg.TaskPeriods.ImpressionSync),
|
|
Events: int64(cfg.TaskPeriods.EventsSync),
|
|
Telemetry: int64(cfg.TaskPeriods.TelemetrySync),
|
|
},
|
|
URLOverrides: &urlOverrides,
|
|
ImpressionsQueueSize: int64(cfg.AdvancedConfig.ImpressionsQueueSize),
|
|
EventsQueueSize: int64(cfg.AdvancedConfig.EventsQueueSize),
|
|
ImpressionsMode: impressionsMode,
|
|
ImpressionsListenerEnabled: cfg.ListenerEnabled,
|
|
HTTPProxyDetected: len(strings.TrimSpace(os.Getenv("HTTP_PROXY"))) > 0,
|
|
TimeUntilReady: timedUntilReady,
|
|
BurTimeouts: e.telemetryStorage.GetBURTimeouts(),
|
|
NonReadyUsages: e.telemetryStorage.GetNonReadyUsages(),
|
|
}, e.metadata)
|
|
if err != nil {
|
|
e.logger.Error("Could not log config data", err.Error())
|
|
if httpError, ok := err.(*dtos.HTTPError); ok {
|
|
e.runtimeTelemetry.RecordSyncError(TelemetrySync, httpError.Code)
|
|
}
|
|
return
|
|
}
|
|
e.runtimeTelemetry.RecordSyncLatency(TelemetrySync, time.Since(before))
|
|
e.runtimeTelemetry.RecordSuccessfulSync(TelemetrySync, time.Now().UTC())
|
|
}
|
|
|
|
// SynchronizeUniqueKeys syncs unique keys
|
|
func (e *RecorderSingle) SynchronizeUniqueKeys(uniques dtos.Uniques) error {
|
|
if len(uniques.Keys) < 1 {
|
|
e.logger.Debug("Unique keys list is empty, nothing to synchronize.")
|
|
return nil
|
|
}
|
|
|
|
err := e.telemetryRecorder.RecordUniqueKeys(uniques, e.metadata)
|
|
if err != nil {
|
|
e.logger.Error("Could not log unique keys", err.Error())
|
|
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|