From b220f1e1271094e83d56d5e766d3daa0da8e6230 Mon Sep 17 00:00:00 2001 From: Ahmed Rafiq Ullah Date: Sat, 20 Nov 2021 15:03:32 +0600 Subject: [PATCH 1/2] Create metric for item counts --- .../Library/LibraryManager.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 778b6225e1..403f21ac32 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -30,6 +30,8 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -50,6 +52,7 @@ using MediaBrowser.Model.Tasks; using MediaBrowser.Providers.MediaInfo; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; +using Prometheus; using Episode = MediaBrowser.Controller.Entities.TV.Episode; using EpisodeInfo = Emby.Naming.TV.EpisodeInfo; using Genre = MediaBrowser.Controller.Entities.Genre; @@ -97,6 +100,16 @@ namespace Emby.Server.Implementations.Library private bool _wizardCompleted; + private static readonly Gauge _itemCountGauge = Metrics.CreateGauge("jellyfin_library_items_total", "The number of items in the library", new[] { "item_type" }); + private static readonly string[] _metricTypes = new string[] + { + nameof(Movie), + nameof(Series), nameof(Season), nameof(Episode), + nameof(MusicArtist), nameof(MusicAlbum), nameof(MusicVideo), nameof(Audio), + nameof(Book), + nameof(PhotoAlbum), nameof(Photo) + }; + /// /// Initializes a new instance of the class. /// @@ -448,6 +461,18 @@ namespace Emby.Server.Implementations.Library _itemRepository.DeleteItem(child.Id); } + if (_metricTypes.Contains(item.GetType().Name)) + { + _itemCountGauge.WithLabels(item.GetType().Name).Dec(); + } + foreach (var child in children) + { + if (_metricTypes.Contains(child.GetType().Name)) + { + _itemCountGauge.WithLabels(child.GetType().Name).Dec(); + } + } + _memoryCache.Remove(item.Id); ReportItemRemoved(item, parent); @@ -786,6 +811,8 @@ namespace Emby.Server.Implementations.Library RegisterItem(folder); + UpdateItemCountGauge(); + return rootFolder; } @@ -1113,6 +1140,8 @@ namespace Emby.Server.Implementations.Library await RunPostScanTasks(innerProgress, cancellationToken).ConfigureAwait(false); + UpdateItemCountGauge(); + progress.Report(100); } @@ -1836,6 +1865,10 @@ namespace Emby.Server.Implementations.Library foreach (var item in items) { RegisterItem(item); + if (_metricTypes.Contains(item.GetType().Name)) + { + _itemCountGauge.WithLabels(item.GetType().Name).Inc(); + } } if (ItemAdded != null) @@ -3262,5 +3295,18 @@ namespace Emby.Server.Implementations.Library CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); } + + private void UpdateItemCountGauge() + { + foreach (var type in _metricTypes) + { + var query = new InternalItemsQuery + { + IncludeItemTypes = new[] { type } + }; + int count = GetCount(query); + _itemCountGauge.WithLabels(type).Set(count); + } + } } } From d84db4b8e4d5c7d92626b60779afb946835cbf1d Mon Sep 17 00:00:00 2001 From: Ahmed Rafiq Date: Wed, 24 Nov 2021 22:22:42 +0600 Subject: [PATCH 2/2] Move prometheus metrics to it's own class --- .../ApplicationHost.cs | 17 +++-- .../Library/LibraryManager.cs | 46 ------------ .../Metrics/PrometheusMetricsCollector.cs | 75 +++++++++++++++++++ .../Metrics/IMetricsCollector.cs | 19 +++++ 4 files changed, 104 insertions(+), 53 deletions(-) create mode 100644 Emby.Server.Implementations/Metrics/PrometheusMetricsCollector.cs create mode 100644 MediaBrowser.Controller/Metrics/IMetricsCollector.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 903c311334..68f54ee1ef 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -35,6 +35,7 @@ using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Library; using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; +using Emby.Server.Implementations.Metrics; using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Plugins; @@ -68,6 +69,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Metrics; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Persistence; @@ -102,7 +104,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Prometheus.DotNetRuntime; using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager; namespace Emby.Server.Implementations @@ -517,12 +518,6 @@ namespace Emby.Server.Implementations MigrateNetworkConfiguration(); NetManager = new NetworkManager(ConfigurationManager, LoggerFactory.CreateLogger()); - // Initialize runtime stat collection - if (ConfigurationManager.Configuration.EnableMetrics) - { - DotNetRuntimeStatsBuilder.Default().StartCollecting(); - } - var networkConfiguration = ConfigurationManager.GetNetworkConfiguration(); HttpPort = networkConfiguration.HttpServerPortNumber; HttpsPort = networkConfiguration.HttpsPortNumber; @@ -661,6 +656,8 @@ namespace Emby.Server.Implementations serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddSingleton(); + + serviceCollection.AddSingleton(); } /// @@ -680,6 +677,12 @@ namespace Emby.Server.Implementations var userDataRepo = (SqliteUserDataRepository)Resolve(); ((SqliteItemRepository)Resolve()).Initialize(userDataRepo, Resolve()); + // Initialize runtime stat collection + if (ConfigurationManager.Configuration.EnableMetrics) + { + Resolve().Initialize(); + } + FindParts(); } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 403f21ac32..778b6225e1 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -30,8 +30,6 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -52,7 +50,6 @@ using MediaBrowser.Model.Tasks; using MediaBrowser.Providers.MediaInfo; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -using Prometheus; using Episode = MediaBrowser.Controller.Entities.TV.Episode; using EpisodeInfo = Emby.Naming.TV.EpisodeInfo; using Genre = MediaBrowser.Controller.Entities.Genre; @@ -100,16 +97,6 @@ namespace Emby.Server.Implementations.Library private bool _wizardCompleted; - private static readonly Gauge _itemCountGauge = Metrics.CreateGauge("jellyfin_library_items_total", "The number of items in the library", new[] { "item_type" }); - private static readonly string[] _metricTypes = new string[] - { - nameof(Movie), - nameof(Series), nameof(Season), nameof(Episode), - nameof(MusicArtist), nameof(MusicAlbum), nameof(MusicVideo), nameof(Audio), - nameof(Book), - nameof(PhotoAlbum), nameof(Photo) - }; - /// /// Initializes a new instance of the class. /// @@ -461,18 +448,6 @@ namespace Emby.Server.Implementations.Library _itemRepository.DeleteItem(child.Id); } - if (_metricTypes.Contains(item.GetType().Name)) - { - _itemCountGauge.WithLabels(item.GetType().Name).Dec(); - } - foreach (var child in children) - { - if (_metricTypes.Contains(child.GetType().Name)) - { - _itemCountGauge.WithLabels(child.GetType().Name).Dec(); - } - } - _memoryCache.Remove(item.Id); ReportItemRemoved(item, parent); @@ -811,8 +786,6 @@ namespace Emby.Server.Implementations.Library RegisterItem(folder); - UpdateItemCountGauge(); - return rootFolder; } @@ -1140,8 +1113,6 @@ namespace Emby.Server.Implementations.Library await RunPostScanTasks(innerProgress, cancellationToken).ConfigureAwait(false); - UpdateItemCountGauge(); - progress.Report(100); } @@ -1865,10 +1836,6 @@ namespace Emby.Server.Implementations.Library foreach (var item in items) { RegisterItem(item); - if (_metricTypes.Contains(item.GetType().Name)) - { - _itemCountGauge.WithLabels(item.GetType().Name).Inc(); - } } if (ItemAdded != null) @@ -3295,18 +3262,5 @@ namespace Emby.Server.Implementations.Library CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); } - - private void UpdateItemCountGauge() - { - foreach (var type in _metricTypes) - { - var query = new InternalItemsQuery - { - IncludeItemTypes = new[] { type } - }; - int count = GetCount(query); - _itemCountGauge.WithLabels(type).Set(count); - } - } } } diff --git a/Emby.Server.Implementations/Metrics/PrometheusMetricsCollector.cs b/Emby.Server.Implementations/Metrics/PrometheusMetricsCollector.cs new file mode 100644 index 0000000000..0a50b582dd --- /dev/null +++ b/Emby.Server.Implementations/Metrics/PrometheusMetricsCollector.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Metrics; +using Prometheus; +using Prometheus.DotNetRuntime; + +namespace Emby.Server.Implementations.Metrics +{ + /// + /// Prometheus backend for metrics. + /// + public class PrometheusMetricsCollector : IMetricsCollector + { + private readonly ILibraryManager _libraryManager; + + private static readonly Gauge _itemCountGauge = Prometheus.Metrics.CreateGauge("jellyfin_library_items_total", "The number of items in the library", new[] { "item_type" }); + private static readonly string[] _metricTypes = new string[] + { + nameof(Movie), + nameof(Series), nameof(Season), nameof(Episode), + nameof(MusicArtist), nameof(MusicAlbum), nameof(MusicVideo), nameof(Audio), + nameof(Book), + nameof(PhotoAlbum), nameof(Photo) + }; + + /// + /// Initializes a new instance of the class. + /// + /// The library manager + public PrometheusMetricsCollector(ILibraryManager libraryManager) + { + _libraryManager = libraryManager; + _libraryManager.ItemAdded += IncrementItemCount; + _libraryManager.ItemRemoved += (s, e) => UpdateItemCount(); + } + + /// + public void Initialize() + { + DotNetRuntimeStatsBuilder.Default().StartCollecting(); + UpdateItemCount(); + } + + private void UpdateItemCount() + { + foreach (var type in _metricTypes) + { + var query = new InternalItemsQuery + { + IncludeItemTypes = new[] { type } + }; + int count = _libraryManager.GetCount(query); + _itemCountGauge.WithLabels(type).Set(count); + } + } + + private void IncrementItemCount(object? sender, ItemChangeEventArgs e) + { + var item = e.Item; + var typeName = item.GetType().Name; + if (_metricTypes.Contains(typeName)) + { + _itemCountGauge.WithLabels(typeName).Inc(); + } + } + } +} diff --git a/MediaBrowser.Controller/Metrics/IMetricsCollector.cs b/MediaBrowser.Controller/Metrics/IMetricsCollector.cs new file mode 100644 index 0000000000..86d2a6dd44 --- /dev/null +++ b/MediaBrowser.Controller/Metrics/IMetricsCollector.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Metrics +{ + /// + /// Interface for metric backends. + /// + public interface IMetricsCollector + { + /// + /// Initializes metrics. + /// + public void Initialize(); + } +}