diff --git a/Vostok.Hosting/Components/BuildContext.cs b/Vostok.Hosting/Components/BuildContext.cs index 14f8ecce..d4d9bca1 100644 --- a/Vostok.Hosting/Components/BuildContext.cs +++ b/Vostok.Hosting/Components/BuildContext.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Vostok.ClusterConfig.Client.Abstractions; using Vostok.Commons.Collections; using Vostok.Configuration; @@ -17,7 +18,6 @@ using Vostok.Logging.Abstractions; using Vostok.Logging.Configuration; using Vostok.Logging.Console; -using Vostok.Logging.File; using Vostok.ServiceDiscovery.Abstractions; using Vostok.Tracing; using Vostok.Tracing.Abstractions; @@ -32,12 +32,14 @@ internal class BuildContext : IDisposable private readonly SubstitutableLog substitutableLog; private readonly SubstitutableTracer substitutableTracer; private readonly SubstitutableDatacenters substitutableDatacenters; + private readonly List disposables; public BuildContext() { substitutableLog = new SubstitutableLog(); substitutableTracer = new SubstitutableTracer(); substitutableDatacenters = new SubstitutableDatacenters(); + disposables = new List(); ExternalComponents = new HashSet(ByReferenceEqualityComparer.Instance); } @@ -59,7 +61,6 @@ public BuildContext() public IVostokHostingEnvironmentSetupContext EnvironmentSetupContext { get; set; } public IVostokConfigurationSetupContext ConfigurationSetupContext { get; set; } public IVostokHostExtensions HostExtensions { get; set; } - public List DisposableHostExtensions { get; set; } public HashSet ExternalComponents { get; } public Logs Logs { get; set; } @@ -75,7 +76,14 @@ public ILog Log public void SubstituteTracer((ITracer tracer, TracerSettings tracerSettings) tracer) => substitutableTracer.SubstituteWith(tracer.tracer, tracer.tracerSettings); - + + public T RegisterDisposable(T disposable) + { + if (disposable != null) + disposables.Add(disposable); + return disposable; + } + public IDatacenters Datacenters { get => substitutableDatacenters; @@ -94,8 +102,7 @@ public void Dispose() { LogDisposing("VostokHostingEnvironment"); - foreach (var hostExtension in DisposableHostExtensions ?? new List()) - TryDispose(hostExtension, $"{hostExtension.GetType().Name} extension"); + TryDisposeImplicitComponents(); TryDispose(DiagnosticsHub, "Diagnostics"); @@ -143,16 +150,26 @@ public void LogDisabled(string name, string reason) => public void LogDisposing(string componentName) => Log.ForContext().Info("Disposing of {ComponentName}..", componentName); - private void TryDispose(object component, string componentName) + private void TryDispose(object component, string componentName, bool shouldLog = true) { if (ExternalComponents.Contains(component)) return; - if (!(component is IDisposable disposable)) + if (component is not IDisposable disposable) return; - LogDisposing(componentName); + if (shouldLog) + LogDisposing(componentName); + disposable.Dispose(); } + + private void TryDisposeImplicitComponents() + { + var registeredExtensions = new HashSet(HostExtensions.GetAll().Select(x => x.Item2), ByReferenceEqualityComparer.Instance); + + foreach (var disposable in disposables ?? new List()) + TryDispose(disposable, $"{disposable.GetType().Name} extension", registeredExtensions.Contains(disposable)); + } } } \ No newline at end of file diff --git a/Vostok.Hosting/Components/Diagnostics/DiagnosticsBuilder.cs b/Vostok.Hosting/Components/Diagnostics/DiagnosticsBuilder.cs index 2f10d1f8..2db7ab59 100644 --- a/Vostok.Hosting/Components/Diagnostics/DiagnosticsBuilder.cs +++ b/Vostok.Hosting/Components/Diagnostics/DiagnosticsBuilder.cs @@ -1,6 +1,5 @@ using System; using Vostok.Commons.Helpers; -using Vostok.Datacenters; using Vostok.Hercules.Client; using Vostok.Hosting.Abstractions.Diagnostics; using Vostok.Hosting.Components.Diagnostics.HealthChecks; @@ -70,7 +69,7 @@ private DiagnosticInfo BuildDiagnosticInfo(BuildContext context, IHealthTracker info.RegisterProvider(CreateEntry(WellKnownDiagnosticInfoProvidersNames.EnvironmentInfo), new EnvironmentInfoProvider(context.Datacenters)); if (infoSettings.AddSystemMetricsInfo) - info.RegisterProvider(CreateEntry(WellKnownDiagnosticInfoProvidersNames.SystemMetrics), new SystemMetricsProvider()); + info.RegisterProvider(CreateEntry(WellKnownDiagnosticInfoProvidersNames.SystemMetrics), context.RegisterDisposable(new SystemMetricsProvider())); if (infoSettings.AddLoadedAssembliesInfo) info.RegisterProvider(CreateEntry(WellKnownDiagnosticInfoProvidersNames.LoadedAssemblies), new LoadedAssembliesProvider()); diff --git a/Vostok.Hosting/Components/Diagnostics/InfoProviders/SystemMetricsProvider.cs b/Vostok.Hosting/Components/Diagnostics/InfoProviders/SystemMetricsProvider.cs index 5d3868b9..1f9fe7a0 100644 --- a/Vostok.Hosting/Components/Diagnostics/InfoProviders/SystemMetricsProvider.cs +++ b/Vostok.Hosting/Components/Diagnostics/InfoProviders/SystemMetricsProvider.cs @@ -6,15 +6,16 @@ namespace Vostok.Hosting.Components.Diagnostics.InfoProviders { - internal class SystemMetricsProvider : IDiagnosticInfoProvider, IObserver + internal class SystemMetricsProvider : IDiagnosticInfoProvider, IObserver, IDisposable { private static readonly TimeSpan ObservationPeriod = 5.Seconds(); private readonly CurrentProcessMonitor monitor = new CurrentProcessMonitor(); + private readonly IDisposable subscription; private volatile CurrentProcessMetrics metrics = new CurrentProcessMetrics(); public SystemMetricsProvider() - => monitor.ObserveMetrics(ObservationPeriod).Subscribe(this); + => subscription = monitor.ObserveMetrics(ObservationPeriod).Subscribe(this); public object Query() { @@ -56,5 +57,11 @@ public void OnCompleted() public void OnError(Exception error) { } + + public void Dispose() + { + subscription?.Dispose(); + monitor?.Dispose(); + } } } diff --git a/Vostok.Hosting/Components/Environment/EnvironmentBuilder.cs b/Vostok.Hosting/Components/Environment/EnvironmentBuilder.cs index 55d872b5..ecdad9ef 100644 --- a/Vostok.Hosting/Components/Environment/EnvironmentBuilder.cs +++ b/Vostok.Hosting/Components/Environment/EnvironmentBuilder.cs @@ -202,7 +202,7 @@ private VostokHostingEnvironment BuildInner(BuildContext context) AnnotationsHelper.ReportLaunching(context.ApplicationIdentity, context.Metrics.Instance); if (settings.DiagnosticMetricsEnabled) - HerculesSinkMetrics.Measure(context.HerculesSink, context.Metrics, context.Log); + context.RegisterDisposable(HerculesSinkMetrics.Measure(context.HerculesSink, context.Metrics, context.Log)); if (settings.ConfigureStaticProviders) FlowingContext.Configuration.ErrorCallback = (errorMessage, error) => context.Log.ForContext(typeof(FlowingContext)).Error(error, errorMessage); @@ -275,7 +275,7 @@ private VostokHostingEnvironment BuildInner(BuildContext context) } if (settings.DiagnosticMetricsEnabled) - LogLevelMetrics.Measure(context.Logs.LogEventLevelCounterFactory.CreateCounter(), context.Metrics); + context.RegisterDisposable(LogLevelMetrics.Measure(context.Logs.LogEventLevelCounterFactory.CreateCounter(), context.Metrics)); if (settings.ConfigureStaticProviders) StaticProvidersHelper.Configure(vostokHostingEnvironment); @@ -284,7 +284,7 @@ private VostokHostingEnvironment BuildInner(BuildContext context) context.DiagnosticsHub.HealthTracker.LaunchPeriodicalChecks(vostokHostingEnvironment.ShutdownToken); if (settings.DiagnosticMetricsEnabled) - HealthCheckMetrics.Measure(context.DiagnosticsHub.HealthTracker, context.Metrics); + context.RegisterDisposable(HealthCheckMetrics.Measure(context.DiagnosticsHub.HealthTracker, context.Metrics)); return vostokHostingEnvironment; } diff --git a/Vostok.Hosting/Components/Hercules/HerculesSinkMetrics.cs b/Vostok.Hosting/Components/Hercules/HerculesSinkMetrics.cs index 7ef3131c..bcde976a 100644 --- a/Vostok.Hosting/Components/Hercules/HerculesSinkMetrics.cs +++ b/Vostok.Hosting/Components/Hercules/HerculesSinkMetrics.cs @@ -27,17 +27,15 @@ private HerculesSinkMetrics(HerculesSink herculesSink, IMetricContext context, I this.log = log.ForContext(); this.herculesSink = herculesSink; tags = context.Tags; - - context.Register(this, ScrapePeriod); } - public static void Measure(IHerculesSink herculesSink, IVostokApplicationMetrics context, ILog log) + public static IDisposable Measure(IHerculesSink herculesSink, IVostokApplicationMetrics context, ILog log) { if (!(herculesSink is HerculesSink sink)) - return; + return null; - // ReSharper disable once ObjectCreationAsStatement - new HerculesSinkMetrics(sink, context.Application.WithTag(WellKnownTagKeys.Component, "HerculesSink"), log); + var builtContext = context.Application.WithTag(WellKnownTagKeys.Component, "HerculesSink"); + return builtContext.Register(new HerculesSinkMetrics(sink, builtContext, log), ScrapePeriod); } public IEnumerable Scrape(DateTimeOffset timestamp) diff --git a/Vostok.Hosting/Components/HostExtensions/HostExtensionsBuilder.cs b/Vostok.Hosting/Components/HostExtensions/HostExtensionsBuilder.cs index 507df374..b315dfe3 100644 --- a/Vostok.Hosting/Components/HostExtensions/HostExtensionsBuilder.cs +++ b/Vostok.Hosting/Components/HostExtensions/HostExtensionsBuilder.cs @@ -42,7 +42,7 @@ public void Build(BuildContext context, IVostokHostingEnvironment environment) builderCustomization.Customize(this); - context.DisposableHostExtensions = disposable; + disposable.ForEach(x => context.RegisterDisposable(x)); context.HostExtensions = HostExtensions; } diff --git a/Vostok.Hosting/Components/Log/LogLevelMetrics.cs b/Vostok.Hosting/Components/Log/LogLevelMetrics.cs index 0c32ca3c..bb9f8745 100644 --- a/Vostok.Hosting/Components/Log/LogLevelMetrics.cs +++ b/Vostok.Hosting/Components/Log/LogLevelMetrics.cs @@ -11,21 +11,18 @@ internal class LogLevelMetrics { private readonly LogEventLevelCounter counter; - public LogLevelMetrics(LogEventLevelCounter counter, IMetricContext context) + private LogLevelMetrics(LogEventLevelCounter counter) { this.counter = counter; - - context.CreateMultiFuncGauge(ProvideMetrics); } - public static void Measure(LogEventLevelCounter counter, IVostokApplicationMetrics context) + public static IDisposable Measure(LogEventLevelCounter counter, IVostokApplicationMetrics context) { - // ReSharper disable once ObjectCreationAsStatement - new LogLevelMetrics( - counter, - context.Instance + return context.Instance .WithTag(WellKnownTagKeys.Component, "VostokLog") - .WithTag(WellKnownTagKeys.Name, "EventsByLevel")); + .WithTag(WellKnownTagKeys.Name, "EventsByLevel") + .CreateMultiFuncGauge(new LogLevelMetrics(counter).ProvideMetrics) + as IDisposable; } private IEnumerable ProvideMetrics() diff --git a/Vostok.Hosting/Components/Metrics/HealthCheckMetrics.cs b/Vostok.Hosting/Components/Metrics/HealthCheckMetrics.cs index 7add970e..6a09f558 100644 --- a/Vostok.Hosting/Components/Metrics/HealthCheckMetrics.cs +++ b/Vostok.Hosting/Components/Metrics/HealthCheckMetrics.cs @@ -17,10 +17,10 @@ private HealthCheckMetrics(IHealthTracker healthTracker, IMetricContext context) healthTracker.ObserveReports().Subscribe(this); } - public static void Measure(IHealthTracker healthTracker, IVostokApplicationMetrics context) + public static IDisposable Measure(IHealthTracker healthTracker, IVostokApplicationMetrics context) { - // ReSharper disable once ObjectCreationAsStatement - new HealthCheckMetrics(healthTracker, context.Instance.WithTag(WellKnownTagKeys.Component, "VostokHealthChecks")); + var metrics = new HealthCheckMetrics(healthTracker, context.Instance.WithTag(WellKnownTagKeys.Component, "VostokHealthChecks")); + return healthTracker.ObserveReports().Subscribe(metrics); } public void OnCompleted() diff --git a/Vostok.Hosting/Components/SystemMetrics/SystemMetricsBuilder.cs b/Vostok.Hosting/Components/SystemMetrics/SystemMetricsBuilder.cs index fc49b94d..def4749f 100644 --- a/Vostok.Hosting/Components/SystemMetrics/SystemMetricsBuilder.cs +++ b/Vostok.Hosting/Components/SystemMetrics/SystemMetricsBuilder.cs @@ -4,6 +4,7 @@ using Vostok.Hosting.Abstractions; using Vostok.Hosting.Setup; using Vostok.Metrics; +using Vostok.Metrics.System.Dns; using Vostok.Metrics.System.Gc; using Vostok.Metrics.System.Host; using Vostok.Metrics.System.Process; @@ -39,6 +40,9 @@ public void Build(BuildContext context, IVostokHostingEnvironment environment) if (RuntimeDetector.IsDotNetCore30AndNewer) RegisterGcMonitor(settings, context, processMetricsContext); + + if (RuntimeDetector.IsDotNet50AndNewer) + RegisterDnsMonitor(settings, context, processMetricsContext); RegisterProcessMonitor(settings, context, processMetricsContext); @@ -50,13 +54,14 @@ private void RegisterGcMonitor(SystemMetricsSettings settings, BuildContext cont var gcMonitor = new GarbageCollectionMonitor(); context.HostExtensions.AsMutable().Add(gcMonitor); - context.DisposableHostExtensions.Add(gcMonitor); if (settings.EnableGcEventsLogging) - context.DisposableHostExtensions.Add(gcMonitor.LogCollections(context.Log, gc => gc.Duration >= settings.GcMinimumDurationForLogging)); + context.RegisterDisposable(gcMonitor.LogCollections(context.Log, gc => gc.Duration >= settings.GcMinimumDurationForLogging)); if (settings.EnableGcEventsMetrics) - context.DisposableHostExtensions.Add(gcMonitor.ReportMetrics(metricContext)); + context.RegisterDisposable(gcMonitor.ReportMetrics(metricContext)); + + context.RegisterDisposable(gcMonitor); } private void RegisterProcessMonitor(SystemMetricsSettings settings, BuildContext context, IMetricContext metricContext) @@ -66,7 +71,7 @@ private void RegisterProcessMonitor(SystemMetricsSettings settings, BuildContext context.HostExtensions.AsMutable().Add(processMonitor); if (settings.EnableProcessMetricsLogging) - context.DisposableHostExtensions.Add(processMonitor.LogPeriodically(context.Log, settings.ProcessMetricsLoggingPeriod)); + context.RegisterDisposable(processMonitor.LogPeriodically(context.Log, settings.ProcessMetricsLoggingPeriod)); if (settings.EnableProcessMetricsReporting) { @@ -77,9 +82,11 @@ private void RegisterProcessMonitor(SystemMetricsSettings settings, BuildContext }; var collector = new CurrentProcessMetricsCollector(collectorSettings); - - collector.ReportMetrics(metricContext, settings.ProcessMetricsReportingPeriod); + context.RegisterDisposable(collector.ReportMetrics(metricContext, settings.ProcessMetricsReportingPeriod)); + context.RegisterDisposable(collector); } + + context.RegisterDisposable(processMonitor); } private void RegisterHostMonitor(SystemMetricsSettings settings, BuildContext context, IMetricContext metricContext) @@ -90,10 +97,28 @@ private void RegisterHostMonitor(SystemMetricsSettings settings, BuildContext co context.HostExtensions.AsMutable().Add(hostMonitor); if (settings.EnableHostMetricsLogging) - context.DisposableHostExtensions.Add(hostMonitor.LogPeriodically(context.Log, settings.HostMetricsLoggingPeriod)); + context.RegisterDisposable(hostMonitor.LogPeriodically(context.Log, settings.HostMetricsLoggingPeriod)); if (settings.EnableHostMetricsReporting) - new HostMetricsCollector(hostMetricsSettings).ReportMetrics(metricContext, settings.HostMetricsReportingPeriod); + { + var collector = new HostMetricsCollector(hostMetricsSettings); + context.RegisterDisposable(collector.ReportMetrics(metricContext, settings.HostMetricsReportingPeriod)); + context.RegisterDisposable(collector); + } + + context.RegisterDisposable(hostMonitor); + } + + private void RegisterDnsMonitor(SystemMetricsSettings settings, BuildContext context, IMetricContext metricContext) + { + var dnsMonitor = new DnsMonitor(); + + context.HostExtensions.AsMutable().Add(dnsMonitor); + + if (settings.EnableDnsEventsMetrics) + context.RegisterDisposable(dnsMonitor.ReportMetrics(metricContext)); + + context.RegisterDisposable(dnsMonitor); } } } diff --git a/Vostok.Hosting/Components/SystemMetrics/SystemMetricsSettings.cs b/Vostok.Hosting/Components/SystemMetrics/SystemMetricsSettings.cs index a4ff2c34..bae1a30f 100644 --- a/Vostok.Hosting/Components/SystemMetrics/SystemMetricsSettings.cs +++ b/Vostok.Hosting/Components/SystemMetrics/SystemMetricsSettings.cs @@ -12,6 +12,8 @@ public class SystemMetricsSettings public bool EnableGcEventsMetrics { get; set; } = true; + public bool EnableDnsEventsMetrics { get; set; } = true; + public bool EnableProcessMetricsLogging { get; set; } = true; public bool EnableProcessMetricsReporting { get; set; } = true;