From 540408545ac8f9acef76a18f0c54d7e28b52387b Mon Sep 17 00:00:00 2001 From: Rahim Rahman Date: Thu, 16 Jan 2025 11:03:41 -0700 Subject: [PATCH] feat(MM-61865): Add mobile client content load network metrics (#29601) * MM-61865: Add mobile client content load network metrics * added new common label * renaming from MobileClientContentLoad to MobileClientNetworkRequests * content_load_group => network_request_group * refactor more NetworkRequest-* changes * replace contentLoadGroup to networkRequestGroup * new metrics elapsedTime * Refactor urlCount to totalRequests * add averageSpeed metric * replace contentLoadGroup with networkRequestGroup * use h.Labels vs commonLabels for network_request_group * add agent * add effective latency metrics * add total parallel requests & total sequential * mocks generated by mockery * did a bit of cleanup and sorting * formatting * updated the AcceptedNetworkRequestGroups * cleanup and sorting --- server/channels/app/metrics.go | 45 ++++++ server/einterfaces/metrics.go | 9 ++ server/einterfaces/mocks/MetricsInterface.go | 45 ++++++ server/enterprise/metrics/metrics.go | 162 ++++++++++++++++++- server/public/model/metrics.go | 40 ++++- 5 files changed, 289 insertions(+), 12 deletions(-) diff --git a/server/channels/app/metrics.go b/server/channels/app/metrics.go index 998413cdab..eb316b29d5 100644 --- a/server/channels/app/metrics.go +++ b/server/channels/app/metrics.go @@ -100,6 +100,51 @@ func (a *App) RegisterPerformanceReport(rctx request.CTX, report *model.Performa case model.MobileClientTeamSwitchDuration: a.Metrics().ObserveMobileClientTeamSwitchDuration(commonLabels["platform"], h.Value/1000) + case model.MobileClientNetworkRequestsAverageSpeed: + a.Metrics().ObserveMobileClientNetworkRequestsAverageSpeed(commonLabels["platform"], + commonLabels["agent"], + h.Labels["network_request_group"], + h.Value) + case model.MobileClientNetworkRequestsEffectiveLatency: + a.Metrics().ObserveMobileClientNetworkRequestsEffectiveLatency(commonLabels["platform"], + commonLabels["agent"], + h.Labels["network_request_group"], + h.Value) + case model.MobileClientNetworkRequestsElapsedTime: + a.Metrics().ObserveMobileClientNetworkRequestsElapsedTime(commonLabels["platform"], + commonLabels["agent"], + h.Labels["network_request_group"], + h.Value) + case model.MobileClientNetworkRequestsLatency: + a.Metrics().ObserveMobileClientNetworkRequestsLatency(commonLabels["platform"], + commonLabels["agent"], + h.Labels["network_request_group"], + h.Value) + case model.MobileClientNetworkRequestsTotalCompressedSize: + a.Metrics().ObserveMobileClientNetworkRequestsTotalCompressedSize(commonLabels["platform"], + commonLabels["agent"], + h.Labels["network_request_group"], + h.Value) + case model.MobileClientNetworkRequestsTotalParallelRequests: + a.Metrics().ObserveMobileClientNetworkRequestsTotalParallelRequests(commonLabels["platform"], + commonLabels["agent"], + h.Labels["network_request_group"], + h.Value) + case model.MobileClientNetworkRequestsTotalRequests: + a.Metrics().ObserveMobileClientNetworkRequestsTotalRequests(commonLabels["platform"], + commonLabels["agent"], + h.Labels["network_request_group"], + h.Value) + case model.MobileClientNetworkRequestsTotalSequentialRequests: + a.Metrics().ObserveMobileClientNetworkRequestsTotalSequentialRequests(commonLabels["platform"], + commonLabels["agent"], + h.Labels["network_request_group"], + h.Value) + case model.MobileClientNetworkRequestsTotalSize: + a.Metrics().ObserveMobileClientNetworkRequestsTotalSize(commonLabels["platform"], + commonLabels["agent"], + h.Labels["network_request_group"], + h.Value) case model.DesktopClientCPUUsage: a.Metrics().ObserveDesktopCpuUsage(commonLabels["platform"], commonLabels["desktop_app_version"], h.Labels["process"], h.Value) case model.DesktopClientMemoryUsage: diff --git a/server/einterfaces/metrics.go b/server/einterfaces/metrics.go index 3ad5807088..bd2930899a 100644 --- a/server/einterfaces/metrics.go +++ b/server/einterfaces/metrics.go @@ -121,6 +121,15 @@ type MetricsInterface interface { ObserveMobileClientLoadDuration(platform string, elapsed float64) ObserveMobileClientChannelSwitchDuration(platform string, elapsed float64) ObserveMobileClientTeamSwitchDuration(platform string, elapsed float64) + ObserveMobileClientNetworkRequestsAverageSpeed(platform, agent, networkRequestGroup string, speed float64) + ObserveMobileClientNetworkRequestsEffectiveLatency(platform, agent, networkRequestGroup string, latency float64) + ObserveMobileClientNetworkRequestsElapsedTime(platform, agent, networkRequestGroup string, elapsedTime float64) + ObserveMobileClientNetworkRequestsLatency(platform, agent, networkRequestGroup string, latency float64) + ObserveMobileClientNetworkRequestsTotalCompressedSize(platform, agent, networkRequestGroup string, size float64) + ObserveMobileClientNetworkRequestsTotalParallelRequests(platform, agent, networkRequestGroup string, count float64) + ObserveMobileClientNetworkRequestsTotalRequests(platform, agent, networkRequestGroup string, count float64) + ObserveMobileClientNetworkRequestsTotalSequentialRequests(platform, agent, networkRequestGroup string, count float64) + ObserveMobileClientNetworkRequestsTotalSize(platform, agent, networkRequestGroup string, size float64) ClearMobileClientSessionMetadata() ObserveMobileClientSessionMetadata(version string, platform string, value float64, notificationDisabled string) ObserveDesktopCpuUsage(platform, version, process string, usage float64) diff --git a/server/einterfaces/mocks/MetricsInterface.go b/server/einterfaces/mocks/MetricsInterface.go index ced067962f..95c2b6462f 100644 --- a/server/einterfaces/mocks/MetricsInterface.go +++ b/server/einterfaces/mocks/MetricsInterface.go @@ -403,6 +403,51 @@ func (_m *MetricsInterface) ObserveMobileClientLoadDuration(platform string, ela _m.Called(platform, elapsed) } +// ObserveMobileClientNetworkRequestsAverageSpeed provides a mock function with given fields: platform, agent, networkRequestGroup, speed +func (_m *MetricsInterface) ObserveMobileClientNetworkRequestsAverageSpeed(platform string, agent string, networkRequestGroup string, speed float64) { + _m.Called(platform, agent, networkRequestGroup, speed) +} + +// ObserveMobileClientNetworkRequestsEffectiveLatency provides a mock function with given fields: platform, agent, networkRequestGroup, latency +func (_m *MetricsInterface) ObserveMobileClientNetworkRequestsEffectiveLatency(platform string, agent string, networkRequestGroup string, latency float64) { + _m.Called(platform, agent, networkRequestGroup, latency) +} + +// ObserveMobileClientNetworkRequestsElapsedTime provides a mock function with given fields: platform, agent, networkRequestGroup, elapsedTime +func (_m *MetricsInterface) ObserveMobileClientNetworkRequestsElapsedTime(platform string, agent string, networkRequestGroup string, elapsedTime float64) { + _m.Called(platform, agent, networkRequestGroup, elapsedTime) +} + +// ObserveMobileClientNetworkRequestsLatency provides a mock function with given fields: platform, agent, networkRequestGroup, latency +func (_m *MetricsInterface) ObserveMobileClientNetworkRequestsLatency(platform string, agent string, networkRequestGroup string, latency float64) { + _m.Called(platform, agent, networkRequestGroup, latency) +} + +// ObserveMobileClientNetworkRequestsTotalCompressedSize provides a mock function with given fields: platform, agent, networkRequestGroup, size +func (_m *MetricsInterface) ObserveMobileClientNetworkRequestsTotalCompressedSize(platform string, agent string, networkRequestGroup string, size float64) { + _m.Called(platform, agent, networkRequestGroup, size) +} + +// ObserveMobileClientNetworkRequestsTotalParallelRequests provides a mock function with given fields: platform, agent, networkRequestGroup, count +func (_m *MetricsInterface) ObserveMobileClientNetworkRequestsTotalParallelRequests(platform string, agent string, networkRequestGroup string, count float64) { + _m.Called(platform, agent, networkRequestGroup, count) +} + +// ObserveMobileClientNetworkRequestsTotalRequests provides a mock function with given fields: platform, agent, networkRequestGroup, count +func (_m *MetricsInterface) ObserveMobileClientNetworkRequestsTotalRequests(platform string, agent string, networkRequestGroup string, count float64) { + _m.Called(platform, agent, networkRequestGroup, count) +} + +// ObserveMobileClientNetworkRequestsTotalSequentialRequests provides a mock function with given fields: platform, agent, networkRequestGroup, count +func (_m *MetricsInterface) ObserveMobileClientNetworkRequestsTotalSequentialRequests(platform string, agent string, networkRequestGroup string, count float64) { + _m.Called(platform, agent, networkRequestGroup, count) +} + +// ObserveMobileClientNetworkRequestsTotalSize provides a mock function with given fields: platform, agent, networkRequestGroup, size +func (_m *MetricsInterface) ObserveMobileClientNetworkRequestsTotalSize(platform string, agent string, networkRequestGroup string, size float64) { + _m.Called(platform, agent, networkRequestGroup, size) +} + // ObserveMobileClientSessionMetadata provides a mock function with given fields: version, platform, value, notificationDisabled func (_m *MetricsInterface) ObserveMobileClientSessionMetadata(version string, platform string, value float64, notificationDisabled string) { _m.Called(version, platform, value, notificationDisabled) diff --git a/server/enterprise/metrics/metrics.go b/server/enterprise/metrics/metrics.go index 309c601bcc..52156a7a0e 100644 --- a/server/enterprise/metrics/metrics.go +++ b/server/enterprise/metrics/metrics.go @@ -216,10 +216,19 @@ type MetricsInterfaceImpl struct { ClientRHSLoadDuration *prometheus.HistogramVec ClientGlobalThreadsLoadDuration *prometheus.HistogramVec - MobileClientLoadDuration *prometheus.HistogramVec - MobileClientChannelSwitchDuration *prometheus.HistogramVec - MobileClientTeamSwitchDuration *prometheus.HistogramVec - MobileClientSessionMetadataGauge *prometheus.GaugeVec + MobileClientLoadDuration *prometheus.HistogramVec + MobileClientChannelSwitchDuration *prometheus.HistogramVec + MobileClientTeamSwitchDuration *prometheus.HistogramVec + MobileClientSessionMetadataGauge *prometheus.GaugeVec + MobileClientNetworkRequestsTotalCompressedSize *prometheus.HistogramVec + MobileClientNetworkRequestsTotalRequests *prometheus.HistogramVec + MobileClientNetworkRequestsTotalParallelRequests *prometheus.HistogramVec + MobileClientNetworkRequestsTotalSequentialRequests *prometheus.HistogramVec + MobileClientNetworkRequestsLatency *prometheus.HistogramVec + MobileClientNetworkRequestsTotalSize *prometheus.HistogramVec + MobileClientNetworkRequestsElapsedTime *prometheus.HistogramVec + MobileClientNetworkRequestsAverageSpeed *prometheus.HistogramVec + MobileClientNetworkRequestsEffectiveLatency *prometheus.HistogramVec DesktopClientCPUUsage *prometheus.HistogramVec DesktopClientMemoryUsage *prometheus.HistogramVec @@ -1357,7 +1366,116 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf }, []string{"platform"}, ) + + m.MobileClientNetworkRequestsAverageSpeed = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_average_speed", + Help: "Average speed of network requests in megabytes per second (MBps)", + Buckets: []float64{1000, 10000, 50000, 100000, 500000, 1000000, 5000000}, + }, + []string{"platform", "agent", "network_request_group"}, + ) + + m.MobileClientNetworkRequestsEffectiveLatency = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_effective_latency", + Help: "Effective latency of network requests in seconds", + Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + }, + []string{"platform", "agent", "network_request_group"}, + ) + + m.MobileClientNetworkRequestsElapsedTime = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_elapsed_time", + Help: "Total elapsed time of network requests in seconds", + Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + }, + []string{"platform", "agent", "network_request_group"}, + ) + + m.MobileClientNetworkRequestsLatency = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_latency", + Help: "Latency of network requests in seconds", + Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + }, + []string{"platform", "agent", "network_request_group"}, + ) + + m.MobileClientNetworkRequestsTotalCompressedSize = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_compressed_size", + Help: "Total compressed size of network requests in bytes", + Buckets: []float64{0.1, 0.5, 1, 2, 5, 10, 20, 50}, + }, + []string{"platform", "agent", "network_request_group"}, + ) + + m.MobileClientNetworkRequestsTotalParallelRequests = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_parallel_requests", + Help: "Total number of parallel network requests made", + Buckets: []float64{1, 2, 5, 10, 20, 50, 100}, + }, + []string{"platform", "agent", "network_request_group"}, + ) + + m.MobileClientNetworkRequestsTotalRequests = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_requests", + Help: "Total number of network requests made", + Buckets: []float64{1, 2, 5, 10, 20, 50, 100}, + }, + []string{"platform", "agent", "network_request_group"}, + ) + + m.MobileClientNetworkRequestsTotalSequentialRequests = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_sequential_requests", + Help: "Total number of sequential network requests made", + Buckets: []float64{1, 2, 5, 10, 20, 50, 100}, + }, + []string{"platform", "agent", "network_request_group"}, + ) + + m.MobileClientNetworkRequestsTotalSize = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_size", + Help: "Total uncompressed size of network requests in bytes", + Buckets: []float64{1000, 10000, 50000, 100000, 500000, 1000000, 5000000}, + }, + []string{"platform", "agent", "network_request_group"}, + ) + m.Registry.MustRegister(m.MobileClientLoadDuration) + m.Registry.MustRegister(m.MobileClientNetworkRequestsAverageSpeed) + m.Registry.MustRegister(m.MobileClientNetworkRequestsEffectiveLatency) + m.Registry.MustRegister(m.MobileClientNetworkRequestsElapsedTime) + m.Registry.MustRegister(m.MobileClientNetworkRequestsLatency) + m.Registry.MustRegister(m.MobileClientNetworkRequestsTotalCompressedSize) + m.Registry.MustRegister(m.MobileClientNetworkRequestsTotalParallelRequests) + m.Registry.MustRegister(m.MobileClientNetworkRequestsTotalRequests) + m.Registry.MustRegister(m.MobileClientNetworkRequestsTotalSequentialRequests) + m.Registry.MustRegister(m.MobileClientNetworkRequestsTotalSize) m.MobileClientChannelSwitchDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ @@ -1956,6 +2074,42 @@ func (mi *MetricsInterfaceImpl) ObserveMobileClientTeamSwitchDuration(platform s mi.MobileClientTeamSwitchDuration.With(prometheus.Labels{"platform": platform}).Observe(elapsed) } +func (mi *MetricsInterfaceImpl) ObserveMobileClientNetworkRequestsTotalCompressedSize(platform, agent, networkRequestGroup string, size float64) { + mi.MobileClientNetworkRequestsTotalCompressedSize.With(prometheus.Labels{"platform": platform, "agent": agent, "network_request_group": networkRequestGroup}).Observe(size) +} + +func (mi *MetricsInterfaceImpl) ObserveMobileClientNetworkRequestsTotalRequests(platform, agent, networkRequestGroup string, count float64) { + mi.MobileClientNetworkRequestsTotalRequests.With(prometheus.Labels{"platform": platform, "agent": agent, "network_request_group": networkRequestGroup}).Observe(count) +} + +func (mi *MetricsInterfaceImpl) ObserveMobileClientNetworkRequestsTotalParallelRequests(platform, agent, networkRequestGroup string, count float64) { + mi.MobileClientNetworkRequestsTotalParallelRequests.With(prometheus.Labels{"platform": platform, "agent": agent, "network_request_group": networkRequestGroup}).Observe(count) +} + +func (mi *MetricsInterfaceImpl) ObserveMobileClientNetworkRequestsTotalSequentialRequests(platform, agent, networkRequestGroup string, count float64) { + mi.MobileClientNetworkRequestsTotalSequentialRequests.With(prometheus.Labels{"platform": platform, "agent": agent, "network_request_group": networkRequestGroup}).Observe(count) +} + +func (mi *MetricsInterfaceImpl) ObserveMobileClientNetworkRequestsLatency(platform, agent, networkRequestGroup string, latency float64) { + mi.MobileClientNetworkRequestsLatency.With(prometheus.Labels{"platform": platform, "agent": agent, "network_request_group": networkRequestGroup}).Observe(latency) +} + +func (mi *MetricsInterfaceImpl) ObserveMobileClientNetworkRequestsTotalSize(platform, agent, networkRequestGroup string, size float64) { + mi.MobileClientNetworkRequestsTotalSize.With(prometheus.Labels{"platform": platform, "agent": agent, "network_request_group": networkRequestGroup}).Observe(size) +} + +func (mi *MetricsInterfaceImpl) ObserveMobileClientNetworkRequestsElapsedTime(platform, agent, networkRequestGroup string, elapsedTime float64) { + mi.MobileClientNetworkRequestsElapsedTime.With(prometheus.Labels{"platform": platform, "agent": agent, "network_request_group": networkRequestGroup}).Observe(elapsedTime) +} + +func (mi *MetricsInterfaceImpl) ObserveMobileClientNetworkRequestsAverageSpeed(platform, agent, networkRequestGroup string, speed float64) { + mi.MobileClientNetworkRequestsAverageSpeed.With(prometheus.Labels{"platform": platform, "agent": agent, "network_request_group": networkRequestGroup}).Observe(speed) +} + +func (mi *MetricsInterfaceImpl) ObserveMobileClientNetworkRequestsEffectiveLatency(platform, agent, networkRequestGroup string, latency float64) { + mi.MobileClientNetworkRequestsEffectiveLatency.With(prometheus.Labels{"platform": platform, "agent": agent, "network_request_group": networkRequestGroup}).Observe(latency) +} + func (mi *MetricsInterfaceImpl) ObserveMobileClientSessionMetadata(version, platform string, value float64, notificationDisabled string) { mi.MobileClientSessionMetadataGauge.With(prometheus.Labels{"version": version, "platform": platform, "notifications_disabled": notificationDisabled}).Set(value) } diff --git a/server/public/model/metrics.go b/server/public/model/metrics.go index 0fe3aab276..7397af8302 100644 --- a/server/public/model/metrics.go +++ b/server/public/model/metrics.go @@ -28,9 +28,18 @@ const ( ClientRHSLoadDuration MetricType = "rhs_load" ClientGlobalThreadsLoadDuration MetricType = "global_threads_load" - MobileClientLoadDuration MetricType = "mobile_load" - MobileClientChannelSwitchDuration MetricType = "mobile_channel_switch" - MobileClientTeamSwitchDuration MetricType = "mobile_team_switch" + MobileClientLoadDuration MetricType = "mobile_load" + MobileClientChannelSwitchDuration MetricType = "mobile_channel_switch" + MobileClientTeamSwitchDuration MetricType = "mobile_team_switch" + MobileClientNetworkRequestsAverageSpeed MetricType = "mobile_network_requests_average_speed" + MobileClientNetworkRequestsEffectiveLatency MetricType = "mobile_network_requests_effective_latency" + MobileClientNetworkRequestsElapsedTime MetricType = "mobile_network_requests_elapsed_time" + MobileClientNetworkRequestsLatency MetricType = "mobile_network_requests_latency" + MobileClientNetworkRequestsTotalCompressedSize MetricType = "mobile_network_requests_total_compressed_size" + MobileClientNetworkRequestsTotalParallelRequests MetricType = "mobile_network_requests_total_parallel_requests" + MobileClientNetworkRequestsTotalRequests MetricType = "mobile_network_requests_total_requests" + MobileClientNetworkRequestsTotalSequentialRequests MetricType = "mobile_network_requests_total_sequential_requests" + MobileClientNetworkRequestsTotalSize MetricType = "mobile_network_requests_total_size" DesktopClientCPUUsage MetricType = "desktop_cpu" DesktopClientMemoryUsage MetricType = "desktop_memory" @@ -56,8 +65,22 @@ var ( "modal_content", "other", ) - AcceptedTrueFalseLabels = SliceToMapKey("true", "false") - AcceptedSplashScreenOrigins = SliceToMapKey("root", "team_controller") + AcceptedTrueFalseLabels = SliceToMapKey("true", "false") + AcceptedSplashScreenOrigins = SliceToMapKey("root", "team_controller") + AcceptedNetworkRequestGroups = SliceToMapKey( + "Cold Start", + "Cold Start Deferred", + "DeepLink", + "DeepLink Deferred", + "Login", + "Login Deferred", + "Notification", + "Notification Deferred", + "Server Switch", + "Server Switch Deferred", + "WebSocket Reconnect", + "WebSocket Reconnect Deferred", + ) ) type MetricSample struct { @@ -110,9 +133,10 @@ func (r *PerformanceReport) IsValid() error { func (r *PerformanceReport) ProcessLabels() map[string]string { return map[string]string{ - "platform": processLabel(r.Labels, "platform", acceptedPlatforms, "other"), - "agent": processLabel(r.Labels, "agent", acceptedAgents, "other"), - "desktop_app_version": r.Labels["desktop_app_version"], + "platform": processLabel(r.Labels, "platform", acceptedPlatforms, "other"), + "agent": processLabel(r.Labels, "agent", acceptedAgents, "other"), + "desktop_app_version": r.Labels["desktop_app_version"], + "network_request_group": processLabel(r.Labels, "network_request_group", AcceptedNetworkRequestGroups, "Login"), } }