diff --git a/Build-COSFPkgs.ps1 b/Build-COSFPkgs.ps1 index ef7eeaf4..f7de696e 100644 --- a/Build-COSFPkgs.ps1 +++ b/Build-COSFPkgs.ps1 @@ -23,11 +23,11 @@ function Build-SFPkg { try { Push-Location $scriptPath - Build-SFPkg "Microsoft.ServiceFabricApps.ClusterObserver.Linux.SelfContained.2.2.8" "$scriptPath\bin\release\ClusterObserver\linux-x64\self-contained\ClusterObserverType" - Build-SFPkg "Microsoft.ServiceFabricApps.ClusterObserver.Linux.FrameworkDependent.2.2.8" "$scriptPath\bin\release\ClusterObserver\linux-x64\framework-dependent\ClusterObserverType" + Build-SFPkg "Microsoft.ServiceFabricApps.ClusterObserver.Linux.SelfContained.2.3.0" "$scriptPath\bin\release\ClusterObserver\linux-x64\self-contained\ClusterObserverType" + Build-SFPkg "Microsoft.ServiceFabricApps.ClusterObserver.Linux.FrameworkDependent.2.3.0" "$scriptPath\bin\release\ClusterObserver\linux-x64\framework-dependent\ClusterObserverType" - Build-SFPkg "Microsoft.ServiceFabricApps.ClusterObserver.Windows.SelfContained.2.2.8" "$scriptPath\bin\release\ClusterObserver\win-x64\self-contained\ClusterObserverType" - Build-SFPkg "Microsoft.ServiceFabricApps.ClusterObserver.Windows.FrameworkDependent.2.2.8" "$scriptPath\bin\release\ClusterObserver\win-x64\framework-dependent\ClusterObserverType" + Build-SFPkg "Microsoft.ServiceFabricApps.ClusterObserver.Windows.SelfContained.2.3.0" "$scriptPath\bin\release\ClusterObserver\win-x64\self-contained\ClusterObserverType" + Build-SFPkg "Microsoft.ServiceFabricApps.ClusterObserver.Windows.FrameworkDependent.2.3.0" "$scriptPath\bin\release\ClusterObserver\win-x64\framework-dependent\ClusterObserverType" } finally { Pop-Location diff --git a/Build-SFPkgs.ps1 b/Build-SFPkgs.ps1 index b9fc7163..26e40472 100644 --- a/Build-SFPkgs.ps1 +++ b/Build-SFPkgs.ps1 @@ -23,11 +23,11 @@ function Build-SFPkg { try { Push-Location $scriptPath - Build-SFPkg "Microsoft.ServiceFabricApps.FabricObserver.Linux.SelfContained.3.2.16" "$scriptPath\bin\release\FabricObserver\linux-x64\self-contained\FabricObserverType" - Build-SFPkg "Microsoft.ServiceFabricApps.FabricObserver.Linux.FrameworkDependent.3.2.16" "$scriptPath\bin\release\FabricObserver\linux-x64\framework-dependent\FabricObserverType" + Build-SFPkg "Microsoft.ServiceFabricApps.FabricObserver.Linux.SelfContained.3.3.0" "$scriptPath\bin\release\FabricObserver\linux-x64\self-contained\FabricObserverType" + Build-SFPkg "Microsoft.ServiceFabricApps.FabricObserver.Linux.FrameworkDependent.3.3.0" "$scriptPath\bin\release\FabricObserver\linux-x64\framework-dependent\FabricObserverType" - Build-SFPkg "Microsoft.ServiceFabricApps.FabricObserver.Windows.SelfContained.3.2.16" "$scriptPath\bin\release\FabricObserver\win-x64\self-contained\FabricObserverType" - Build-SFPkg "Microsoft.ServiceFabricApps.FabricObserver.Windows.FrameworkDependent.3.2.16" "$scriptPath\bin\release\FabricObserver\win-x64\framework-dependent\FabricObserverType" + Build-SFPkg "Microsoft.ServiceFabricApps.FabricObserver.Windows.SelfContained.3.3.0" "$scriptPath\bin\release\FabricObserver\win-x64\self-contained\FabricObserverType" + Build-SFPkg "Microsoft.ServiceFabricApps.FabricObserver.Windows.FrameworkDependent.3.3.0" "$scriptPath\bin\release\FabricObserver\win-x64\framework-dependent\FabricObserverType" } finally { Pop-Location diff --git a/ClusterObserver.nuspec.template b/ClusterObserver.nuspec.template index 3ab3aea6..1a9cbf14 100644 --- a/ClusterObserver.nuspec.template +++ b/ClusterObserver.nuspec.template @@ -2,11 +2,9 @@ %PACKAGE_ID% - 2.2.8 + 2.3.0 -- *Breaking Change*: Telemetry configuration settings are now required to be overridden in ApplicationManifest.xml to support versionless, parameter-only application upgrades for telemetry settings. See [Issue 292](https://github.com/microsoft/service-fabric-observer/issues/292) for details. Just move your related settings' Value strings from Settings.xml to ApplicationManifest.xml app parameter (the names of these settings are the same). -- Bug fix in app param update for log path and max archive lifetime settings. -- Updated nuget package dependencies to latest versions. +- .NET 8 implementation of ClusterObserver. This version is built for .NET 8 and SF Runtime >= 9.1 (Self-Contained FO builds only). If you have deployed SF Runtime version >= 10.1 Cumulative Update 3.0 (CU3), then you can deploy the framework-dependent release build for the target platform (Windows or Linux). If you are not running SF Runtime version >= 10.1 CU3, then you must deploy the Self-Contained release build for the target platform (Windows or Linux). **If you can't upgrade to .NET 8 yet, then do not upgrade to this version.** Microsoft MIT @@ -15,14 +13,14 @@ icon.png conuget.md en-US - This package contains the Service Fabric ClusterObserver(CO) Application - built for .NET 6.0 and SF Runtime 9.x. CO is a highly configurable and extensible Service Fabric stateless service that monitors aggregated cluster health and emits SF entity-specific telemetry. It is designed to be run in Service Fabric Windows and Linux clusters. This package contains the entire application and can be used to build .NET Standard 2.0 observer plugins. NOTE: If you want to target .NET 6 for your plugins, then you must use Microsoft.ServiceFabricApps.FabricObserver.Extensibility.3.2.9 nuget package to build them. + This package contains the Service Fabric ClusterObserver(CO) Application - built for .NET 8.0 and SF Runtime 10.1.x. CO is a highly configurable and extensible Service Fabric stateless service that monitors aggregated cluster health and emits SF entity-specific telemetry. It is designed to be run in Service Fabric Windows and Linux clusters. This package contains the entire application and can be used to build .NET Standard 2.0/.NET6 observer plugins. NOTE: If you want to target .NET 6 for your plugins, then you must use Microsoft.ServiceFabricApps.FabricObserver.Extensibility.3.2.9 or higher nuget package to build them. - + - + https://aka.ms/sf/FabricObserver @@ -31,9 +29,9 @@ - - - + + + diff --git a/ClusterObserver/ClusterObserver.cs b/ClusterObserver/ClusterObserver.cs index 1c0eb228..1332d40c 100644 --- a/ClusterObserver/ClusterObserver.cs +++ b/ClusterObserver/ClusterObserver.cs @@ -71,11 +71,11 @@ public bool EmitWarningDetails get; set; } - public ClusterObserver(StatelessServiceContext serviceContext, bool ignoreDefaultQueryTimeout = false) + public ClusterObserver(StatelessServiceContext serviceContext, bool ignoreDefaultQueryTimeout = false) : base (null, serviceContext) { - NodeStatusDictionary = new Dictionary(); - ApplicationUpgradesCompletedStatus = new Dictionary(); + NodeStatusDictionary = []; + ApplicationUpgradesCompletedStatus = []; this.ignoreDefaultQueryTimeout = ignoreDefaultQueryTimeout; @@ -400,7 +400,7 @@ private async Task ReportApplicationUpgradeStatus(Uri appName, CancellationToken { ServiceFabricUpgradeEventData appUpgradeInfo = await FabricClientRetryHelper.ExecuteFabricActionWithRetryAsync( - () => UpgradeChecker.GetApplicationUpgradeDetailsAsync(FabricClientInstance, Token, appName), + () => UpgradeChecker.GetApplicationUpgradeDetailsAsync(FabricClientInstance, appName, Token), Token); if (appUpgradeInfo?.ApplicationUpgradeProgress == null || Token.IsCancellationRequested) @@ -534,7 +534,7 @@ private async Task ProcessApplicationHealthAsync(ApplicationHealthState appHealt appHealth.HealthEvents.Where( e => e.HealthInformation.HealthState is HealthState.Error or HealthState.Warning).ToList(); - if (!appHealthEvents.Any()) + if (appHealthEvents.Count == 0) { return; } diff --git a/ClusterObserver/ClusterObserver.csproj b/ClusterObserver/ClusterObserver.csproj index d2903107..6894bd99 100644 --- a/ClusterObserver/ClusterObserver.csproj +++ b/ClusterObserver/ClusterObserver.csproj @@ -4,19 +4,21 @@ ClusterObserver ClusterObserver Exe - net6.0 + net8.0 disable True win-x64;linux-x64 True ClusterObserver - 2.2.8 - 2.2.8 + 2.3.0 + 2.3.0 Copyright © 2024 true false ClusterObserver.Program x64 + diff --git a/ClusterObserver/ClusterObserverManager.cs b/ClusterObserver/ClusterObserverManager.cs index a9bcece2..43c99c6b 100644 --- a/ClusterObserver/ClusterObserverManager.cs +++ b/ClusterObserver/ClusterObserverManager.cs @@ -39,7 +39,7 @@ public sealed class ClusterObserverManager : IDisposable private bool appParamsUpdating; // Folks often use their own version numbers. This is for internal diagnostic telemetry. - private const string InternalVersionNumber = "2.2.8"; + private const string InternalVersionNumber = "2.3.0"; public bool EnableOperationalTelemetry { diff --git a/ClusterObserver/FabricClusterObserver.cs b/ClusterObserver/FabricClusterObserver.cs index e7384a54..27a726dd 100644 --- a/ClusterObserver/FabricClusterObserver.cs +++ b/ClusterObserver/FabricClusterObserver.cs @@ -11,19 +11,17 @@ using System.Reflection; using System.Linq; using FabricObserver.Utilities; +using FabricObserver.Observers.Utilities; +using FabricObserver.Observers.Utilities.Telemetry; namespace ClusterObserver { /// /// An instance of this class is created for each service instance by the Service Fabric runtime. /// - internal sealed class FabricClusterObserver : StatelessService + internal sealed class FabricClusterObserver(StatelessServiceContext context) : StatelessService(context) { - public FabricClusterObserver(StatelessServiceContext context) - : base(context) - { - - } + private readonly Logger logger = new("ClusterObserverService"); /// /// This is the main entry point for your service instance. @@ -68,11 +66,12 @@ private void LoadObserversFromPlugins(IServiceCollection services) } PluginLoader[] pluginLoaders = new PluginLoader[pluginDlls.Length]; - Type[] sharedTypes = { typeof(FabricObserverStartupAttribute), typeof(IFabricObserverStartup), typeof(IServiceCollection) }; + Type[] sharedTypes = [typeof(FabricObserverStartupAttribute), typeof(IFabricObserverStartup), typeof(IServiceCollection)]; + string dll = ""; for (int i = 0; i < pluginDlls.Length; ++i) { - string dll = pluginDlls[i]; + dll = pluginDlls[i]; PluginLoader loader = PluginLoader.CreateFromAssemblyFile(dll, sharedTypes, a => a.IsUnloadable = false); pluginLoaders[i] = loader; } @@ -109,8 +108,33 @@ private void LoadObserversFromPlugins(IServiceCollection services) } catch (Exception e) when (e is ArgumentException or BadImageFormatException or IOException) { + if (e is IOException) + { + string error = $"Plugin dll {dll} could not be loaded. {e.Message}"; + HealthReport healthReport = new() + { + AppName = new Uri($"{Context.CodePackageActivationContext.ApplicationName}"), + EmitLogEvent = true, + HealthMessage = error, + EntityType = EntityType.Application, + HealthReportTimeToLive = TimeSpan.FromMinutes(10), + State = System.Fabric.Health.HealthState.Warning, + Property = "ClusterObserverPluginLoadError", + SourceId = $"ClusterObserverService-{Context.NodeContext.NodeName}", + NodeName = Context.NodeContext.NodeName, + }; + + ObserverHealthReporter observerHealth = new(logger); + observerHealth.ReportHealthToServiceFabric(healthReport); + } + continue; } + catch (Exception e) when (e is not OutOfMemoryException) + { + logger.LogError($"Unhandled exception in ClusterObserverService Instance: {e.Message}"); + throw; + } } } } diff --git a/ClusterObserver/PackageRoot/Config/Settings.xml b/ClusterObserver/PackageRoot/Config/Settings.xml index efb7404c..321d7505 100644 --- a/ClusterObserver/PackageRoot/Config/Settings.xml +++ b/ClusterObserver/PackageRoot/Config/Settings.xml @@ -90,10 +90,11 @@ If you want to enable versionless, parameter-only application upgrades, then add MustOverride to the Parameters you want to be able to change without redeploying CO and add them to ApplicationManifest.xml just like for ClusterObserver. -
+
+
--> diff --git a/ClusterObserver/PackageRoot/ServiceManifest.xml b/ClusterObserver/PackageRoot/ServiceManifest.xml index 43b4e5ca..c03a7af7 100644 --- a/ClusterObserver/PackageRoot/ServiceManifest.xml +++ b/ClusterObserver/PackageRoot/ServiceManifest.xml @@ -1,6 +1,6 @@  @@ -11,7 +11,7 @@ - + ClusterObserver @@ -21,11 +21,11 @@ - + - + diff --git a/ClusterObserver/Readme.md b/ClusterObserver/Readme.md index 0cfb8c43..9e2b393e 100644 --- a/ClusterObserver/Readme.md +++ b/ClusterObserver/Readme.md @@ -1,11 +1,13 @@ -### ClusterObserver 2.2.8 -#### This version - and all subsequent versions - requires SF Runtime >= 9.0 and targets .NET 6 +### ClusterObserver 2.3.0 (.NET 8) +#### This version targets .NET 8 and SF Runtime >= 9.1. -ClusterObserver (CO) is a stateless singleton Service Fabric .NET 6 service that runs on one node in a cluster. CO observes cluster health (aggregated) +ClusterObserver (CO) is a stateless singleton Service Fabric .NET 8 service that runs on one node in a cluster. CO observes cluster health (aggregated) and sends telemetry when a cluster is in Error or Warning. CO shares a very small subset of FabricObserver's (FO) code. It is designed to be completely independent from FO sources, but lives in this repo (and SLN) because it is very useful to have both services deployed, especially for those who want cluster-level health observation and reporting in addition to the node-level user-defined resource monitoring, health event creation, and health reporting done by FO. FabricObserver is designed to generate Service Fabric health events based on user-defined resource usage Warning and Error thresholds which ClusterObserver sends to your log analytics and alerting service. +Starting with version 2.3.0, you must deploy the self-contained release package unless you are deploying to a cluster running SF Version >= 10.1 CU3 or higher, then you can deploy framework-dependent release. + By design, CO will send an Ok health state report when a cluster goes from Warning or Error state to Ok. CO only sends telemetry when something is wrong or when something that was previously wrong recovers. This limits the amount of data sent to your log analytics service. Like FabricObserver, you can implement whatever analytics backend @@ -30,7 +32,7 @@ Application Parameter Upgrade Example: ```Powershell $appName = "fabric:/ClusterObserver" -$appVersion = "2.2.8" +$appVersion = "2.3.0" $application = Get-ServiceFabricApplication -ApplicationName $appName @@ -159,7 +161,7 @@ Start-ServiceFabricApplicationUpgrade -ApplicationName $appName -ApplicationType ``` XML - + @@ -188,7 +190,7 @@ Start-ServiceFabricApplicationUpgrade -ApplicationName $appName -ApplicationType should match the Name and Version attributes of the ServiceManifest element defined in the ServiceManifest.xml file. --> - + @@ -260,8 +262,8 @@ Here is a full example of exactly what is sent in one of these telemetry events, "TaskName": "ClusterObserver", "ClusterId": "00000000-1111-1111-0000-00f00d000d", "ClusterType": "SFRP", - "COVersion": "2.2.0.960", - "Timestamp": "2022-07-12T19:02:04.4287671Z", + "COVersion": "2.3.0", + "Timestamp": "2024-06-06T19:02:04.4287671Z", "OS": "Windows" } ``` diff --git a/ClusterObserver/Utilities/UpgradeChecker.cs b/ClusterObserver/Utilities/UpgradeChecker.cs index 32379ed9..6b6d7a06 100644 --- a/ClusterObserver/Utilities/UpgradeChecker.cs +++ b/ClusterObserver/Utilities/UpgradeChecker.cs @@ -23,7 +23,7 @@ public static class UpgradeChecker /// CancellationToken /// ApplicationName (Uri) /// An instance of ServiceFabricUpgradeEventData containing ApplicationUpgradeProgress instance. - internal static async Task GetApplicationUpgradeDetailsAsync(FabricClient fabricClient, CancellationToken token, Uri app) + internal static async Task GetApplicationUpgradeDetailsAsync(FabricClient fabricClient, Uri app, CancellationToken token) { if (token.IsCancellationRequested) { diff --git a/ClusterObserverApp/ApplicationPackageRoot/ApplicationManifest.xml b/ClusterObserverApp/ApplicationPackageRoot/ApplicationManifest.xml index 41897bbd..51ae3925 100644 --- a/ClusterObserverApp/ApplicationPackageRoot/ApplicationManifest.xml +++ b/ClusterObserverApp/ApplicationPackageRoot/ApplicationManifest.xml @@ -1,5 +1,5 @@  - + @@ -28,7 +28,7 @@ - + @@ -43,7 +43,7 @@ should match the Name and Version attributes of the ServiceManifest element defined in the ServiceManifest.xml file. --> - + diff --git a/Documentation/Deployment/service-fabric-cluster-observer.json b/Documentation/Deployment/service-fabric-cluster-observer.json index 958c15c5..04cdc689 100644 --- a/Documentation/Deployment/service-fabric-cluster-observer.json +++ b/Documentation/Deployment/service-fabric-cluster-observer.json @@ -11,7 +11,7 @@ }, "applicationTypeVersionClusterObserver": { "type": "string", - "defaultValue": "2.2.8", + "defaultValue": "2.3.0", "metadata": { "description": "Provide the app version number of ClusterObserver. This must be identical to the version specified in the corresponding sfpkg." } diff --git a/Documentation/Deployment/service-fabric-cluster-observer.v2.2.8.parameters.json b/Documentation/Deployment/service-fabric-cluster-observer.v2.3.0.parameters.json similarity index 90% rename from Documentation/Deployment/service-fabric-cluster-observer.v2.2.8.parameters.json rename to Documentation/Deployment/service-fabric-cluster-observer.v2.3.0.parameters.json index a6e4ad41..25f50903 100644 --- a/Documentation/Deployment/service-fabric-cluster-observer.v2.2.8.parameters.json +++ b/Documentation/Deployment/service-fabric-cluster-observer.v2.3.0.parameters.json @@ -1,15 +1,15 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "clusterName": { - "value": "" - }, - "applicationTypeVersionClusterObserver": { - "value": "2.2.8" - }, - "packageUrlClusterObserver": { - "value": "" - } - } +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "clusterName": { + "value": "" + }, + "applicationTypeVersionClusterObserver": { + "value": "2.3.0" + }, + "packageUrlClusterObserver": { + "value": "" + } + } } \ No newline at end of file diff --git a/Documentation/Deployment/service-fabric-observer.json b/Documentation/Deployment/service-fabric-observer.json index 37e44ce1..3f2e818c 100644 --- a/Documentation/Deployment/service-fabric-observer.json +++ b/Documentation/Deployment/service-fabric-observer.json @@ -11,16 +11,16 @@ }, "applicationTypeVersionFabricObserver": { "type": "string", - "defaultValue": "3.2.16", + "defaultValue": "3.3.0", "metadata": { - "description": "Provide the app version number of FabricObserver. This must be identical to the version, 3.2.16, in the referenced sfpkg specified in packageUrlFabricObserver." + "description": "Provide the app version number of FabricObserver. This must be identical to the version, 3.3.0, in the referenced sfpkg specified in packageUrlFabricObserver." } }, "packageUrlFabricObserver": { "type": "string", "defaultValue": "", "metadata": { - "description": "This has to be a public accessible URL for the sfpkg file which contains the FabricObserver app package. Example: https://github.com/microsoft/service-fabric-observer/releases/download/[xxxxxxxx]/Microsoft.ServiceFabricApps.FabricObserver.Windows.SelfContained.3.2.16.sfpkg" + "description": "This has to be a public accessible URL for the sfpkg file which contains the FabricObserver app package. Example: https://github.com/microsoft/service-fabric-observer/releases/download/[xxxxxxxx]/Microsoft.ServiceFabricApps.FabricObserver.Windows.SelfContained.3.3.0.sfpkg" } } }, diff --git a/Documentation/Deployment/service-fabric-observer.v3.2.16.parameters.json b/Documentation/Deployment/service-fabric-observer.v3.3.0.parameters.json similarity index 93% rename from Documentation/Deployment/service-fabric-observer.v3.2.16.parameters.json rename to Documentation/Deployment/service-fabric-observer.v3.3.0.parameters.json index cc5397c6..9e7bea4c 100644 --- a/Documentation/Deployment/service-fabric-observer.v3.2.16.parameters.json +++ b/Documentation/Deployment/service-fabric-observer.v3.3.0.parameters.json @@ -6,7 +6,7 @@ "value": "" }, "applicationTypeVersionFabricObserver": { - "value": "3.2.16" + "value": "3.3.0" }, "packageUrlFabricObserver": { "value": "" diff --git a/Documentation/OperationalTelemetry.md b/Documentation/OperationalTelemetry.md index b67a7a4b..4f06ff5c 100644 --- a/Documentation/OperationalTelemetry.md +++ b/Documentation/OperationalTelemetry.md @@ -18,7 +18,7 @@ As with most of FabricObserver's application settings, you can also do this with Connect-ServiceFabricCluster ... $appParams = @{ "ObserverManagerEnableOperationalFOTelemetry" = "false"; } -Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationParameter $appParams -ApplicationTypeVersion 3.2.16 -UnMonitoredAuto +Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationParameter $appParams -ApplicationTypeVersion 3.3.0 -UnMonitoredAuto ``` @@ -44,11 +44,11 @@ Here is a full example of exactly what is sent in one of these telemetry events, "ClusterId": "00000000-1111-1111-0000-00f00d000d", "ClusterType": "SFRP", "NodeNameHash": "3e83569d4c6aad78083cd081215dafc81e5218556b6a46cb8dd2b183ed0095ad", - "FOVersion": "3.2.16", + "FOVersion": "3.3.0", "HasPlugins": "False", "SFRuntimeVersion":"9.0.1028.9590" "UpTime": "1.00:30:18.8058379", - "Timestamp": "2024-04-27T02:45:28.9827940Z", + "Timestamp": "2024-08-06T02:45:28.9827940Z", "OS": "Windows", "EnabledObserverCount": 5, "AppObserverTotalMonitoredApps": 5, diff --git a/Documentation/Plugins.md b/Documentation/Plugins.md index 87ba6969..6b3d455d 100644 --- a/Documentation/Plugins.md +++ b/Documentation/Plugins.md @@ -2,7 +2,7 @@ #### Note that starting in version 2.2.0, ClusterObserver supports the FO plugin model. So, you can build cluster-level monitoring plugins should you so desire. -1. Create a .NET 6 Library project. +1. Create a .NET 8 Library project. 2. Install the latest Microsoft.ServiceFabricApps.FabricObserver.Extensibility nupkg from https://www.nuget.org/profiles/ServiceFabricApps into your plugin project. @@ -72,5 +72,5 @@ cd C:\Users\me\source\repos\service-fabric-observer ./Build-FabricObserver ./Build-NugetPackages ``` -The output from the above commands contains FabricObserver platform-specific nupkgs and a nupkg you have to use for plugin authoring named Microsoft.ServiceFabricApps.FabricObserver.Extensibility.3.2.16.nupkg. Nuget packages will be located in +The output from the above commands contains FabricObserver platform-specific nupkgs and a nupkg you have to use for plugin authoring named Microsoft.ServiceFabricApps.FabricObserver.Extensibility.3.3.0.nupkg. Nuget packages will be located in C:\Users\me\source\repos\service-fabric-observer\bin\release\FabricObserver\Nugets. \ No newline at end of file diff --git a/Documentation/Using.md b/Documentation/Using.md index 064340b6..ae9e2641 100644 --- a/Documentation/Using.md +++ b/Documentation/Using.md @@ -708,7 +708,7 @@ $appParams = @{ "FabricSystemObserverEnabled" = "true"; "FabricSystemObserverMem Then execute the application upgrade with ```Powershell -Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationTypeVersion 3.2.16 -ApplicationParameter $appParams -Monitored -FailureAction rollback +Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationTypeVersion 3.3.0 -ApplicationParameter $appParams -Monitored -FailureAction rollback ``` **Important**: This action will overwrite previous app paramemter changes that were made in an earlier application upgrade, for example. If you want to preserve any earlier changes, then you will need to @@ -716,7 +716,7 @@ supply those parameter values again along with the new ones. You do this in the ```PowerShell $appName = "fabric:/FabricObserver" -$appVersion = "3.2.16" +$appVersion = "3.3.0" $application = Get-ServiceFabricApplication -ApplicationName $appName $appParamCollection = $application.ApplicationParameters $applicationParameterMap = @{} diff --git a/FabricObserver.Extensibility.nuspec.template b/FabricObserver.Extensibility.nuspec.template index a92a3955..1d859e5d 100644 --- a/FabricObserver.Extensibility.nuspec.template +++ b/FabricObserver.Extensibility.nuspec.template @@ -2,9 +2,9 @@ %PACKAGE_ID% - 3.2.16 + 3.3.0 -Refactored/Updated OSInfoProvider to prevent cross platform visibility compiler warnings. +This is the .NET 8 implementation of FabricObserver's Extensibility library. Use this library to build .NET 8 FabricObserver and ClusterObserver plugins. Microsoft MIT @@ -13,12 +13,12 @@ Refactored/Updated OSInfoProvider to prevent cross platform visibility compiler icon.png foextlib.md en-US - This package contains the FabricObserver Extensibility library (.NET 6) for use in building FabricObserver and ClusterObserver observer plugins in Visual Studio. Each time a new version of FabricObserver is released, a new version of this library (same as FabricObserver's version) will be released to the nuget.org gallery. Unless specified as part of a release with breaking changes that would impact building a plugin (exceedingly rare), you can use earlier versions of this library to build your plugins and they will successfully work with higher versions of FabricObserver/ClusterObserver. In other words, you do not need to rebuild your plugins each time a new version of FabricObserver or ClusterObserver is released unless you are required to, which will be made clear. + This package contains the FabricObserver Extensibility library (NET8) for use in building FabricObserver and ClusterObserver observer plugins in Visual Studio. Each time a new version of FabricObserver is released, a new version of this library (same as FabricObserver's version) will be released to the nuget.org gallery. Unless specified as part of a release with breaking changes that would impact building a plugin (exceedingly rare), you can use earlier versions of this library to build your plugins and they will successfully work with higher versions of FabricObserver/ClusterObserver. In other words, you do not need to rebuild your plugins each time a new version of FabricObserver or ClusterObserver is released unless you are required to, which will be made clear. - + @@ -35,8 +35,8 @@ Refactored/Updated OSInfoProvider to prevent cross platform visibility compiler © Microsoft Corporation. All rights reserved. - - + + diff --git a/FabricObserver.Extensibility/Extensibility/FabricObserverStartupAttribute.cs b/FabricObserver.Extensibility/Extensibility/FabricObserverStartupAttribute.cs index 5b594ca9..f91b0947 100644 --- a/FabricObserver.Extensibility/Extensibility/FabricObserverStartupAttribute.cs +++ b/FabricObserver.Extensibility/Extensibility/FabricObserverStartupAttribute.cs @@ -8,16 +8,11 @@ namespace FabricObserver { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - public sealed class FabricObserverStartupAttribute : Attribute + public sealed class FabricObserverStartupAttribute(Type startupType) : Attribute { - public FabricObserverStartupAttribute(Type startupType) - { - StartupType = startupType; - } - public Type StartupType { get; - } + } = startupType; } } diff --git a/FabricObserver.Extensibility/FabricObserver.Extensibility.csproj b/FabricObserver.Extensibility/FabricObserver.Extensibility.csproj index 9b98f503..9c148778 100644 --- a/FabricObserver.Extensibility/FabricObserver.Extensibility.csproj +++ b/FabricObserver.Extensibility/FabricObserver.Extensibility.csproj @@ -1,19 +1,20 @@  - net6.0 + net8.0 x64 FabricObserver Copyright © 2024 FabricObserver - 3.2.16 - 3.2.16 + 3.3.0 + 3.3.0 + true - + diff --git a/FabricObserver.Extensibility/ObserverBase.cs b/FabricObserver.Extensibility/ObserverBase.cs index e74128d2..57d3b682 100644 --- a/FabricObserver.Extensibility/ObserverBase.cs +++ b/FabricObserver.Extensibility/ObserverBase.cs @@ -587,11 +587,11 @@ public bool DumpWindowsServiceProcess(int processId, string procName, string met } - if (!ServiceDumpCountDictionary.ContainsKey(dumpKey)) + if (!ServiceDumpCountDictionary.TryGetValue(dumpKey, out (int DumpCount, DateTime LastDumpDate) value)) { _ = ServiceDumpCountDictionary.TryAdd(dumpKey, (0, DateTime.UtcNow)); } - else if (DateTime.UtcNow.Subtract(ServiceDumpCountDictionary[dumpKey].LastDumpDate) >= MaxDumpsTimeWindow) + else if (DateTime.UtcNow.Subtract(value.LastDumpDate) >= MaxDumpsTimeWindow) { ServiceDumpCountDictionary[dumpKey] = (0, DateTime.UtcNow); } diff --git a/FabricObserver.Extensibility/Utilities/CpuUsageProcess.cs b/FabricObserver.Extensibility/Utilities/CpuUsageProcess.cs index c59e41c0..0345ad68 100644 --- a/FabricObserver.Extensibility/Utilities/CpuUsageProcess.cs +++ b/FabricObserver.Extensibility/Utilities/CpuUsageProcess.cs @@ -69,7 +69,7 @@ public double GetCurrentCpuUsagePercentage(int procId, string procName = null, S } catch (Exception e) { - ProcessInfoProvider.ProcessInfoLogger.LogWarning($"GetCurrentCpuUsagePercentage(NET6 Process impl) failure (pid = {procId}): {e.Message}"); + ProcessInfoProvider.ProcessInfoLogger.LogWarning($"GetCurrentCpuUsagePercentage(NET8 Process impl) failure (pid = {procId}): {e.Message}"); throw; } } diff --git a/FabricObserver.Extensibility/Utilities/DataTableFileLogger.cs b/FabricObserver.Extensibility/Utilities/DataTableFileLogger.cs index 16b3d43b..d26ded76 100644 --- a/FabricObserver.Extensibility/Utilities/DataTableFileLogger.cs +++ b/FabricObserver.Extensibility/Utilities/DataTableFileLogger.cs @@ -14,7 +14,7 @@ namespace FabricObserver.Observers.Utilities // CSV file logger for long-running monitoring data (memory/cpu/disk/network usage data). public class DataTableFileLogger { - private static ILogger DataLogger + private static NLog.Logger DataLogger { get; set; } diff --git a/FabricObserver.Extensibility/Utilities/InvalidPluginException.cs b/FabricObserver.Extensibility/Utilities/InvalidPluginException.cs index eb997751..e60cf13d 100644 --- a/FabricObserver.Extensibility/Utilities/InvalidPluginException.cs +++ b/FabricObserver.Extensibility/Utilities/InvalidPluginException.cs @@ -4,7 +4,6 @@ // ------------------------------------------------------------ using System; -using System.Runtime.Serialization; namespace FabricObserver.Utilities { @@ -22,9 +21,5 @@ public InvalidPluginException(string message) : base(message) public InvalidPluginException(string message, Exception innerException) : base(message, innerException) { } - - protected InvalidPluginException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/FabricObserver.Extensibility/Utilities/LinuxPermissionException.cs b/FabricObserver.Extensibility/Utilities/LinuxPermissionException.cs index f1a54b62..13fb53b0 100644 --- a/FabricObserver.Extensibility/Utilities/LinuxPermissionException.cs +++ b/FabricObserver.Extensibility/Utilities/LinuxPermissionException.cs @@ -4,7 +4,6 @@ // ------------------------------------------------------------ using System; -using System.Runtime.Serialization; namespace FabricObserver.Observers.Utilities { @@ -22,9 +21,5 @@ public LinuxPermissionException(string message) : base(message) public LinuxPermissionException(string message, Exception innerException) : base(message, innerException) { } - - protected LinuxPermissionException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } \ No newline at end of file diff --git a/FabricObserver.Extensibility/Utilities/NativeMethods.cs b/FabricObserver.Extensibility/Utilities/NativeMethods.cs index 2ba81778..4c819d5e 100644 --- a/FabricObserver.Extensibility/Utilities/NativeMethods.cs +++ b/FabricObserver.Extensibility/Utilities/NativeMethods.cs @@ -1289,7 +1289,7 @@ public static bool RefreshSFUserProcessDataCache(bool getChildProcesses = false) public static List<(string procName, int procId)> NtGetSFSystemServiceProcessInfo() { SYSTEM_PROCESS_INFORMATION[] procInfoList = NtGetSysProcInfo(); - List<(string procName, int procId)> result = new(); + List<(string procName, int procId)> result = []; uint fabricHostPid = 0; // If NtGetSysProcInfo returns null, it means that something went wrong (logged). FSO accounts for this and @@ -1373,8 +1373,8 @@ private static bool NtSetSFUserServiceCaches(bool getChildProcesses = false) return false; } - descendantsDictionary = new(); - currentSFServiceProcCache = new(); + descendantsDictionary = []; + currentSFServiceProcCache = []; for (int i = 0; i < procInfoList.Count; ++i) { @@ -1414,10 +1414,10 @@ private static bool NtSetSFUserServiceCaches(bool getChildProcesses = false) if (!descendantsDictionary.ContainsKey((int)parentPid)) { - List<(string childProcName, int childProcId, DateTime childProcStartTime)> descendants = new() - { + List<(string childProcName, int childProcId, DateTime childProcStartTime)> descendants = + [ child - }; + ]; _ = descendantsDictionary.TryAdd((int)parentPid, descendants); } @@ -1521,7 +1521,7 @@ public static void ClearSFUserProcessDataCache() return null; } - List<(string procName, int procId, DateTime processStartTime)> childProcs = new(); + List<(string procName, int procId, DateTime processStartTime)> childProcs = []; do { @@ -1948,7 +1948,7 @@ internal static bool GetSytemPerformanceInfo(ref PerformanceInformation pi) return null; } - List<(string, uint)> ret = new(); + List<(string, uint)> ret = []; for (int i = 0; i < currentProcIds.Length; ++i) { @@ -2141,7 +2141,7 @@ private static List NtGetFilteredProcessInfo() return null; } - List procInfoList = new(); + List procInfoList = []; for (int i = 0; i < procInfo.Length; ++i) { @@ -2308,7 +2308,7 @@ private static List NtGetFilteredProcessInfo() private static SYSTEM_PROCESS_INFORMATION[] NtGetSysProcInfo() { const int MAX_TRIES = 5; - ArrayList arrProcInfo = new(); + ArrayList arrProcInfo = []; uint size = 1024; int tried = 0; IntPtr procInfoBuffer = IntPtr.Zero; diff --git a/FabricObserver.Extensibility/Utilities/ObserverHealthReporter.cs b/FabricObserver.Extensibility/Utilities/ObserverHealthReporter.cs index 82e3b9d0..6dea6260 100644 --- a/FabricObserver.Extensibility/Utilities/ObserverHealthReporter.cs +++ b/FabricObserver.Extensibility/Utilities/ObserverHealthReporter.cs @@ -15,20 +15,13 @@ namespace FabricObserver.Observers.Utilities /// /// Reports health data to Service Fabric Health Manager and logs locally (optional). /// - public class ObserverHealthReporter + /// + /// Initializes a new instance of the class. + /// + /// to this constructor as the instance is not used anywhere in this type. + public class ObserverHealthReporter(Logger logger) { - private readonly Logger logger; - - /// - /// Initializes a new instance of the class. - /// - /// File logger instance. Will throw ArgumentException if null. - /// Unused. Exists for compatibility reasons for older plugin impls. Update your plugins to not pass a FabricClient instance - /// to this constructor as the instance is not used anywhere in this type. - public ObserverHealthReporter(Logger logger, FabricClient fabricClient = null) - { - this.logger = logger; - } + private readonly Logger logger = logger; /// /// This function generates Service Fabric Health Reports that will show up in SFX. It supports generating health reports for the following SF entities: diff --git a/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/LinuxInfoProvider.cs b/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/LinuxInfoProvider.cs index b817a751..6d2e3307 100644 --- a/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/LinuxInfoProvider.cs +++ b/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/LinuxInfoProvider.cs @@ -150,7 +150,7 @@ public override async Task GetOSInfoAsync(CancellationToken cancellation ** Example: ** Description:\tUbuntu 18.04.2 LTS */ - osInfo.Name = outputLines[0].Split(new[] { ':' }, 2)[1].Trim(); + osInfo.Name = outputLines[0].Split([':'], 2)[1].Trim(); } osInfo.Version = File.ReadAllText("/proc/version"); @@ -273,7 +273,7 @@ private static int GetPortCount(int processId, Predicate predicate, stri if (process?.ExitCode != 0) { // Try and work around the unsetting of caps issues when SF runs a cluster upgrade. - if (error.ToLower().Contains("permission denied")) + if (error.Contains("permission denied", StringComparison.CurrentCultureIgnoreCase)) { // Throwing LinuxPermissionException here will eventually take down FO (by design). The failure will be logged and telemetry will be emitted, then // the exception will be re-thrown by ObserverManager and the FO process will fail fast exit. Then, SF will create a new instance of FO on the offending node which @@ -373,7 +373,7 @@ public override int GetTotalAllocatedFileHandlesCount() RedirectStandardError = false }; - List output = new(); + List output = []; using Process process = Process.Start(startInfo); string line; diff --git a/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/TcpPortInfo.cs b/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/TcpPortInfo.cs index faacf14c..75c5ef34 100644 --- a/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/TcpPortInfo.cs +++ b/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/TcpPortInfo.cs @@ -52,6 +52,8 @@ public int OwningProcessId get; private set; } + private static readonly char[] separator = [' ']; + /// /// Creates a new instance of TcpPortInfo and set properties based on supplied netstat out row string. /// @@ -64,7 +66,7 @@ public TcpPortInfo(string netstatOutputLine) throw new ArgumentException("netstatOutputLine value must be a valid nestat output row"); } - string[] stats = netstatOutputLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + string[] stats = netstatOutputLine.Split(separator, StringSplitOptions.RemoveEmptyEntries); if (stats.Length != 5 || !int.TryParse(stats[4], out int pid)) { diff --git a/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/WindowsInfoProvider.cs b/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/WindowsInfoProvider.cs index 8c2dc63a..9d5cfac9 100644 --- a/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/WindowsInfoProvider.cs +++ b/FabricObserver.Extensibility/Utilities/OperatingSystemInfo/WindowsInfoProvider.cs @@ -22,7 +22,7 @@ namespace FabricObserver.Observers.Utilities { [SupportedOSPlatform("windows")] - public class WindowsInfoProvider : OSInfoProvider + public partial class WindowsInfoProvider : OSInfoProvider { private const string TcpProtocol = "tcp"; private const int portDataMaxCacheTimeSeconds = 45; @@ -75,7 +75,7 @@ public WindowsInfoProvider() } else { - win32TcpConnInfo = new List<(ushort LocalPort, int OwningProcessId, MIB_TCP_STATE State)>(); + win32TcpConnInfo = []; } } @@ -278,11 +278,7 @@ public override (int LowPort, int HighPort, int NumberOfPorts) TupleGetDynamicPo if (process.WaitForExit(60000)) { - Match match = Regex.Match( - output, - @"Start Port\s+:\s+(?\d+).+?Number of Ports\s+:\s+(?\d+)", - RegexOptions.Singleline | RegexOptions.IgnoreCase); - + Match match = PortRegex().Match(output); string startPort = match.Groups["startPort"].Value; string portCount = match.Groups["numberOfPorts"].Value; int exitStatus = process.ExitCode; @@ -772,9 +768,9 @@ public override string GetOSHotFixes(bool generateKbUrl, CancellationToken token return string.Empty; } - resultsOrdered = results.Cast() + resultsOrdered = [.. results.Cast() .Where(obj => obj["InstalledOn"] != null && obj["InstalledOn"].ToString() != string.Empty) - .OrderByDescending(obj => DateTime.Parse(obj["InstalledOn"].ToString() ?? string.Empty)).ToArray(); + .OrderByDescending(obj => DateTime.Parse(obj["InstalledOn"].ToString() ?? string.Empty))]; var sb = new StringBuilder(); var baseUrl = "https://support.microsoft.com/help/"; @@ -841,5 +837,8 @@ public override float GetAverageDiskQueueLength(string instance) throw; } } + + [GeneratedRegex(@"Start Port\s+:\s+(?\d+).+?Number of Ports\s+:\s+(?\d+)", RegexOptions.IgnoreCase | RegexOptions.Singleline)] + private static partial Regex PortRegex(); } } \ No newline at end of file diff --git a/FabricObserver.Extensibility/Utilities/ProcessInfo/LinuxProcessInfoProvider.cs b/FabricObserver.Extensibility/Utilities/ProcessInfo/LinuxProcessInfoProvider.cs index de1d5895..226dca36 100644 --- a/FabricObserver.Extensibility/Utilities/ProcessInfo/LinuxProcessInfoProvider.cs +++ b/FabricObserver.Extensibility/Utilities/ProcessInfo/LinuxProcessInfoProvider.cs @@ -84,7 +84,7 @@ public override float GetProcessAllocatedHandles(int processId, string configPat ProcessInfoLogger.LogWarning($"elevated_proc_fd exited with: {process.ExitCode}"); // Try and work around the unsetting of caps issues when SF runs a cluster upgrade. - if (error.ToLower().Contains("permission denied")) + if (error.Contains("permission denied", StringComparison.CurrentCultureIgnoreCase)) { // Throwing LinuxPermissionException here will eventually take down FO (by design). The failure will be logged and telemetry will be emitted, then // the exception will be re-thrown by ObserverManager and the FO process will fail fast exit. Then, SF will create a new instance of FO on the offending node which diff --git a/FabricObserver.Extensibility/Utilities/ProcessInfo/WindowsProcessInfoProvider.cs b/FabricObserver.Extensibility/Utilities/ProcessInfo/WindowsProcessInfoProvider.cs index 90e3e875..d2a3df52 100644 --- a/FabricObserver.Extensibility/Utilities/ProcessInfo/WindowsProcessInfoProvider.cs +++ b/FabricObserver.Extensibility/Utilities/ProcessInfo/WindowsProcessInfoProvider.cs @@ -57,10 +57,7 @@ private static PerformanceCounterCategory PerfCounterProcessCategory { lock (lockObj) { - if (performanceCounterCategory == null) - { - performanceCounterCategory = new(ProcessCategoryName); - } + performanceCounterCategory ??= new(ProcessCategoryName); } } return performanceCounterCategory; diff --git a/FabricObserver.Extensibility/Utilities/RetryableException.cs b/FabricObserver.Extensibility/Utilities/RetryableException.cs index 532d1cc3..9170e710 100644 --- a/FabricObserver.Extensibility/Utilities/RetryableException.cs +++ b/FabricObserver.Extensibility/Utilities/RetryableException.cs @@ -4,7 +4,6 @@ // ------------------------------------------------------------ using System; -using System.Runtime.Serialization; namespace FabricObserver.Observers.Utilities { @@ -22,9 +21,5 @@ public RetryableException(string message) : base(message) public RetryableException(string message, Exception innerException) : base(message, innerException) { } - - protected RetryableException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } \ No newline at end of file diff --git a/FabricObserver.Extensibility/Utilities/ServiceFabric/FabricClientUtilities.cs b/FabricObserver.Extensibility/Utilities/ServiceFabric/FabricClientUtilities.cs index a615f999..c0196ab6 100644 --- a/FabricObserver.Extensibility/Utilities/ServiceFabric/FabricClientUtilities.cs +++ b/FabricObserver.Extensibility/Utilities/ServiceFabric/FabricClientUtilities.cs @@ -176,7 +176,7 @@ public async Task> GetAllDeployedAppsAsync(Cancellatio /// A List of ReplicaOrInstanceMonitoringInfo objects representing all replicas in any status (consumer should filter Status per need) on the local (or specified) node. public async Task> GetAllDeployedReplicasOrInstancesAsync(bool includeChildProcesses, CancellationToken token, string nodeName = null) { - List repList = new(); + List repList = []; List appList = await GetAllDeployedAppsAsync(token); if (isWindows && !NativeMethods.RefreshSFUserProcessDataCache(getChildProcesses: includeChildProcesses)) @@ -204,7 +204,7 @@ public async Task> GetAllDeployedReplicasO var deployedReplicaList = await FabricClientSingleton.QueryManager.GetDeployedReplicaListAsync(nodeName ?? this.nodeName, app.ApplicationName, null, null, TimeSpan.FromSeconds(60), token); - if (deployedReplicaList == null || !deployedReplicaList.Any()) + if (deployedReplicaList == null || deployedReplicaList.Count == 0) { // Application has no deployed replicas. continue; @@ -637,7 +637,7 @@ any processes (children) that the service process (parent) created/spawned. */ /// A List of tuple (string ServiceName, string ProcName, int Pid) representing all services supplied in the ReplicaOrInstanceMonitoringInfo instance, including child processes of each service, if any. public List<(string ServiceName, string ProcName, int Pid, DateTime ProcessStartTime)> GetServiceProcessInfo(List repOrInsts) { - List<(string ServiceName, string ProcName, int Pid, DateTime ProcessStartTime)> pids = new(); + List<(string ServiceName, string ProcName, int Pid, DateTime ProcessStartTime)> pids = []; foreach (var repOrInst in repOrInsts) { @@ -761,7 +761,7 @@ public static string ParseAppParameterValue(string appParamValue, ApplicationPar } // Application parameter value specified as a Service Fabric Application Manifest variable. - if (appParamValue.StartsWith("[")) + if (appParamValue.StartsWith('[')) { appParamValue = appParamValue.Replace("[", string.Empty).Replace("]", string.Empty); @@ -782,7 +782,7 @@ public static string ParseAppParameterValue(string appParamValue, ApplicationPar public static void AddParametersIfNotExists(ApplicationParameterList toParameters, ApplicationParameterList fromParameters) { // If toParameters is passed in as null, then make it a new instance. - toParameters ??= new ApplicationParameterList(); + toParameters ??= []; if (fromParameters != null) { @@ -1209,7 +1209,7 @@ private async Task RemoveServiceHealthReportsAsync(ServiceHealthState service, b && (e.HealthInformation.SourceId.StartsWith(ObserverConstants.AppObserverName) || e.HealthInformation.SourceId.StartsWith(ObserverConstants.ContainerObserverName))).ToList(); - if (!serviceHealthEvents.Any()) + if (serviceHealthEvents.Count == 0) { return; } @@ -1262,7 +1262,7 @@ private async Task RemoveApplicationHealthReportsAsync(ApplicationHealthState ap || e.HealthInformation.SourceId.StartsWith(ObserverConstants.FabricSystemObserverName) || e.HealthInformation.SourceId.StartsWith(ObserverConstants.NetworkObserverName))).ToList(); - if (!appHealthEvents.Any()) + if (appHealthEvents.Count == 0) { return; } @@ -1319,7 +1319,7 @@ private async Task RemoveNodeHealthReportsAsync(IEnumerable nod || e.HealthInformation.SourceId.StartsWith(ObserverConstants.NodeObserverName) || e.HealthInformation.SourceId.StartsWith(ObserverConstants.OSObserverName)).ToList(); - if (!nodeHealthEvents.Any()) + if (nodeHealthEvents.Count == 0) { return; } diff --git a/FabricObserver.Extensibility/Utilities/Telemetry/AppInsightsTelemetry.cs b/FabricObserver.Extensibility/Utilities/Telemetry/AppInsightsTelemetry.cs index 73cbbc2e..d271e6bc 100644 --- a/FabricObserver.Extensibility/Utilities/Telemetry/AppInsightsTelemetry.cs +++ b/FabricObserver.Extensibility/Utilities/Telemetry/AppInsightsTelemetry.cs @@ -187,7 +187,7 @@ public Task ReportHealthAsync(TelemetryDataBase telemetryData, CancellationToken if (telemetryData is ServiceTelemetryData serviceTelemData) { - properties = new Dictionary + properties = new() { { "ClusterId", serviceTelemData.ClusterId }, { "EntityType", serviceTelemData.EntityType.ToString() }, @@ -216,7 +216,7 @@ public Task ReportHealthAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is NodeTelemetryData nodeTelemData) { - properties = new Dictionary + properties = new() { { "ClusterId", nodeTelemData.ClusterId }, { "EntityType", nodeTelemData.EntityType.ToString() }, @@ -231,7 +231,7 @@ public Task ReportHealthAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is DiskTelemetryData diskTelemData) { - properties = new Dictionary + properties = new() { { "ClusterId", diskTelemData.ClusterId }, { "EntityType", diskTelemData.EntityType.ToString() }, @@ -247,7 +247,7 @@ public Task ReportHealthAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is ClusterTelemetryData clusterTelemData) { - properties = new Dictionary + properties = new() { { "ClusterId", clusterTelemData.ClusterId }, { "EntityType", EntityType.Cluster.ToString() }, @@ -261,7 +261,7 @@ public Task ReportHealthAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is SystemServiceTelemetryData systemServiceTelemData) { - properties = new Dictionary + properties = new() { { "ApplicationName", systemServiceTelemData.ApplicationName }, { "ClusterId", systemServiceTelemData.ClusterId }, @@ -279,7 +279,7 @@ public Task ReportHealthAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is NetworkTelemetryData networkTelemData) { - properties = new Dictionary + properties = new() { { "ApplicationName", networkTelemData.ApplicationName }, { "ClusterId", networkTelemData.ClusterId }, @@ -293,7 +293,7 @@ public Task ReportHealthAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is ContainerTelemetryData containerTelemData) { - properties = new Dictionary + properties = new() { { "ApplicationName", containerTelemData.ApplicationName }, { "ServiceName", containerTelemData.ServiceName }, @@ -312,7 +312,7 @@ public Task ReportHealthAsync(TelemetryDataBase telemetryData, CancellationToken } else { - properties = new Dictionary + properties = new() { { "ClusterId", telemetryData.ClusterId }, { "EntityType", telemetryData.EntityType.ToString() }, @@ -399,7 +399,7 @@ public Task ReportMetricAsync(TelemetryDataBase telemetryData, CancellationToken if (telemetryData is ServiceTelemetryData serviceTelemData) { - properties = new Dictionary + properties = new() { { "ClusterId", serviceTelemData.ClusterId }, { "EntityType", serviceTelemData.EntityType.ToString() }, @@ -428,7 +428,7 @@ public Task ReportMetricAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is NodeTelemetryData nodeTelemData) { - properties = new Dictionary + properties = new() { { "ClusterId", nodeTelemData.ClusterId }, { "EntityType", nodeTelemData.EntityType.ToString() }, @@ -443,7 +443,7 @@ public Task ReportMetricAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is DiskTelemetryData diskTelemData) { - properties = new Dictionary + properties = new() { { "ClusterId", diskTelemData.ClusterId }, { "EntityType", diskTelemData.EntityType.ToString() }, @@ -459,7 +459,7 @@ public Task ReportMetricAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is ClusterTelemetryData clusterTelemData) { - properties = new Dictionary + properties = new() { { "ClusterId", clusterTelemData.ClusterId }, { "EntityType", EntityType.Cluster.ToString() }, @@ -472,7 +472,7 @@ public Task ReportMetricAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is SystemServiceTelemetryData systemServiceTelemData) { - properties = new Dictionary + properties = new() { { "ApplicationName", systemServiceTelemData.ApplicationName }, { "ClusterId", systemServiceTelemData.ClusterId }, @@ -489,7 +489,7 @@ public Task ReportMetricAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is NetworkTelemetryData networkTelemData) { - properties = new Dictionary + properties = new() { { "ApplicationName", networkTelemData.ApplicationName }, { "ClusterId", networkTelemData.ClusterId }, @@ -503,7 +503,7 @@ public Task ReportMetricAsync(TelemetryDataBase telemetryData, CancellationToken } else if (telemetryData is ContainerTelemetryData containerTelemData) { - properties = new Dictionary + properties = new() { { "ApplicationName", containerTelemData.ApplicationName }, { "ServiceName", containerTelemData.ServiceName }, @@ -522,7 +522,7 @@ public Task ReportMetricAsync(TelemetryDataBase telemetryData, CancellationToken } else { - properties = new Dictionary + properties = new() { { "ClusterId", telemetryData.ClusterId }, { "EntityType", telemetryData.EntityType.ToString() }, @@ -704,7 +704,7 @@ public Task ReportClusterUpgradeStatusAsync(ServiceFabricUpgradeEventData eventD try { - IDictionary eventProperties = new Dictionary + Dictionary eventProperties = new() { { "EventName", "ClusterUpgradeEvent" }, { "TaskName", eventData.TaskName }, @@ -749,7 +749,7 @@ public Task ReportApplicationUpgradeStatusAsync(ServiceFabricUpgradeEventData ev try { - IDictionary eventProperties = new Dictionary + Dictionary eventProperties = new() { { "EventName", "ApplicationUpgradeEvent" }, { "TaskName", eventData.TaskName }, diff --git a/FabricObserver.Extensibility/Utilities/Telemetry/LogAnalyticsTelemetry.cs b/FabricObserver.Extensibility/Utilities/Telemetry/LogAnalyticsTelemetry.cs index 54dfef6d..68858749 100644 --- a/FabricObserver.Extensibility/Utilities/Telemetry/LogAnalyticsTelemetry.cs +++ b/FabricObserver.Extensibility/Utilities/Telemetry/LogAnalyticsTelemetry.cs @@ -21,27 +21,30 @@ namespace FabricObserver.Observers.Utilities.Telemetry { // LogAnalyticsTelemetry class is partially (SendTelemetryAsync/GetSignature) based on public sample: https://dejanstojanovic.net/aspnet/2018/february/send-data-to-azure-log-analytics-from-c-code/ - public class LogAnalyticsTelemetry : ITelemetryProvider + public class LogAnalyticsTelemetry( + string workspaceId, + string sharedKey, + string logType) : ITelemetryProvider { private const string ApiVersion = "2016-04-01"; - private readonly Logger logger; + private readonly Logger logger = new Logger("TelemetryLogger"); private string WorkspaceId { get; - } + } = workspaceId; private string LogType { get; - } + } = logType; private string TargetUri => $"https://{WorkspaceId}.ods.opinsights.azure.com/api/logs?api-version={ApiVersion}"; public string Key { get; set; - } + } = sharedKey; /// /// Sends telemetry data to Azure LogAnalytics via REST. @@ -111,17 +114,6 @@ private string GetSignature( return $"SharedKey {WorkspaceId}:{Convert.ToBase64String(encryptor.ComputeHash(bytes))}"; } - public LogAnalyticsTelemetry( - string workspaceId, - string sharedKey, - string logType) - { - WorkspaceId = workspaceId; - Key = sharedKey; - LogType = logType; - logger = new Logger("TelemetryLogger"); - } - public async Task ReportHealthAsync( string propertyName, HealthState state, diff --git a/FabricObserver.nuspec.template b/FabricObserver.nuspec.template index e65b5db6..a3381d87 100644 --- a/FabricObserver.nuspec.template +++ b/FabricObserver.nuspec.template @@ -2,13 +2,11 @@ %PACKAGE_ID% - 3.2.16 + 3.3.0 -- Removed global suppression for compiler warning related to cross platform visibility of functions that only support Windows. -- Refactored/updated Windows-only code that led to compiler warnings for cross platform visibility. -- ServiceFabricConfigurationObserver no longer exists and all references to the defunct FabricObserverWebApi project have been deleted. Any related settings have also been deleted in ApplicationManifest and Settings.xml. Please note these changes. -- FabricSystemObserver no longer monitors Windows Event Log as that feature was only to support the obsolete FabricObserverWebApi application (defunct). -- Updated nuget package versions. Note that SF SDK version was bumped to highest 6.0 version (highest 9.0 Runtime, which you should no longer have deployed to production, anyway as it is unsupported). + - .NET 8 implementation of FabricObserver. This version is built for .NET 8 and SF Runtime >= 9.1 (Self-Contained FO builds only). If you have deployed SF Runtime version >= 10.1 Cumulative Update 3.0 (CU3), then you can deploy the framework-dependent release build for the target platform (Windows or Linux). If you are not running SF Runtime version >= 10.1 CU3, then you must deploy the Self-Contained release build for the target platform (Windows or Linux). **If you can't upgrade to .NET 8 yet, then do not upgrade to this version.** + - The FabricObserverWebAPI project has been completely removed and all related usage in observers removed. + - FabricSystemObserver no longer monitors Windows Event Logs. Setting the related configuration values will have no effect. Microsoft MIT @@ -17,12 +15,12 @@ icon.png fonuget.md en-US - This package contains the FabricObserver(FO) Application - built for .NET 6.0 and SF Runtime 9.x. FO a highly configurable and extensible resource usage watchdog service that is designed to be run in Azure Service Fabric Windows and Linux clusters. This package contains the entire application and can be used to build .NET Standard 2.0 observer plugins. NOTE: If you want to target .NET 6 for your plugins, then you must use Microsoft.ServiceFabricApps.FabricObserver.Extensibility.3.2.16 nuget package to build them. + This package contains the FabricObserver(FO) Application - built for NET8 and SF Runtime 9.x. FO a highly configurable and extensible resource usage watchdog service that is designed to be run in Azure Service Fabric Windows and Linux clusters. This package contains the entire application and can be used to build .NET Standard 2.0 or .NET 8 observer plugins. - + @@ -32,9 +30,9 @@ - - - + + + diff --git a/FabricObserver.sln b/FabricObserver.sln index adf0566a..b7574bb7 100644 --- a/FabricObserver.sln +++ b/FabricObserver.sln @@ -35,10 +35,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md SECURITY.md = SECURITY.md Documentation\Deployment\service-fabric-cluster-observer.json = Documentation\Deployment\service-fabric-cluster-observer.json - Documentation\Deployment\service-fabric-cluster-observer.v2.2.8.parameters.json = Documentation\Deployment\service-fabric-cluster-observer.v2.2.8.parameters.json Documentation\Deployment\service-fabric-observer.json = Documentation\Deployment\service-fabric-observer.json + Documentation\Deployment\service-fabric-cluster-observer.v2.3.0.parameters.json = Documentation\Deployment\service-fabric-cluster-observer.v2.3.0.parameters.json Documentation\Using.md = Documentation\Using.md - Documentation\Deployment\service-fabric-observer.v3.2.16.parameters.json = Documentation\Deployment\service-fabric-observer.v3.2.16.parameters.json + Documentation\Deployment\service-fabric-observer.v3.3.0.parameters.json = Documentation\Deployment\service-fabric-observer.v3.3.0.parameters.json EndProjectSection EndProject Project("{A07B5EB6-E848-4116-A8D0-A826331D98C6}") = "ClusterObserverApp", "ClusterObserverApp\ClusterObserverApp.sfproj", "{BD5D216F-5F89-4CC4-92FD-D6FDEC5A19AD}" diff --git a/FabricObserver/FabricObserver.cs b/FabricObserver/FabricObserver.cs index 218cf6a8..ed81be85 100644 --- a/FabricObserver/FabricObserver.cs +++ b/FabricObserver/FabricObserver.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using FabricObserver.Observers; using FabricObserver.Observers.Utilities; +using FabricObserver.Observers.Utilities.Telemetry; using FabricObserver.Utilities; using McMaster.NETCore.Plugins; using Microsoft.Extensions.DependencyInjection; @@ -22,19 +23,14 @@ namespace FabricObserver /// /// An instance of this class is created for each service instance by the Service Fabric runtime. /// - internal sealed class FabricObserverService : StatelessService + /// + /// Initializes a new instance of the type. + /// + /// StatelessServiceContext instance. + internal sealed class FabricObserverService(StatelessServiceContext context) : StatelessService(context) { private ObserverManager observerManager; - private readonly Logger logger; - - /// - /// Initializes a new instance of the type. - /// - /// StatelessServiceContext instance. - public FabricObserverService(StatelessServiceContext context) : base(context) - { - logger = new Logger("FabricObserverService"); - } + private readonly Logger logger = new("FabricObserverService"); /// /// This is the main entry point for your service instance. @@ -108,7 +104,7 @@ private void LoadObserversFromPlugins(IServiceCollection services) } PluginLoader[] pluginLoaders = new PluginLoader[pluginDlls.Length]; - Type[] sharedTypes = { typeof(FabricObserverStartupAttribute), typeof(IFabricObserverStartup), typeof(IServiceCollection) }; + Type[] sharedTypes = [typeof(FabricObserverStartupAttribute), typeof(IFabricObserverStartup), typeof(IServiceCollection)]; string dll = ""; for (int i = 0; i < pluginDlls.Length; ++i) @@ -158,7 +154,7 @@ private void LoadObserversFromPlugins(IServiceCollection services) AppName = new Uri($"{Context.CodePackageActivationContext.ApplicationName}"), EmitLogEvent = true, HealthMessage = error, - EntityType = Observers.Utilities.Telemetry.EntityType.Application, + EntityType = EntityType.Application, HealthReportTimeToLive = TimeSpan.FromMinutes(10), State = System.Fabric.Health.HealthState.Warning, Property = "FabricObserverPluginLoadError", diff --git a/FabricObserver/FabricObserver.csproj b/FabricObserver/FabricObserver.csproj index 614d1329..833e4257 100644 --- a/FabricObserver/FabricObserver.csproj +++ b/FabricObserver/FabricObserver.csproj @@ -4,20 +4,22 @@ FabricObserver FabricObserver Exe - net6.0 + net8.0 disable True win-x64;linux-x64 True Copyright © 2024 FabricObserver - 3.2.16 - 3.2.16 + 3.3.0 + 3.3.0 true true FabricObserver.Program true x64 + @@ -28,8 +30,8 @@ - - + + diff --git a/FabricObserver/Observers/AppObserver.cs b/FabricObserver/Observers/AppObserver.cs index 0212fac6..4034f39a 100644 --- a/FabricObserver/Observers/AppObserver.cs +++ b/FabricObserver/Observers/AppObserver.cs @@ -26,10 +26,14 @@ using Microsoft.Win32.SafeHandles; namespace FabricObserver.Observers -{ - // This observer monitors the behavior of user SF service processes (and their children) and signals Warning and Error based on user-supplied resource thresholds - // in AppObserver.config.json. This observer will also emit telemetry (ETW, LogAnalytics/AppInsights) if enabled in Settings.xml (ObserverManagerConfiguration) and ApplicationManifest.xml (AppObserverEnableEtw). - public sealed class AppObserver : ObserverBase +{ + // This observer monitors the behavior of user SF service processes (and their children) and signals Warning and Error based on user-supplied resource thresholds + // in AppObserver.config.json. This observer will also emit telemetry (ETW, LogAnalytics/AppInsights) if enabled in Settings.xml (ObserverManagerConfiguration) and ApplicationManifest.xml (AppObserverEnableEtw). + /// + /// Creates a new instance of the type. + /// + /// The StatelessServiceContext instance. + public sealed class AppObserver(StatelessServiceContext context) : ObserverBase(null, context) { private const double KvsLvidsWarningPercentage = 75.0; private const double MaxRGMemoryInUsePercent = 90.0; @@ -71,7 +75,7 @@ public sealed class AppObserver : ObserverBase // List is thread-safe for concurrent reads. There are no concurrent writes to this List. private List deployedApps; - private readonly Stopwatch stopwatch; + private readonly Stopwatch stopwatch = new(); private readonly object lockObj = new(); private FabricClientUtilities fabricClientUtilities; private ParallelOptions parallelOptions; @@ -173,15 +177,6 @@ public bool MonitorResourceGovernanceLimits get; set; } - /// - /// Creates a new instance of the type. - /// - /// The StatelessServiceContext instance. - public AppObserver(StatelessServiceContext context) : base(null, context) - { - stopwatch = new Stopwatch(); - } - public override async Task ObserveAsync(CancellationToken token) { ObserverLogger.LogInfo($"Started ObserveAsync."); @@ -373,9 +368,9 @@ public override Task ReportAsync(CancellationToken token) try { // CPU Time (Percent) - if (AllAppCpuData != null && AllAppCpuData.ContainsKey(id)) + if (AllAppCpuData != null && AllAppCpuData.TryGetValue(id, out FabricResourceUsageData cpuPctFrud)) { - var parentFrud = AllAppCpuData[id]; + var parentFrud = cpuPctFrud; int childProcCount = 0; if (hasChildProcs) @@ -413,9 +408,9 @@ public override Task ReportAsync(CancellationToken token) } // Working Set (MB) - if (AllAppMemDataMb != null && AllAppMemDataMb.ContainsKey(id)) + if (AllAppMemDataMb != null && AllAppMemDataMb.TryGetValue(id, out FabricResourceUsageData memMbFrud)) { - var parentFrud = AllAppMemDataMb[id]; + var parentFrud = memMbFrud; int childProcCount = 0; if (hasChildProcs) @@ -452,9 +447,9 @@ public override Task ReportAsync(CancellationToken token) } // Working Set (Percent) - if (AllAppMemDataPercent != null && AllAppMemDataPercent.ContainsKey(id)) + if (AllAppMemDataPercent != null && AllAppMemDataPercent.TryGetValue(id, out FabricResourceUsageData memPctFrud)) { - var parentFrud = AllAppMemDataPercent[id]; + var parentFrud = memPctFrud; int childProcCount = 0; if (hasChildProcs) @@ -491,11 +486,11 @@ public override Task ReportAsync(CancellationToken token) } // Private Bytes (MB) - if (AllAppPrivateBytesDataMb != null && AllAppPrivateBytesDataMb.ContainsKey(id)) + if (AllAppPrivateBytesDataMb != null && AllAppPrivateBytesDataMb.TryGetValue(id, out FabricResourceUsageData privateBytesMbFrud)) { if (app.WarningPrivateBytesMb > 0 || app.ErrorPrivateBytesMb > 0) { - var parentFrud = AllAppPrivateBytesDataMb[id]; + var parentFrud = privateBytesMbFrud; int childProcCount = 0; if (hasChildProcs) @@ -533,11 +528,11 @@ public override Task ReportAsync(CancellationToken token) } // Private Bytes (Percent) - if (AllAppPrivateBytesDataPercent != null && AllAppPrivateBytesDataPercent.ContainsKey(id)) + if (AllAppPrivateBytesDataPercent != null && AllAppPrivateBytesDataPercent.TryGetValue(id, out FabricResourceUsageData privateBytesPctFrud)) { if (app.WarningPrivateBytesPercent > 0 || app.ErrorPrivateBytesPercent > 0) { - var parentFrud = AllAppPrivateBytesDataPercent[id]; + var parentFrud = privateBytesPctFrud; int childProcCount = 0; if (hasChildProcs) @@ -578,9 +573,9 @@ public override Task ReportAsync(CancellationToken token) if (MonitorResourceGovernanceLimits) { // RG Memory Monitoring (Private Bytes Percent) - if (repOrInst.RGMemoryEnabled && AllAppRGMemoryUsagePercent != null && AllAppRGMemoryUsagePercent.ContainsKey(id)) + if (repOrInst.RGMemoryEnabled && AllAppRGMemoryUsagePercent != null && AllAppRGMemoryUsagePercent.TryGetValue(id, out FabricResourceUsageData rgMemPctFrud)) { - var parentFrud = AllAppRGMemoryUsagePercent[id]; + var parentFrud = rgMemPctFrud; int childProcCount = 0; if (hasChildProcs) @@ -617,9 +612,9 @@ public override Task ReportAsync(CancellationToken token) } // RG CPU Monitoring (CPU Time Percent) - if (repOrInst.RGCpuEnabled && AllAppRGCpuUsagePercent != null && AllAppRGCpuUsagePercent.ContainsKey(id)) + if (repOrInst.RGCpuEnabled && AllAppRGCpuUsagePercent != null && AllAppRGCpuUsagePercent.TryGetValue(id, out FabricResourceUsageData rgCpuPctFrud)) { - var parentFrud = AllAppRGCpuUsagePercent[id]; + var parentFrud = rgCpuPctFrud; int childProcCount = 0; if (hasChildProcs) @@ -657,9 +652,9 @@ public override Task ReportAsync(CancellationToken token) } // TCP Ports - Active - if (AllAppTotalActivePortsData != null && AllAppTotalActivePortsData.ContainsKey(id)) + if (AllAppTotalActivePortsData != null && AllAppTotalActivePortsData.TryGetValue(id, out FabricResourceUsageData tcpPortsFrud)) { - var parentFrud = AllAppTotalActivePortsData[id]; + var parentFrud = tcpPortsFrud; int childProcCount = 0; if (hasChildProcs) @@ -696,9 +691,9 @@ public override Task ReportAsync(CancellationToken token) } // TCP Ports Total - Ephemeral (port numbers fall in the dynamic range) - if (AllAppEphemeralPortsData != null && AllAppEphemeralPortsData.ContainsKey(id)) + if (AllAppEphemeralPortsData != null && AllAppEphemeralPortsData.TryGetValue(id, out FabricResourceUsageData ePortsRawFrud)) { - var parentFrud = AllAppEphemeralPortsData[id]; + var parentFrud = ePortsRawFrud; int childProcCount = 0; if (hasChildProcs) @@ -735,9 +730,9 @@ public override Task ReportAsync(CancellationToken token) } // TCP Ports Percentage - Ephemeral (port numbers fall in the dynamic range) - if (AllAppEphemeralPortsDataPercent != null && AllAppEphemeralPortsDataPercent.ContainsKey(id)) + if (AllAppEphemeralPortsDataPercent != null && AllAppEphemeralPortsDataPercent.TryGetValue(id, out FabricResourceUsageData ePortsPctFrud)) { - var parentFrud = AllAppEphemeralPortsDataPercent[id]; + var parentFrud = ePortsPctFrud; int childProcCount = 0; if (hasChildProcs) @@ -774,9 +769,9 @@ public override Task ReportAsync(CancellationToken token) } // Handles - if (AllAppHandlesData != null && AllAppHandlesData.ContainsKey(id)) + if (AllAppHandlesData != null && AllAppHandlesData.TryGetValue(id, out FabricResourceUsageData handlesFrud)) { - var parentFrud = AllAppHandlesData[id]; + var parentFrud = handlesFrud; int childProcCount = 0; if (hasChildProcs) @@ -813,9 +808,9 @@ public override Task ReportAsync(CancellationToken token) } // Threads - if (AllAppThreadsData != null && AllAppThreadsData.ContainsKey(id)) + if (AllAppThreadsData != null && AllAppThreadsData.TryGetValue(id, out FabricResourceUsageData threadsFrud)) { - var parentFrud = AllAppThreadsData[id]; + var parentFrud = threadsFrud; int childProcCount = 0; if (hasChildProcs) @@ -852,9 +847,9 @@ public override Task ReportAsync(CancellationToken token) } // KVS LVIDs - Windows-only (EnableKvsLvidMonitoring will always be false otherwise) - if (EnableKvsLvidMonitoring && AllAppKvsLvidsData != null && AllAppKvsLvidsData.ContainsKey(id)) + if (EnableKvsLvidMonitoring && AllAppKvsLvidsData != null && AllAppKvsLvidsData.TryGetValue(id, out FabricResourceUsageData lvidsFrud)) { - var parentFrud = AllAppKvsLvidsData[id]; + var parentFrud = lvidsFrud; int childProcCount = 0; if (hasChildProcs) @@ -903,7 +898,7 @@ public override Task ReportAsync(CancellationToken token) if (IsTelemetryEnabled && EmitRawMetricTelemetry) { - _ = TelemetryClient?.ReportMetricAsync(childProcessTelemetryDataList.ToList(), token); + _ = TelemetryClient?.ReportMetricAsync([.. childProcessTelemetryDataList], token); } } } @@ -925,9 +920,9 @@ public override Task ReportAsync(CancellationToken token) public async Task InitializeAsync() { ObserverLogger.LogInfo($"Initializing AppObserver."); - ReplicaOrInstanceList = new List(); - userTargetList = new List(); - deployedTargetList = new List(); + ReplicaOrInstanceList = []; + userTargetList = []; + deployedTargetList = []; // NodeName is passed here to not break unit tests, which include a mock service fabric context. fabricClientUtilities = new FabricClientUtilities(NodeName); @@ -1187,7 +1182,7 @@ private bool PopulateAppInfoWithAppManifestThresholds(DeployedApplication deploy } } - ApplicationParameterList parameters = new(); + ApplicationParameterList parameters = []; FabricClientUtilities.AddParametersIfNotExists(parameters, appParameters); FabricClientUtilities.AddParametersIfNotExists(parameters, defaultParameters); @@ -1683,7 +1678,7 @@ private void FilterTargetAppFormat() continue; } - if (target.TargetApp == "*" || target.TargetApp.ToLower() == "all") + if (target.TargetApp == "*" || target.TargetApp.Equals("all", StringComparison.CurrentCultureIgnoreCase)) { continue; } @@ -2103,7 +2098,7 @@ private int ProcessChildProcs( PartitionId = repOrInst.PartitionId.ToString(), ReplicaId = repOrInst.ReplicaOrInstanceId, ChildProcessCount = childProcs.Count, - ChildProcessInfo = new List() + ChildProcessInfo = [] }; string appNameOrType = GetAppNameOrType(repOrInst); @@ -2466,9 +2461,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppCpuData.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.CpuTime, id, capacity, UseCircularBuffer, EnableConcurrentMonitoring)); } - if (AllAppCpuData != null && AllAppCpuData.ContainsKey(id)) - { - AllAppCpuData[id].ClearData(); + if (AllAppCpuData != null && AllAppCpuData.TryGetValue(id, out FabricResourceUsageData cpuDataFrud)) + { + cpuDataFrud.ClearData(); checkCpu = true; } @@ -2478,9 +2473,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppMemDataMb.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.MemoryConsumptionMb, id, capacity, UseCircularBuffer, EnableConcurrentMonitoring)); } - if (AllAppMemDataMb != null && AllAppMemDataMb.ContainsKey(id)) - { - AllAppMemDataMb[id].ClearData(); + if (AllAppMemDataMb != null && AllAppMemDataMb.TryGetValue(id, out FabricResourceUsageData memDataMbFrud)) + { + memDataMbFrud.ClearData(); checkMemMb = true; } @@ -2490,9 +2485,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppMemDataPercent.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.MemoryConsumptionPercentage, id, capacity, UseCircularBuffer, EnableConcurrentMonitoring)); } - if (AllAppMemDataPercent != null && AllAppMemDataPercent.ContainsKey(id)) - { - AllAppMemDataPercent[id].ClearData(); + if (AllAppMemDataPercent != null && AllAppMemDataPercent.TryGetValue(id, out FabricResourceUsageData memDataPctFrud)) + { + memDataPctFrud.ClearData(); checkMemPct = true; } @@ -2502,9 +2497,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppPrivateBytesDataMb.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.PrivateBytesMb, id, 1, false, EnableConcurrentMonitoring)); } - if (IsWindows && AllAppPrivateBytesDataMb != null && AllAppPrivateBytesDataMb.ContainsKey(id)) - { - AllAppPrivateBytesDataMb[id].ClearData(); + if (IsWindows && AllAppPrivateBytesDataMb != null && AllAppPrivateBytesDataMb.TryGetValue(id, out FabricResourceUsageData prBytesMbFrud)) + { + prBytesMbFrud.ClearData(); checkMemPrivateBytes = true; } @@ -2514,9 +2509,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppPrivateBytesDataPercent.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.PrivateBytesPercent, id, 1, false, EnableConcurrentMonitoring)); } - if (IsWindows && AllAppPrivateBytesDataPercent != null && AllAppPrivateBytesDataPercent.ContainsKey(id)) - { - AllAppPrivateBytesDataPercent[id].ClearData(); + if (IsWindows && AllAppPrivateBytesDataPercent != null && AllAppPrivateBytesDataPercent.TryGetValue(id, out FabricResourceUsageData prBytesPctFrud)) + { + prBytesPctFrud.ClearData(); checkMemPrivateBytesPct = true; } @@ -2526,7 +2521,7 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppRGMemoryUsagePercent.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.RGMemoryUsagePercent, id, 1, false, EnableConcurrentMonitoring)); } - if (IsWindows && AllAppRGMemoryUsagePercent != null && AllAppRGMemoryUsagePercent.ContainsKey(id)) + if (IsWindows && AllAppRGMemoryUsagePercent != null && AllAppRGMemoryUsagePercent.TryGetValue(id, out FabricResourceUsageData rgMemPctFrud)) { rgMemoryPercentThreshold = application.WarningRGMemoryLimitPercent; @@ -2540,9 +2535,9 @@ any processes that the service process (parent) created/spawned (children). */ else { rgMemoryPercentThreshold = MaxRGMemoryInUsePercent; // Default: 90%. - } - - AllAppRGMemoryUsagePercent[id].ClearData(); + } + + rgMemPctFrud.ClearData(); } // CPU - RG monitoring. Windows-only for now. @@ -2551,7 +2546,7 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppRGCpuUsagePercent.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.RGCpuUsagePercent, id, 1, false, EnableConcurrentMonitoring)); } - if (IsWindows && AllAppRGCpuUsagePercent != null && AllAppRGCpuUsagePercent.ContainsKey(id)) + if (IsWindows && AllAppRGCpuUsagePercent != null && AllAppRGCpuUsagePercent.TryGetValue(id, out FabricResourceUsageData rgCpuPctDataFrud)) { rgCpuPercentThreshold = application.WarningRGCpuLimitPercent; @@ -2565,7 +2560,9 @@ any processes that the service process (parent) created/spawned (children). */ else { rgCpuPercentThreshold = MaxRGCpuInUsePercent; // Default: 90%. - } + } + + rgCpuPctDataFrud.ClearData(); } // Active TCP Ports @@ -2574,9 +2571,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppTotalActivePortsData.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.ActiveTcpPorts, id, 1, false, EnableConcurrentMonitoring)); } - if (AllAppTotalActivePortsData != null && AllAppTotalActivePortsData.ContainsKey(id)) - { - AllAppTotalActivePortsData[id].ClearData(); + if (AllAppTotalActivePortsData != null && AllAppTotalActivePortsData.TryGetValue(id, out FabricResourceUsageData totalActivePortsDataFrud)) + { + totalActivePortsDataFrud.ClearData(); checkAllPorts = true; } @@ -2586,9 +2583,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppEphemeralPortsData.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.ActiveEphemeralPorts, id, 1, false, EnableConcurrentMonitoring)); } - if (AllAppEphemeralPortsData != null && AllAppEphemeralPortsData.ContainsKey(id)) - { - AllAppEphemeralPortsData[id].ClearData(); + if (AllAppEphemeralPortsData != null && AllAppEphemeralPortsData.TryGetValue(id, out FabricResourceUsageData ePortsDataFrud)) + { + ePortsDataFrud.ClearData(); checkEphemeralPorts = true; } @@ -2598,9 +2595,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppEphemeralPortsDataPercent.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.ActiveEphemeralPortsPercentage, id, 1, false, EnableConcurrentMonitoring)); } - if (AllAppEphemeralPortsDataPercent != null && AllAppEphemeralPortsDataPercent.ContainsKey(id)) - { - AllAppEphemeralPortsDataPercent[id].ClearData(); + if (AllAppEphemeralPortsDataPercent != null && AllAppEphemeralPortsDataPercent.TryGetValue(id, out FabricResourceUsageData ePortsPctDataFrud)) + { + ePortsPctDataFrud.ClearData(); checkPercentageEphemeralPorts = true; } @@ -2611,9 +2608,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppHandlesData.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.HandleCount, id, 1, false, EnableConcurrentMonitoring)); } - if (AllAppHandlesData != null && AllAppHandlesData.ContainsKey(id)) - { - AllAppHandlesData[id].ClearData(); + if (AllAppHandlesData != null && AllAppHandlesData.TryGetValue(id, out FabricResourceUsageData handlesDataFrud)) + { + handlesDataFrud.ClearData(); checkHandles = true; } @@ -2623,9 +2620,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppThreadsData.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.ThreadCount, id, 1, false, EnableConcurrentMonitoring)); } - if (AllAppThreadsData != null && AllAppThreadsData.ContainsKey(id)) - { - AllAppThreadsData[id].ClearData(); + if (AllAppThreadsData != null && AllAppThreadsData.TryGetValue(id, out FabricResourceUsageData threadsDataFrud)) + { + threadsDataFrud.ClearData(); checkThreads = true; } @@ -2636,9 +2633,9 @@ any processes that the service process (parent) created/spawned (children). */ _ = AllAppKvsLvidsData.TryAdd(id, new FabricResourceUsageData(ErrorWarningProperty.KvsLvidsPercent, id, 1, false, EnableConcurrentMonitoring)); } - if (AllAppKvsLvidsData != null && AllAppKvsLvidsData.ContainsKey(id)) - { - AllAppKvsLvidsData[id].ClearData(); + if (AllAppKvsLvidsData != null && AllAppKvsLvidsData.TryGetValue(id, out FabricResourceUsageData lvidsDataFrud)) + { + lvidsDataFrud.ClearData(); checkLvids = true; } @@ -3326,7 +3323,7 @@ private async Task> GetDeployedReplicasAsy ConfigurationSettings.AsyncTimeout, Token); - if (deployedReplicaList == null || !deployedReplicaList.Any()) + if (deployedReplicaList == null || deployedReplicaList.Count == 0) { return null; } @@ -3408,7 +3405,7 @@ private void SetInstanceOrReplicaMonitoringList( { if (filterList != null && filterType != ServiceFilterType.None) { - bool isInFilterList = filterList.Any(s => statefulReplica.ServiceName.OriginalString.ToLower().Contains(s.ToLower())); + bool isInFilterList = filterList.Any(s => statefulReplica.ServiceName.OriginalString.Contains(s, StringComparison.CurrentCultureIgnoreCase)); switch (filterType) { @@ -3464,7 +3461,7 @@ any processes (children) that the service process (parent) created/spawned. */ { if (filterList != null && filterType != ServiceFilterType.None) { - bool isInFilterList = filterList.Any(s => statelessInstance.ServiceName.OriginalString.ToLower().Contains(s.ToLower())); + bool isInFilterList = filterList.Any(s => statelessInstance.ServiceName.OriginalString.Contains(s, StringComparison.CurrentCultureIgnoreCase)); switch (filterType) { @@ -3723,7 +3720,7 @@ private void ProcessServiceConfiguration(string appTypeName, string codepackageN if (!string.IsNullOrWhiteSpace(appManifest) && appManifest.Contains($"<{ObserverConstants.RGPolicyNodeName} ") || appManifest.Contains($"<{ObserverConstants.RGSvcPkgPolicyNodeName} ")) { - ApplicationParameterList parameters = new(); + ApplicationParameterList parameters = []; FabricClientUtilities.AddParametersIfNotExists(parameters, appParameters); FabricClientUtilities.AddParametersIfNotExists(parameters, defaultParameters); @@ -3968,45 +3965,45 @@ private void LogAllAppResourceDataToCsv(string key) // Memory - Working set \\ - if (AllAppMemDataMb != null && AllAppMemDataMb.ContainsKey(key)) + if (AllAppMemDataMb != null && AllAppMemDataMb.TryGetValue(key, out FabricResourceUsageData memDataMbLogFrud)) { CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.MemoryConsumptionMb, "Average", - AllAppMemDataMb[key].AverageDataValue); + memDataMbLogFrud.AverageDataValue); CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.MemoryConsumptionMb, "Peak", - AllAppMemDataMb[key].MaxDataValue); + memDataMbLogFrud.MaxDataValue); } - if (AllAppMemDataPercent != null && AllAppMemDataPercent.ContainsKey(key)) + if (AllAppMemDataPercent != null && AllAppMemDataPercent.TryGetValue(key, out FabricResourceUsageData memDataPctLogFrud)) { CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.MemoryConsumptionPercentage, "Average", - AllAppMemDataPercent[key].AverageDataValue); + memDataPctLogFrud.AverageDataValue); CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.MemoryConsumptionPercentage, "Peak", - AllAppMemDataPercent[key].MaxDataValue); + memDataPctLogFrud.MaxDataValue); } // Memory - Private Bytes \\ if (IsWindows) { - if (AllAppPrivateBytesDataMb != null && AllAppPrivateBytesDataMb.ContainsKey(key)) + if (AllAppPrivateBytesDataMb != null && AllAppPrivateBytesDataMb.TryGetValue(key, out FabricResourceUsageData privBytesMbDataLogFrud)) { if (AllAppPrivateBytesDataMb.Any(x => x.Key == key)) { @@ -4015,18 +4012,18 @@ private void LogAllAppResourceDataToCsv(string key) key, ErrorWarningProperty.PrivateBytesMb, "Average", - AllAppPrivateBytesDataMb[key].AverageDataValue); + privBytesMbDataLogFrud.AverageDataValue); CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.PrivateBytesMb, "Peak", - AllAppPrivateBytesDataMb[key].MaxDataValue); + privBytesMbDataLogFrud.MaxDataValue); } } - if (AllAppPrivateBytesDataPercent != null && AllAppPrivateBytesDataPercent.ContainsKey(key)) + if (AllAppPrivateBytesDataPercent != null && AllAppPrivateBytesDataPercent.TryGetValue(key, out FabricResourceUsageData privBytesPctLogFrud)) { if (AllAppPrivateBytesDataPercent.Any(x => x.Key == key)) { @@ -4035,60 +4032,60 @@ private void LogAllAppResourceDataToCsv(string key) key, ErrorWarningProperty.PrivateBytesPercent, "Average", - AllAppPrivateBytesDataPercent[key].AverageDataValue); + privBytesPctLogFrud.AverageDataValue); CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.PrivateBytesPercent, "Peak", - AllAppPrivateBytesDataPercent[key].MaxDataValue); + privBytesPctLogFrud.MaxDataValue); } } } // Ports \\ - if (AllAppTotalActivePortsData != null && AllAppTotalActivePortsData.ContainsKey(key)) + if (AllAppTotalActivePortsData != null && AllAppTotalActivePortsData.TryGetValue(key, out FabricResourceUsageData activePortsLogFrod)) { CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.ActiveTcpPorts, "Total", - AllAppTotalActivePortsData[key].MaxDataValue); + activePortsLogFrod.MaxDataValue); } - if (AllAppEphemeralPortsData != null && AllAppEphemeralPortsData.ContainsKey(key)) + if (AllAppEphemeralPortsData != null && AllAppEphemeralPortsData.TryGetValue(key, out FabricResourceUsageData ePortsLogFrud)) { CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.ActiveEphemeralPorts, "Total", - AllAppEphemeralPortsData[key].MaxDataValue); + ePortsLogFrud.MaxDataValue); } // Handles - if (AllAppHandlesData != null && AllAppHandlesData.ContainsKey(key)) + if (AllAppHandlesData != null && AllAppHandlesData.TryGetValue(key, out FabricResourceUsageData handlesLogFrud)) { CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.AllocatedFileHandles, "Total", - AllAppHandlesData[key].MaxDataValue); + handlesLogFrud.MaxDataValue); } // Threads - if (AllAppThreadsData != null && AllAppThreadsData.ContainsKey(key)) + if (AllAppThreadsData != null && AllAppThreadsData.TryGetValue(key, out FabricResourceUsageData threadsLogFrud)) { CsvFileLogger.LogData( fileName, key, ErrorWarningProperty.ThreadCount, "Total", - AllAppHandlesData[key].MaxDataValue); + threadsLogFrud.MaxDataValue); } } catch (Exception e) when (e is ArgumentException or KeyNotFoundException or InvalidOperationException) diff --git a/FabricObserver/Observers/AzureStorageUploadObserver.cs b/FabricObserver/Observers/AzureStorageUploadObserver.cs index 449d2ecc..96090519 100644 --- a/FabricObserver/Observers/AzureStorageUploadObserver.cs +++ b/FabricObserver/Observers/AzureStorageUploadObserver.cs @@ -23,9 +23,13 @@ namespace FabricObserver.Observers // configured to do so - assuming correctly encrypted and specified ConnectionString for an Azure Storage Account, a container name, and other basic settings. // Since only Windows is supported for dumping service processes today by FO, this observer is not useful for Liunx in this version. // So, if you are deploying FO to Linux servers, then don't enable this observer (it won't do anything if it is enabled, so no need have it resident in memory). - public sealed class AzureStorageUploadObserver : ObserverBase + /// + /// Creates a new instance of the type. + /// + /// The StatelessServiceContext instance. + public sealed class AzureStorageUploadObserver(StatelessServiceContext context) : ObserverBase(null, context) { - private readonly Stopwatch stopwatch; + private readonly Stopwatch stopwatch = new(); // Only AppObserver is supported today. No other observers generate dmp files. private const string AppObserverDumpFolder = "MemoryDumps"; @@ -61,15 +65,6 @@ private CompressionLevel ZipCompressionLevel get; set; } = CompressionLevel.Optimal; - /// - /// Creates a new instance of the type. - /// - /// The StatelessServiceContext instance. - public AzureStorageUploadObserver(StatelessServiceContext context) : base(null, context) - { - stopwatch = new Stopwatch(); - } - public override async Task ObserveAsync(CancellationToken token) { // Since there is currently only support for Windows process dumps (by AppObserver only), there is no need to run this Observer on Linux (today..). @@ -105,7 +100,7 @@ public override async Task ObserveAsync(CancellationToken token) } // In case upload failed, also try and upload any zip files that remained in target local directory. - await ProcessFilesAsync(appObsDumpFolderPath, new[] { "*.zip", "*.dmp" }, token); + await ProcessFilesAsync(appObsDumpFolderPath, ["*.zip", "*.dmp"], token); await ReportAsync(token); CleanUp(); diff --git a/FabricObserver/Observers/CertificateObserver.cs b/FabricObserver/Observers/CertificateObserver.cs index 1eedf6b1..5820e441 100644 --- a/FabricObserver/Observers/CertificateObserver.cs +++ b/FabricObserver/Observers/CertificateObserver.cs @@ -21,7 +21,11 @@ namespace FabricObserver.Observers { - public sealed class CertificateObserver : ObserverBase + /// + /// Creates a new instance of the type. + /// + /// The StatelessServiceContext instance. + public sealed class CertificateObserver(StatelessServiceContext context) : ObserverBase(null, context) { private const string HowToUpdateCnCertsSfLinkHtml = "Click here to learn how to update expiring/expired certificates."; @@ -49,15 +53,6 @@ private List ExpiringWarnings get; set; } - /// - /// Creates a new instance of the type. - /// - /// The StatelessServiceContext instance. - public CertificateObserver(StatelessServiceContext context) : base (null, context) - { - - } - public int DaysUntilClusterExpireWarningThreshold { get; set; @@ -101,9 +96,9 @@ public override async Task ObserveAsync(CancellationToken token) await Initialize(token); - ExpiredWarnings = new List(); - ExpiringWarnings = new List(); - NotFoundWarnings = new List(); + ExpiredWarnings = []; + ExpiringWarnings = []; + NotFoundWarnings = []; // Unix LocalMachine X509Store is limited to the Root and CertificateAuthority stores. var store = new X509Store(IsWindows ? StoreName.My : StoreName.Root, StoreLocation.LocalMachine); @@ -122,7 +117,7 @@ public override async Task ObserveAsync(CancellationToken token) CheckByThumbprint(store, SecurityConfiguration.ClusterCertThumbprintOrCommonName, DaysUntilClusterExpireWarningThreshold); } - if (AppCertificateCommonNamesToObserve != null && AppCertificateCommonNamesToObserve.Any()) + if (AppCertificateCommonNamesToObserve != null && AppCertificateCommonNamesToObserve.Count != 0) { // App certificates foreach (string commonName in AppCertificateCommonNamesToObserve) @@ -132,7 +127,7 @@ public override async Task ObserveAsync(CancellationToken token) } } - if (AppCertificateThumbprintsToObserve != null && AppCertificateThumbprintsToObserve.Any()) + if (AppCertificateThumbprintsToObserve != null && AppCertificateThumbprintsToObserve.Count != 0) { // App certificates foreach (string thumbprint in AppCertificateThumbprintsToObserve) @@ -325,7 +320,7 @@ private async Task Initialize(CancellationToken token) ConfigurationSectionName, ObserverConstants.CertificateObserverAppCertificateThumbprints); - AppCertificateThumbprintsToObserve = !string.IsNullOrEmpty(appThumbprintsToObserve) ? JsonHelper.ConvertFromString>(appThumbprintsToObserve) : new List(); + AppCertificateThumbprintsToObserve = !string.IsNullOrEmpty(appThumbprintsToObserve) ? JsonHelper.ConvertFromString>(appThumbprintsToObserve) : []; } if (AppCertificateCommonNamesToObserve == null) @@ -334,7 +329,7 @@ private async Task Initialize(CancellationToken token) ConfigurationSectionName, ObserverConstants.CertificateObserverAppCertificateCommonNames); - AppCertificateCommonNamesToObserve = !string.IsNullOrEmpty(appCommonNamesToObserve) ? JsonHelper.ConvertFromString>(appCommonNamesToObserve) : new List(); + AppCertificateCommonNamesToObserve = !string.IsNullOrEmpty(appCommonNamesToObserve) ? JsonHelper.ConvertFromString>(appCommonNamesToObserve) : []; } await GetSecurityTypes(token); @@ -362,15 +357,15 @@ private async Task GetSecurityTypes(CancellationToken token) var certificateNode = xdoc.SelectNodes($"//sf:NodeType[@Name='{NodeType}']//sf:Certificates", nsmgr); - if (certificateNode != null ? certificateNode.Count == 0 : false) + if (certificateNode != null && certificateNode.Count == 0) { SecurityConfiguration.SecurityType = SecurityType.None; } else { - var clusterCertificateNode = certificateNode != null ? certificateNode.Item(0) != null ? certificateNode.Item(0).ChildNodes.Item(0) : null : null; + var clusterCertificateNode = certificateNode?.Item(0)?.ChildNodes.Item(0); - var commonNameAttribute = clusterCertificateNode != null ? clusterCertificateNode.Attributes != null ? clusterCertificateNode.Attributes.GetNamedItem("X509FindType") : null : null; + var commonNameAttribute = clusterCertificateNode?.Attributes?.GetNamedItem("X509FindType"); if (commonNameAttribute != null) { if (commonNameAttribute.Value == "FindBySubjectName") @@ -387,7 +382,7 @@ private async Task GetSecurityTypes(CancellationToken token) SecurityConfiguration.SecurityType = SecurityType.Thumbprint; SecurityConfiguration.ClusterCertThumbprintOrCommonName = clusterCertificateNode != null ? clusterCertificateNode.Attributes != null ? clusterCertificateNode.Attributes.GetNamedItem("X509FindValue").Value : null : null; - var secondaryThumbprintAttribute = clusterCertificateNode != null ? clusterCertificateNode.Attributes != null ? clusterCertificateNode.Attributes.GetNamedItem("X509FindValueSecondary") : null : null; + var secondaryThumbprintAttribute = clusterCertificateNode != null ? clusterCertificateNode.Attributes?.GetNamedItem("X509FindValueSecondary") : null; if (secondaryThumbprintAttribute != null) { diff --git a/FabricObserver/Observers/ContainerObserver.cs b/FabricObserver/Observers/ContainerObserver.cs index 70e8abfe..ac59cff5 100644 --- a/FabricObserver/Observers/ContainerObserver.cs +++ b/FabricObserver/Observers/ContainerObserver.cs @@ -22,7 +22,11 @@ namespace FabricObserver.Observers { - public sealed class ContainerObserver : ObserverBase + /// + /// Creates a new instance of the type. + /// + /// The StatelessServiceContext instance. + public sealed class ContainerObserver(StatelessServiceContext context) : ObserverBase(null, context) { private const int MaxProcessExitWaitTimeMS = 60000; private ConcurrentDictionary> allCpuDataPercentage; @@ -48,14 +52,7 @@ public ParallelOptions ParallelOptions get; private set; } - /// - /// Creates a new instance of the type. - /// - /// The StatelessServiceContext instance. - public ContainerObserver(StatelessServiceContext context) : base(null, context) - { - - } + private static readonly char[] separator = new[] { ' ' }; // OsbserverManager passes in a special token to ObserveAsync and ReportAsync that enables it to stop this observer outside of // of the SF runtime, but this token will also cancel when the runtime cancels the main token. @@ -225,7 +222,7 @@ private async Task InitializeAsync(CancellationToken token) TaskScheduler = TaskScheduler.Default }; - userTargetList = new List(); + userTargetList = []; deployedTargetList = new ConcurrentQueue(); ReplicaOrInstanceList = new ConcurrentQueue(); @@ -512,7 +509,7 @@ fed0da6f7bad sf-243-723d5795-01c7-477f-950e-45a400000000_2cc293c0-929c-5c40-bc } else { - if (error.ToLower().Contains("permission denied")) + if (error.Contains("permission denied", StringComparison.CurrentCultureIgnoreCase)) { msg += "elevated_docker_stats Capabilities may have been removed. " + "This is most likely due to an SF runtime upgrade, which unsets Linux caps because SF re-acls (so, touches) all binaries in application Code folders as part of the upgrade process. " + @@ -566,7 +563,7 @@ fed0da6f7bad sf-243-723d5795-01c7-477f-950e-45a400000000_2cc293c0-929c-5c40-bc } // Linux: Try and work around the unsetting of caps issues when SF runs a cluster upgrade. - if (!IsWindows && error.ToLower().Contains("permission denied")) + if (!IsWindows && error.Contains("permission denied", StringComparison.CurrentCultureIgnoreCase)) { // Throwing LinuxPermissionException here will eventually take down FO (by design). The failure will be logged and telemetry will be emitted, then // the exception will be re-thrown by ObserverManager and the FO process will fail fast exit. Then, SF will create a new instance of FO on the offending node which @@ -613,7 +610,7 @@ fed0da6f7bad sf-243-723d5795-01c7-477f-950e-45a400000000_2cc293c0-929c-5c40-bc continue; } - string[] stats = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + string[] stats = line.Split(separator, StringSplitOptions.RemoveEmptyEntries); // Something went wrong if the collection size is less than 4 given the supplied output table format: // {{.Container}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}} @@ -734,7 +731,7 @@ private async Task SetInstanceOrReplicaMonitoringList( { if (filterList != null && filterType != ServiceFilterType.None) { - bool isInFilterList = filterList.Any(s => statefulReplica.ServiceName.OriginalString.ToLower().Contains(s.ToLower())); + bool isInFilterList = filterList.Any(s => statefulReplica.ServiceName.OriginalString.Contains(s, StringComparison.CurrentCultureIgnoreCase)); switch (filterType) { @@ -765,7 +762,7 @@ private async Task SetInstanceOrReplicaMonitoringList( { if (filterList != null && filterType != ServiceFilterType.None) { - bool isInFilterList = filterList.Any(s => statelessInstance.ServiceName.OriginalString.ToLower().Contains(s.ToLower())); + bool isInFilterList = filterList.Any(s => statelessInstance.ServiceName.OriginalString.Contains(s, StringComparison.CurrentCultureIgnoreCase)); switch (filterType) { diff --git a/FabricObserver/Observers/DiskObserver.cs b/FabricObserver/Observers/DiskObserver.cs index 645a4724..2f2821ec 100644 --- a/FabricObserver/Observers/DiskObserver.cs +++ b/FabricObserver/Observers/DiskObserver.cs @@ -22,7 +22,11 @@ namespace FabricObserver.Observers { // DiskObserver monitors logical disk states (space consumption, queue length and folder sizes) and creates Service Fabric // Warning or Error Node-level health reports based on settings in ApplicationManifest.xml. - public sealed class DiskObserver : ObserverBase + /// + /// Creates a new instance of the type. + /// + /// The StatelessServiceContext instance. + public sealed partial class DiskObserver(StatelessServiceContext context) : ObserverBase(null, context) { // Data storage containers for post run analysis. private List> DiskAverageQueueLengthData; @@ -30,7 +34,7 @@ public sealed class DiskObserver : ObserverBase private List> DiskSpaceAvailableMbData; private List> DiskSpaceTotalMbData; private List> FolderSizeDataMb; - private readonly Stopwatch stopWatch; + private readonly Stopwatch stopWatch = new(); public int DiskSpacePercentErrorThreshold { @@ -69,15 +73,6 @@ public Dictionary FolderSizeConfigDataWarning get; set; } - /// - /// Creates a new instance of the type. - /// - /// The StatelessServiceContext instance. - public DiskObserver(StatelessServiceContext context) : base(null, context) - { - stopWatch = new Stopwatch(); - } - public override async Task ObserveAsync(CancellationToken token) { // If set, this observer will only run during the supplied interval. @@ -97,7 +92,7 @@ public override async Task ObserveAsync(CancellationToken token) DiskSpaceUsagePercentageData ??= new List>(driveCount); DiskSpaceAvailableMbData ??= new List>(driveCount); DiskSpaceTotalMbData ??= new List>(driveCount); - FolderSizeDataMb ??= new List>(); + FolderSizeDataMb ??= []; if (IsWindows) { @@ -215,7 +210,7 @@ private void ProcessFolderSizeConfig() if (!string.IsNullOrWhiteSpace(FolderPathsErrorThresholdPairs)) { - FolderSizeConfigDataError ??= new Dictionary(); + FolderSizeConfigDataError ??= []; AddFolderSizeConfigData(FolderPathsErrorThresholdPairs, false); } @@ -223,7 +218,7 @@ private void ProcessFolderSizeConfig() if (!string.IsNullOrWhiteSpace(FolderPathsWarningThresholdPairs)) { - FolderSizeConfigDataWarning ??= new Dictionary(); + FolderSizeConfigDataWarning ??= []; AddFolderSizeConfigData(FolderPathsWarningThresholdPairs, true); } } @@ -257,7 +252,7 @@ private void AddFolderSizeConfigData(string folderSizeConfig, bool isWarningThre // Contains env variable(s)? if (path.Contains('%')) { - if (Regex.Match(path, @"^%[a-zA-Z0-9_]+%").Success) + if (EnvRegex().Match(path).Success) { path = Environment.ExpandEnvironmentVariables(pairs[0]); } @@ -277,11 +272,11 @@ private void AddFolderSizeConfigData(string folderSizeConfig, bool isWarningThre { if (FolderSizeConfigDataWarning != null) { - if (!FolderSizeConfigDataWarning.ContainsKey(path)) + if (!FolderSizeConfigDataWarning.TryGetValue(path, out double folderSizeWarningThreshold)) { FolderSizeConfigDataWarning.Add(path, threshold); } - else if (FolderSizeConfigDataWarning[path] != threshold) // App Parameter upgrade? + else if (folderSizeWarningThreshold != threshold) // App Parameter upgrade? { FolderSizeConfigDataWarning[path] = threshold; } @@ -289,11 +284,11 @@ private void AddFolderSizeConfigData(string folderSizeConfig, bool isWarningThre } else if (FolderSizeConfigDataError != null) { - if (!FolderSizeConfigDataError.ContainsKey(path)) + if (!FolderSizeConfigDataError.TryGetValue(path, out double folderSizeErrorThreshold)) { FolderSizeConfigDataError.Add(path, threshold); } - else if (FolderSizeConfigDataError[path] != threshold) // App Parameter upgrade? + else if (folderSizeErrorThreshold != threshold) // App Parameter upgrade? { FolderSizeConfigDataError[path] = threshold; } @@ -404,7 +399,7 @@ private void CheckFolderSizeUsage(IDictionary data) // Contains Windows env variable(s)? if (IsWindows && path.Contains('%')) { - if (Regex.Match(path, @"^%[a-zA-Z0-9_]+%").Success) + if (EnvRegex().Match(path).Success) { path = Environment.ExpandEnvironmentVariables(item.Key); } @@ -498,14 +493,14 @@ public override Task ReportAsync(CancellationToken token) double errorThreshold = 0.0; double warningThreshold = 0.0; - if (FolderSizeConfigDataError?.Count > 0 && FolderSizeConfigDataError.ContainsKey(data.Id)) + if (FolderSizeConfigDataError?.Count > 0 && FolderSizeConfigDataError.TryGetValue(data.Id, out double fsDataSizeError)) { - errorThreshold = FolderSizeConfigDataError[data.Id]; + errorThreshold = fsDataSizeError; } - if (FolderSizeConfigDataWarning?.Count > 0 && FolderSizeConfigDataWarning.ContainsKey(data.Id)) + if (FolderSizeConfigDataWarning?.Count > 0 && FolderSizeConfigDataWarning.TryGetValue(data.Id, out double fsDataSizeWarning)) { - warningThreshold = FolderSizeConfigDataWarning[data.Id]; + warningThreshold = fsDataSizeWarning; } ProcessResourceDataReportHealth( @@ -695,5 +690,8 @@ private void CleanUp() } ObserverLogger.LogInfo("Completed CleanUp..."); } + + [GeneratedRegex(@"^%[a-zA-Z0-9_]+%")] + private static partial Regex EnvRegex(); } } \ No newline at end of file diff --git a/FabricObserver/Observers/FabricSystemObserver.cs b/FabricObserver/Observers/FabricSystemObserver.cs index b8d139dd..4aa861b9 100644 --- a/FabricObserver/Observers/FabricSystemObserver.cs +++ b/FabricObserver/Observers/FabricSystemObserver.cs @@ -30,7 +30,7 @@ public sealed class FabricSystemObserver : ObserverBase private readonly string[] processNameWatchList; private Stopwatch stopwatch; private bool checkPrivateWorkingSet; - private List<(string procName, int procId)> fabricSystemProcInfo = new(); + private List<(string procName, int procId)> fabricSystemProcInfo = []; // Health Report data container - For use in analysis to determine health state. private Dictionary> allCpuData; @@ -50,8 +50,8 @@ public FabricSystemObserver(StatelessServiceContext context) : base(null, contex // Linux if (!IsWindows) { - processNameWatchList = new[] - { + processNameWatchList = + [ "Fabric", "FabricDCA.dll", "FabricDnsService", @@ -62,13 +62,13 @@ public FabricSystemObserver(StatelessServiceContext context) : base(null, contex "FabricIS.dll", "FabricRM.exe", "FabricUS.dll" - }; + ]; } else { // Windows - processNameWatchList = new[] - { + processNameWatchList = + [ "EventStore.Service", "Fabric", "FabricApplicationGateway", @@ -81,7 +81,7 @@ public FabricSystemObserver(StatelessServiceContext context) : base(null, contex "FabricHost", "FabricIS", "FabricRM" - }; + ]; } } @@ -383,7 +383,7 @@ private Process[] GetDotnetLinuxProcessesByFirstArgument(string argument) if (cmdline.Contains(sfAppDir)) { - string bin = cmdline[(cmdline.LastIndexOf("/", StringComparison.Ordinal) + 1)..]; + string bin = cmdline[(cmdline.LastIndexOf('/') + 1)..]; if (string.Equals(argument, bin, StringComparison.InvariantCulture)) { @@ -440,7 +440,7 @@ private void Initialize() // CPU data if (allCpuData == null && (CpuErrorUsageThresholdPct > 0 || CpuWarnUsageThresholdPct > 0)) { - allCpuData = new Dictionary>(); + allCpuData = []; foreach (var proc in processNameWatchList) { @@ -452,7 +452,7 @@ private void Initialize() // Memory data if (allMemData == null && (MemErrorUsageThresholdMb > 0 || MemWarnUsageThresholdMb > 0)) { - allMemData = new Dictionary>(); + allMemData = []; foreach (var proc in processNameWatchList) { @@ -464,7 +464,7 @@ private void Initialize() // Ports if (allActiveTcpPortData == null && (ActiveTcpPortCountError > 0 || ActiveTcpPortCountWarning > 0)) { - allActiveTcpPortData = new Dictionary>(); + allActiveTcpPortData = []; foreach (var proc in processNameWatchList) { @@ -475,7 +475,7 @@ private void Initialize() if (allEphemeralTcpPortData == null && (ActiveEphemeralPortCountError > 0 || ActiveEphemeralPortCountWarning > 0)) { - allEphemeralTcpPortData = new Dictionary>(); + allEphemeralTcpPortData = []; foreach (var proc in processNameWatchList) { @@ -487,7 +487,7 @@ private void Initialize() // Handles if (allHandlesData == null && (AllocatedHandlesError > 0 || AllocatedHandlesWarning > 0)) { - allHandlesData = new Dictionary>(); + allHandlesData = []; foreach (var proc in processNameWatchList) { @@ -499,7 +499,7 @@ private void Initialize() // Threads if (allThreadsData == null && (ThreadCountError > 0 || ThreadCountWarning > 0)) { - allThreadsData = new Dictionary>(); + allThreadsData = []; foreach (var proc in processNameWatchList) { @@ -511,7 +511,7 @@ private void Initialize() // KVS LVIDs - Windows-only (EnableKvsLvidMonitoring will always be false otherwise) if (EnableKvsLvidMonitoring && allAppKvsLvidsData == null) { - allAppKvsLvidsData = new Dictionary>(); + allAppKvsLvidsData = []; foreach (var proc in processNameWatchList) { @@ -756,9 +756,9 @@ private async Task GetProcessInfoAsync(string procName, CancellationToken token) if (ActiveTcpPortCountError > 0 || ActiveTcpPortCountWarning > 0) { - if (allActiveTcpPortData.ContainsKey(dotnetArg)) + if (allActiveTcpPortData.TryGetValue(dotnetArg, out FabricResourceUsageData tcpPortsFrud)) { - allActiveTcpPortData[dotnetArg].AddData(activePortCount); + tcpPortsFrud.AddData(activePortCount); } } @@ -768,9 +768,9 @@ private async Task GetProcessInfoAsync(string procName, CancellationToken token) if (ActiveEphemeralPortCountError > 0 || ActiveEphemeralPortCountWarning > 0) { - if (allEphemeralTcpPortData.ContainsKey(dotnetArg)) + if (allEphemeralTcpPortData.TryGetValue(dotnetArg, out FabricResourceUsageData ePortsFrud)) { - allEphemeralTcpPortData[dotnetArg].AddData(activeEphemeralPortCount); + ePortsFrud.AddData(activeEphemeralPortCount); } } @@ -813,18 +813,18 @@ private async Task GetProcessInfoAsync(string procName, CancellationToken token) // Handles/FDs if (AllocatedHandlesError > 0 || AllocatedHandlesWarning > 0) { - if (allHandlesData.ContainsKey(dotnetArg)) + if (allHandlesData.TryGetValue(dotnetArg, out FabricResourceUsageData handlesFrud)) { - allHandlesData[dotnetArg].AddData(handles); + handlesFrud.AddData(handles); } } // Threads if (ThreadCountError > 0 || ThreadCountWarning > 0) { - if (allThreadsData.ContainsKey(dotnetArg)) + if (allThreadsData.TryGetValue(dotnetArg, out FabricResourceUsageData threadsFrud)) { - allThreadsData[dotnetArg].AddData(threads); + threadsFrud.AddData(threads); } } @@ -836,9 +836,9 @@ private async Task GetProcessInfoAsync(string procName, CancellationToken token) // GetProcessKvsLvidsUsedPercentage internally handles exceptions and will always return -1 when it fails. if (lvidPct > -1) { - if (allAppKvsLvidsData.ContainsKey(dotnetArg)) + if (allAppKvsLvidsData.TryGetValue(dotnetArg, out FabricResourceUsageData lvidsFrud)) { - allAppKvsLvidsData[dotnetArg].AddData(lvidPct); + lvidsFrud.AddData(lvidPct); } } } @@ -848,9 +848,9 @@ private async Task GetProcessInfoAsync(string procName, CancellationToken token) { float processMem = ProcessInfoProvider.Instance.GetProcessWorkingSetMb(procId, dotnetArg, token, checkPrivateWorkingSet); - if (allMemData.ContainsKey(dotnetArg)) + if (allMemData.TryGetValue(dotnetArg, out FabricResourceUsageData memMbFrud)) { - allMemData[dotnetArg].AddData(processMem); + memMbFrud.AddData(processMem); } } @@ -892,9 +892,9 @@ private async Task GetProcessInfoAsync(string procName, CancellationToken token) // process is no longer running if cpu == -1. if (cpu >= 0) { - if (allCpuData.ContainsKey(dotnetArg)) + if (allCpuData.TryGetValue(dotnetArg, out FabricResourceUsageData cpuFrud)) { - allCpuData[dotnetArg].AddData(cpu); + cpuFrud.AddData(cpu); } } diff --git a/FabricObserver/Observers/NetworkObserver.cs b/FabricObserver/Observers/NetworkObserver.cs index 6a3f146a..bc267d46 100644 --- a/FabricObserver/Observers/NetworkObserver.cs +++ b/FabricObserver/Observers/NetworkObserver.cs @@ -12,9 +12,7 @@ using System.Linq; using System.Net; using System.Net.Http; -using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Text; using System.Threading; using System.Threading.Tasks; using FabricObserver.Observers.MachineInfoModel; @@ -31,16 +29,20 @@ namespace FabricObserver.Observers /// The output (a local file) is used by the API service and the HTML frontend (https://[domain:[port]]/api/ObserverManager). /// Health Report processor will also emit diagnostic telemetry if configured in Settings.xml. /// - public sealed class NetworkObserver : ObserverBase + /// + /// Creates a new instance of the type. + /// + /// The StatelessServiceContext instance. + public sealed class NetworkObserver(StatelessServiceContext context) : ObserverBase(null, context) { private const int MaxTcpConnTestRetries = 5; - private readonly List defaultConfig = new() - { + private readonly List defaultConfig = + [ new NetworkObserverConfig { TargetApp = "fabric:/test", - Endpoints = new List - { + Endpoints = + [ new() { HostName = "www.microsoft.com", Port = 443 @@ -53,25 +55,16 @@ public sealed class NetworkObserver : ObserverBase HostName = "www.google.com", Port = 443 } - } + ] } - }; - private readonly List userConfig = new(); - private readonly List connectionStatus = new(); - private readonly Dictionary connEndpointTestResults = new(); - private readonly Stopwatch stopwatch; + ]; + private readonly List userConfig = []; + private readonly List connectionStatus = []; + private readonly Dictionary connEndpointTestResults = []; + private readonly Stopwatch stopwatch = new(); private HealthState healthState = HealthState.Ok; private int tcpConnTestRetried; - /// - /// Creates a new instance of the type. - /// - /// The StatelessServiceContext instance. - public NetworkObserver(StatelessServiceContext context) : base(null, context) - { - stopwatch = new Stopwatch(); - } - public override async Task ObserveAsync(CancellationToken token) { // If set, this observer will only run during the supplied interval. @@ -253,56 +246,6 @@ public override Task ReportAsync(CancellationToken token) return Task.CompletedTask; } - private static string GetNetworkInterfaceInfo(CancellationToken token) - { - try - { - var iPGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); - var nics = NetworkInterface.GetAllNetworkInterfaces(); - - if (nics.Length < 1) - { - return string.Empty; - } - - var interfaceInfo = new StringBuilder($"Network Interface information for {iPGlobalProperties.HostName}:{Environment.NewLine} "); - - foreach (var nic in nics) - { - token.ThrowIfCancellationRequested(); - - _ = interfaceInfo.Append($"{Environment.NewLine}{nic.Description}{Environment.NewLine}"); - _ = interfaceInfo.AppendFormat($" Interface type : {0}{Environment.NewLine}", nic.NetworkInterfaceType); - _ = interfaceInfo.AppendFormat($" Operational status: {0}{Environment.NewLine}", nic.OperationalStatus); - - // Traffic. - if (nic.OperationalStatus != OperationalStatus.Up) - { - continue; - } - - _ = interfaceInfo.AppendLine(" Traffic Info:"); - - var stats = nic.GetIPv4Statistics(); - - _ = interfaceInfo.AppendFormat($" Bytes received: {0}{Environment.NewLine}", stats.BytesReceived); - _ = interfaceInfo.AppendFormat($" Bytes sent: {0}{Environment.NewLine}", stats.BytesSent); - _ = interfaceInfo.AppendFormat($" Incoming Packets With Errors: {0}{Environment.NewLine}", stats.IncomingPacketsWithErrors); - _ = interfaceInfo.AppendFormat($" Outgoing Packets With Errors: {0}{Environment.NewLine}", stats.OutgoingPacketsWithErrors); - _ = interfaceInfo.AppendLine(); - } - - var s = interfaceInfo.ToString(); - _ = interfaceInfo.Clear(); - - return s; - } - catch (NetworkInformationException) - { - return string.Empty; - } - } - private async Task InitializeAsync() { Token.ThrowIfCancellationRequested(); @@ -373,9 +316,9 @@ private void InternetConnectionStateIsConnected() } // Don't re-test endpoint if it has already been tested for a different targetApp. - if (connEndpointTestResults.ContainsKey(endpoint.HostName)) + if (connEndpointTestResults.TryGetValue(endpoint.HostName, out bool value)) { - SetHealthState(endpoint, config.TargetApp, connEndpointTestResults[endpoint.HostName]); + SetHealthState(endpoint, config.TargetApp, value); continue; } @@ -437,11 +380,7 @@ private void InternetConnectionStateIsConnected() } SetHealthState(endpoint, config.TargetApp, passed); - - if (!connEndpointTestResults.ContainsKey(endpoint.HostName)) - { - connEndpointTestResults.Add(endpoint.HostName, passed); - } + _ = connEndpointTestResults.TryAdd(endpoint.HostName, passed); } } } diff --git a/FabricObserver/Observers/NodeObserver.cs b/FabricObserver/Observers/NodeObserver.cs index 7a653b75..bf1b3586 100644 --- a/FabricObserver/Observers/NodeObserver.cs +++ b/FabricObserver/Observers/NodeObserver.cs @@ -16,9 +16,13 @@ namespace FabricObserver.Observers { // This observer monitors machine level resource usage across CPU, Memory, TCP ports, (Linux) File handles, and (Windows) firewall rules. - public sealed class NodeObserver : ObserverBase + /// + /// Creates a new instance of the type. + /// + /// The StatelessServiceContext instance. + public sealed class NodeObserver(StatelessServiceContext context) : ObserverBase(null, context) { - private readonly Stopwatch stopwatch; + private readonly Stopwatch stopwatch = new(); // These are public properties because they are used in unit tests. public FabricResourceUsageData MemDataInUse; @@ -132,15 +136,6 @@ public bool EnableNodeSnapshots get; set; } - /// - /// Creates a new instance of the type. - /// - /// The StatelessServiceContext instance. - public NodeObserver(StatelessServiceContext context) : base(null, context) - { - stopwatch = new Stopwatch(); - } - public override async Task ObserveAsync(CancellationToken token) { // If set, this observer will only run during the supplied interval. diff --git a/FabricObserver/Observers/ObserverManager.cs b/FabricObserver/Observers/ObserverManager.cs index 11a52fa3..10da8bd9 100644 --- a/FabricObserver/Observers/ObserverManager.cs +++ b/FabricObserver/Observers/ObserverManager.cs @@ -24,7 +24,6 @@ using FabricObserver.Utilities.ServiceFabric; using ConfigurationSettings = System.Fabric.Description.ConfigurationSettings; using System.Runtime.Versioning; -using StartupServicesModel; namespace FabricObserver.Observers { @@ -55,7 +54,7 @@ private List Observers private CancellationTokenSource linkedSFRuntimeObserverTokenSource; // Folks often use their own version numbers. This is for internal diagnostic telemetry. - private const string InternalVersionNumber = "3.2.16"; + private const string InternalVersionNumber = "3.3.0"; private static FabricClient FabricClientInstance => FabricClientUtilities.FabricClientSingleton; @@ -781,8 +780,8 @@ private Dictionary GetObserverData() { var observerData = new Dictionary(); var enabledObs = Observers.Where(o => o.IsEnabled); - string[] builtInObservers = new string[] - { + string[] builtInObservers = + [ ObserverConstants.AppObserverName, ObserverConstants.AzureStorageUploadObserverName, ObserverConstants.CertificateObserverName, @@ -792,7 +791,7 @@ private Dictionary GetObserverData() ObserverConstants.NetworkObserverName, ObserverConstants.NodeObserverName, ObserverConstants.OSObserverName - }; + ]; foreach (var obs in enabledObs) { @@ -808,7 +807,7 @@ ObserverConstants.ContainerObserverName or ObserverConstants.NetworkObserverName or ObserverConstants.FabricSystemObserverName) { - if (!observerData.ContainsKey(obs.ObserverName)) + if (!observerData.TryGetValue(obs.ObserverName, out ObserverData value)) { _ = observerData.TryAdd( obs.ObserverName, @@ -825,9 +824,9 @@ ObserverConstants.NetworkObserverName or } else { - observerData[obs.ObserverName].ErrorCount = obs.CurrentErrorCount; - observerData[obs.ObserverName].WarningCount = obs.CurrentWarningCount; - observerData[obs.ObserverName].ServiceData = + value.ErrorCount = obs.CurrentErrorCount; + value.WarningCount = obs.CurrentWarningCount; + value.ServiceData = new ServiceData { MonitoredAppCount = obs.MonitoredAppCount, diff --git a/FabricObserver/PackageRoot/Config/Settings.xml b/FabricObserver/PackageRoot/Config/Settings.xml index 97117d1b..145e6145 100644 --- a/FabricObserver/PackageRoot/Config/Settings.xml +++ b/FabricObserver/PackageRoot/Config/Settings.xml @@ -400,7 +400,6 @@
- diff --git a/FabricObserver/PackageRoot/Data/Plugins/Readme.txt b/FabricObserver/PackageRoot/Data/Plugins/Readme.txt index 75563031..83c64d3a 100644 --- a/FabricObserver/PackageRoot/Data/Plugins/Readme.txt +++ b/FabricObserver/PackageRoot/Data/Plugins/Readme.txt @@ -7,8 +7,8 @@ Note that the observer API lives in its own library, FabricObserver.Extensibilit 1. Create a new .NET 6 Library project. 2. Install the same version of the Microsoft.ServiceFabricApps.FabricObserver.Extensibility nupkg from https://www.nuget.org/profiles/ServiceFabricApps as the version of FabricObserver you are deploying. - E.g., 3.2.16 if you are going to deploy FO 3.2.16. - NOTE: You can also consume the entire FabricObserver 3.2.16 nupkg to build your plugin. Please see the SampleObserverPlugin project's csproj file for more information. + E.g., 3.3.0 if you are going to deploy FO 3.3.0. + NOTE: You can also consume the entire FabricObserver 3.3.0 nupkg to build your plugin. Please see the SampleObserverPlugin project's csproj file for more information. 3. Write an observer! @@ -68,5 +68,5 @@ cd C:\Users\me\source\repos\service-fabric-observer ./Build-FabricObserver ./Build-NugetPackages -The output from the above commands contains FabricObserver platform-specific nupkgs and a package you have to use for plugin authoring named Microsoft.ServiceFabricApps.FabricObserver.Extensibility.3.2.16.nupkg. Nupkg files from above command would be located in +The output from the above commands contains FabricObserver platform-specific nupkgs and a package you have to use for plugin authoring named Microsoft.ServiceFabricApps.FabricObserver.Extensibility.3.3.0.nupkg. Nupkg files from above command would be located in C:\Users\me\source\repos\service-fabric-observer\bin\release\FabricObserver\Nugets. \ No newline at end of file diff --git a/FabricObserver/PackageRoot/ServiceManifest.xml b/FabricObserver/PackageRoot/ServiceManifest.xml index 0205ea39..51a8c823 100644 --- a/FabricObserver/PackageRoot/ServiceManifest.xml +++ b/FabricObserver/PackageRoot/ServiceManifest.xml @@ -1,6 +1,6 @@  @@ -9,7 +9,7 @@ This name must match the string used in RegisterServiceType call in Program.cs. --> - + FabricObserver @@ -19,10 +19,10 @@ - + - + \ No newline at end of file diff --git a/FabricObserver/PackageRoot/ServiceManifest_linux.xml b/FabricObserver/PackageRoot/ServiceManifest_linux.xml index 75356e22..b712593c 100644 --- a/FabricObserver/PackageRoot/ServiceManifest_linux.xml +++ b/FabricObserver/PackageRoot/ServiceManifest_linux.xml @@ -1,6 +1,6 @@  @@ -11,7 +11,7 @@ - + setcaps.sh @@ -27,10 +27,10 @@ - + - + \ No newline at end of file diff --git a/FabricObserverApp/ApplicationPackageRoot/ApplicationManifest.xml b/FabricObserverApp/ApplicationPackageRoot/ApplicationManifest.xml index 9a2eb5e4..ef5e4e73 100644 --- a/FabricObserverApp/ApplicationPackageRoot/ApplicationManifest.xml +++ b/FabricObserverApp/ApplicationPackageRoot/ApplicationManifest.xml @@ -1,6 +1,6 @@  - + @@ -249,7 +249,7 @@ should match the Name and Version attributes of the ServiceManifest element defined in the ServiceManifest.xml file. --> - + diff --git a/FabricObserverTests/FabricObserverTests.csproj b/FabricObserverTests/FabricObserverTests.csproj index 477e0004..d47cf55a 100644 --- a/FabricObserverTests/FabricObserverTests.csproj +++ b/FabricObserverTests/FabricObserverTests.csproj @@ -2,23 +2,22 @@ false FabricObserverTests - net6.0 + net8.0 disable win-x64 - CS0414 FabricObserverTests FabricObserverTests x64 - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/FabricObserverTests/HealthReportNotFoundException.cs b/FabricObserverTests/HealthReportNotFoundException.cs index db0a75e7..79a95998 100644 --- a/FabricObserverTests/HealthReportNotFoundException.cs +++ b/FabricObserverTests/HealthReportNotFoundException.cs @@ -4,7 +4,6 @@ // ------------------------------------------------------------ using System; -using System.Runtime.Serialization; namespace FabricObserverTests { @@ -37,14 +36,5 @@ public HealthReportNotFoundException(string message) : base(message) public HealthReportNotFoundException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Creates an instance of HealthReportNotFoundException. - /// - /// SerializationInfo - /// StreamingContext - protected HealthReportNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } \ No newline at end of file diff --git a/FabricObserverTests/ObserverTests.cs b/FabricObserverTests/ObserverTests.cs index 8d8fd975..4ccada77 100644 --- a/FabricObserverTests/ObserverTests.cs +++ b/FabricObserverTests/ObserverTests.cs @@ -53,6 +53,7 @@ public class ObserverTests private static FabricClient FabricClientSingleton => FabricClientUtilities.FabricClientSingleton; [ClassInitialize] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Noise..")] public static async Task TestClassStartUp(TestContext testContext) { if (!IsLocalSFRuntimePresent()) @@ -113,11 +114,11 @@ public static async Task TestClassStartUp(TestContext testContext) private static async Task DeployTestAppsAppsAsync() { - await DeployHealthMetricsAppAsync(); - await DeployTestApp42Async(); - await DeployVotingAppAsync(); - await DeployCpuStressAppAsync(); - await DeployPortTestAppAsync(); + await DeployHealthMetricsAppAsync().ConfigureAwait(false); + await DeployTestApp42Async().ConfigureAwait(false); + await DeployVotingAppAsync().ConfigureAwait(false); + await DeployCpuStressAppAsync().ConfigureAwait(false); + await DeployPortTestAppAsync().ConfigureAwait(false); // Wait a little extra time for apps to be fully in ready state. await Task.Delay(5000); @@ -480,7 +481,7 @@ private static async Task DeployPortTestAppAsync() StatelessServiceDescription serviceDescription = new() { - ServiceName = new Uri(appName + "/PortTestService"), + ServiceName = new Uri($"{appName}/PortTestService"), ServiceTypeName = "PortTestServiceType", PartitionSchemeDescription = new SingletonPartitionSchemeDescription(), ApplicationName = new Uri(appName), @@ -489,8 +490,7 @@ private static async Task DeployPortTestAppAsync() }; await FabricClientSingleton.ServiceManager.CreateServiceAsync(serviceDescription); - - await Task.Delay(TimeSpan.FromSeconds(15)); + await Task.Delay(TimeSpan.FromSeconds(30)); } catch (FabricException fe) { @@ -899,7 +899,7 @@ public async Task AppObserver_InitializeAsync_TargetAppType_ServiceExcludeList_E await obs.InitializeAsync(); var deployedTargets = obs.ReplicaOrInstanceList; - Assert.IsTrue(deployedTargets.Any()); + Assert.IsTrue(deployedTargets.Count != 0); Assert.IsFalse(deployedTargets.Any(t => t.ServiceName.OriginalString.Contains("DoctorActorService"))); } @@ -913,7 +913,7 @@ public async Task AppObserver_InitializeAsync_TargetApp_ServiceExcludeList_Ensur await obs.InitializeAsync(); var deployedTargets = obs.ReplicaOrInstanceList; - Assert.IsTrue(deployedTargets.Any()); + Assert.IsTrue(deployedTargets.Count != 0); Assert.IsFalse(deployedTargets.Any(t => t.ServiceName.OriginalString.Contains("DoctorActorServiceType"))); } @@ -927,7 +927,7 @@ public async Task AppObserver_InitializeAsync_TargetAppType_ServiceIncludeList_E await obs.InitializeAsync(); var deployedTargets = obs.ReplicaOrInstanceList; - Assert.IsTrue(deployedTargets.Any()); + Assert.IsTrue(deployedTargets.Count != 0); Assert.IsTrue(deployedTargets.All(t => t.ServiceName.OriginalString.Contains("DoctorActorService"))); } @@ -941,7 +941,7 @@ public async Task AppObserver_InitializeAsync_TargetApp_ServiceIncludeList_Ensur await obs.InitializeAsync(); var deployedTargets = obs.ReplicaOrInstanceList; - Assert.IsTrue(deployedTargets.Any()); + Assert.IsTrue(deployedTargets.Count != 0); Assert.IsTrue(deployedTargets.All(t => t.ServiceName.OriginalString.Contains("DoctorActorService"))); } @@ -957,7 +957,7 @@ public async Task AppObserver_InitializeAsync_TargetAppType_MultiServiceExcludeL await obs.InitializeAsync(); var serviceReplicas = obs.ReplicaOrInstanceList; - Assert.IsTrue(serviceReplicas.Any()); + Assert.IsTrue(serviceReplicas.Count != 0); // You can't supply multiple Exclude lists for the same target app/type. None of the target services will be excluded.. Assert.IsTrue( @@ -976,7 +976,7 @@ public async Task AppObserver_InitializeAsync_TargetApp_MultiServiceExcludeList_ await obs.InitializeAsync(); var serviceReplicas = obs.ReplicaOrInstanceList; - Assert.IsTrue(serviceReplicas.Any()); + Assert.IsTrue(serviceReplicas.Count != 0); // You can't supply multiple Exclude lists for the same target app/type. None of the target services will be excluded.. Assert.IsTrue( @@ -995,7 +995,7 @@ public async Task AppObserver_InitializeAsync_TargetAppType_MultiServiceIncludeL await obs.InitializeAsync(); var serviceReplicas = obs.ReplicaOrInstanceList; - Assert.IsTrue(serviceReplicas.Any()); + Assert.IsTrue(serviceReplicas.Count != 0); Assert.IsTrue(serviceReplicas.Count == 2); } @@ -1009,11 +1009,11 @@ public async Task AppObserver_InitializeAsync_TargetApp_MultiServiceIncludeList_ await obs.InitializeAsync(); var deployedTargets = obs.ReplicaOrInstanceList; - Assert.IsTrue(deployedTargets.Any()); + Assert.IsTrue(deployedTargets.Count != 0); await obs.InitializeAsync(); var serviceReplicas = obs.ReplicaOrInstanceList; - Assert.IsTrue(serviceReplicas.Any()); + Assert.IsTrue(serviceReplicas.Count != 0); Assert.IsTrue(serviceReplicas.Count == 2); } @@ -1297,7 +1297,7 @@ await FabricClientSingleton.ApplicationManager.UpgradeApplicationAsync( var defaultParameters = applicationTypeList.First(a => a.ApplicationTypeVersion == "1.0.0").DefaultParameters; Assert.IsTrue(defaultParameters.Any()); - ApplicationParameterList parameters = new(); + ApplicationParameterList parameters = []; // Fill parameter list with app and default parameters. The position of these matters (first add app parameters. Then, add default parameters). FabricClientUtilities.AddParametersIfNotExists(parameters, appParameters); @@ -1380,7 +1380,7 @@ public async Task Ensure_ConcurrentQueue_Collection_Has_Data_CPU_Win32Impl() FabricClientUtilities fabricClientUtilities = new(NodeName); var services = await fabricClientUtilities.GetAllDeployedReplicasOrInstancesAsync(true, Token); - Assert.IsTrue(services.Any()); + Assert.IsTrue(services.Count != 0); ConcurrentDictionary> AllAppCpuData = new(); ConcurrentQueue serviceProcs = new(); @@ -1422,7 +1422,7 @@ public async Task Ensure_ConcurrentQueue_Collection_Has_Data_CPU_Win32Impl() } }); - Assert.IsTrue(AllAppCpuData.Any() && serviceProcs.Any()); + Assert.IsTrue(!AllAppCpuData.IsEmpty && !serviceProcs.IsEmpty); Assert.IsTrue(serviceProcs.Count == AllAppCpuData.Count); TimeSpan duration = TimeSpan.FromSeconds(3); @@ -1453,12 +1453,12 @@ public async Task Ensure_ConcurrentQueue_Collection_Has_Data_CPU_Win32Impl() } [TestMethod] - public async Task Ensure_ConcurrentQueue_Collection_Has_Data_CPU_NET6ProcessImpl() + public async Task Ensure_ConcurrentQueue_Collection_Has_Data_CPU_NET8ProcessImpl() { FabricClientUtilities fabricClientUtilities = new(NodeName); var services = await fabricClientUtilities.GetAllDeployedReplicasOrInstancesAsync(true, Token); - Assert.IsTrue(services.Any()); + Assert.IsTrue(services.Count != 0); ConcurrentDictionary> AllAppCpuData = new(); ConcurrentQueue serviceProcs = new(); @@ -1500,7 +1500,7 @@ public async Task Ensure_ConcurrentQueue_Collection_Has_Data_CPU_NET6ProcessImpl } }); - Assert.IsTrue(AllAppCpuData.Any() && serviceProcs.Any()); + Assert.IsTrue(!AllAppCpuData.IsEmpty && !serviceProcs.IsEmpty); Assert.IsTrue(serviceProcs.Count == AllAppCpuData.Count); TimeSpan duration = TimeSpan.FromSeconds(3); @@ -1536,7 +1536,7 @@ public async Task Ensure_CircularBuffer_Collection_Has_Data_CPU_Win32Impl() FabricClientUtilities fabricClientUtilities = new(NodeName); var services = await fabricClientUtilities.GetAllDeployedReplicasOrInstancesAsync(true, Token); - Assert.IsTrue(services.Any()); + Assert.IsTrue(services.Count != 0); ConcurrentDictionary> AllAppCpuData = new(); ConcurrentQueue serviceProcs = new(); @@ -1578,7 +1578,7 @@ public async Task Ensure_CircularBuffer_Collection_Has_Data_CPU_Win32Impl() } }); - Assert.IsTrue(AllAppCpuData.Any() && serviceProcs.Any()); + Assert.IsTrue(!AllAppCpuData.IsEmpty && !serviceProcs.IsEmpty); Assert.IsTrue(serviceProcs.Count == AllAppCpuData.Count); TimeSpan duration = TimeSpan.FromSeconds(3); @@ -1609,12 +1609,12 @@ public async Task Ensure_CircularBuffer_Collection_Has_Data_CPU_Win32Impl() } [TestMethod] - public async Task Ensure_CircularBuffer_Collection_Has_Data_CPU_NET6ProcessImpl() + public async Task Ensure_CircularBuffer_Collection_Has_Data_CPU_NET8ProcessImpl() { FabricClientUtilities fabricClientUtilities = new(NodeName); var services = await fabricClientUtilities.GetAllDeployedReplicasOrInstancesAsync(true, Token); - Assert.IsTrue(services.Any()); + Assert.IsTrue(services.Count != 0); ConcurrentDictionary> AllAppCpuData = new(); ConcurrentQueue serviceProcs = new(); @@ -1656,7 +1656,7 @@ public async Task Ensure_CircularBuffer_Collection_Has_Data_CPU_NET6ProcessImpl( } }); - Assert.IsTrue(AllAppCpuData.Any() && serviceProcs.Any()); + Assert.IsTrue(!AllAppCpuData.IsEmpty && !serviceProcs.IsEmpty); Assert.IsTrue(serviceProcs.Count == AllAppCpuData.Count); TimeSpan duration = TimeSpan.FromSeconds(3); @@ -1706,7 +1706,7 @@ public async Task AppObserver_DumpProcessOnWarning_SuccessfulDumpCreation() var dmps = Directory.GetFiles(obs.DumpsPath, "*.dmp"); - Assert.IsTrue(dmps != null && dmps.Any()); + Assert.IsTrue(dmps != null && dmps.Length != 0); // VotingData service, and two helper codepackage binaries. Assert.IsTrue(dmps.All(d => d.Contains("VotingData") || d.Contains("ConsoleApp6") || d.Contains("ConsoleApp7"))); @@ -1742,7 +1742,7 @@ public async Task AppObserver_DumpProcessOnError_SuccessfulDumpCreation() var dmps = Directory.GetFiles(obs.DumpsPath, "*.dmp"); - Assert.IsTrue(dmps != null && dmps.Any()); + Assert.IsTrue(dmps != null && dmps.Length != 0); // VotingData service, and two helper codepackage binaries. Assert.IsTrue(dmps.All(d => d.Contains("VotingData") || d.Contains("ConsoleApp6") || d.Contains("ConsoleApp7"))); @@ -2694,7 +2694,7 @@ public async Task AppObserver_ETW_EventData_RG_ValuesAreNonZero() Assert.IsTrue(telemData.Count > 0); telemData = telemData.Where(t => t.ApplicationName == "fabric:/Voting").ToList(); - Assert.IsTrue(telemData.Any()); + Assert.IsTrue(telemData.Count != 0); foreach (var data in telemData) { @@ -2754,7 +2754,7 @@ public async Task AppObserver_ETW_PrivateBytes_Multiple_CodePackages_ValuesAreNo t => t.ApplicationName == "fabric:/Voting" && t.HealthState == HealthState.Warning).ToList(); // 2 service code packages + 2 helper code packages (VotingData) * 2 metrics = 8 warnings... - Assert.IsTrue(telemData.Any() && telemData.Count == 8); + Assert.IsTrue(telemData.Count != 0 && telemData.Count == 8); } // Private Bytes @@ -2875,7 +2875,7 @@ public async Task DiskObserver_ETW_EventData_IsTelemetryData_Warnings() Assert.IsTrue(telemData.Count > 0); telemData = telemData.Where(d => d.HealthState == HealthState.Warning).ToList(); - Assert.IsTrue(telemData.Any()); + Assert.IsTrue(telemData.Count != 0); foreach (var data in telemData) { @@ -2954,7 +2954,7 @@ public async Task FabricSystemObserver_ETW_EventData_Is_SystemServiceTelemetryDa Assert.IsTrue(telemData.Count > 0); telemData = telemData.Where(d => d.HealthState == HealthState.Warning).ToList(); - Assert.IsTrue(telemData.Any()); + Assert.IsTrue(telemData.Count != 0); foreach (var data in telemData) { @@ -3059,7 +3059,7 @@ public async Task NodeObserver_ETW_EventData_IsNodeTelemetryData_Warnings() Assert.IsTrue(telemData.Count > 0); telemData = telemData.Where(d => d.HealthState == HealthState.Warning).ToList(); - Assert.IsTrue(telemData.Any()); + Assert.IsTrue(telemData.Count != 0); foreach (var data in telemData) { diff --git a/FabricObserverTests/VotingApp.zip b/FabricObserverTests/VotingApp.zip index 2f0ea101..33d2446d 100644 Binary files a/FabricObserverTests/VotingApp.zip and b/FabricObserverTests/VotingApp.zip differ diff --git a/README.md b/README.md index f5b266f1..bb223cf6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -## FabricObserver 3.2.16 +## FabricObserver 3.3.0 (.NET 8) [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fservice-fabric-observer%2Fmain%2FDocumentation%2FDeployment%2Fservice-fabric-observer.json) -[**FabricObserver (FO)**](https://github.com/microsoft/service-fabric-observer/releases) is a production-ready watchdog service with an easy-to-use extensibility model, written as a stateless, singleton Service Fabric **.NET 6** application that by default +[**FabricObserver (FO)**](https://github.com/microsoft/service-fabric-observer/releases) is a production-ready watchdog service with an easy-to-use extensibility model, written as a stateless, singleton Service Fabric **.NET 8** application that by default 1. Monitors a broad range of physical machine resources that tend to be very important to all Service Fabric services and maps these metrics to the related Service Fabric entities. 2. Runs on multiple versions of Windows Server and Ubuntu. @@ -10,7 +10,7 @@ 4. Supports [Configuration Setting Application Updates](/Documentation/Using.md#parameterUpdates) for any observer for any supported setting. 5. Is actively developed in the open. -> FabricObserver targets SF runtime versions 9 and higher. +> FabricObserver targets SF runtime versions 9.1 and higher. Starting with version 3.3.0, you must deploy the self-contained release package unless you are deploying to a cluster running SF Version 10.1 CU3 or higher, then you can deploy framework-dependent release. FO is a Stateless Service Fabric Application composed of a single service that runs on every node in your cluster, so it can be deployed and run alongside your applications without any changes to them. Each FO service instance knows nothing about other FO instances in the cluster, by design. @@ -91,7 +91,7 @@ see [FOAzurePipeline.yaml](/FOAzurePipeline.yaml) for msazure devops build tasks .net6 installed (if you deploy VM images from Azure gallery, then they will not have .net6 installed), then you must deploy the SelfContained package. ### Deploy FabricObserver -**Note: You must deploy this version (3.2.16) to clusters that are running SF 9.0 and above. This version also requires .NET 6.** +**Note: You must deploy this version (3.3.0) to clusters that are running SF 9.0 and above. This version also requires .NET 6.** You can deploy FabricObserver (and ClusterObserver) using Visual Studio (if you build the sources yourself), PowerShell or ARM. Please note that this version of FabricObserver no longer supports the DefaultServices node in ApplicationManifest.xml. This means that should you deploy using PowerShell, you must create an instance of the service as the last command in your script. This was done to support ARM deployment, specifically. The StartupServices.xml file you see in the FabricHealerApp project now contains the service information once held in ApplicationManifest's DefaultServices node. Note that this information is primarily useful for deploying from Visual Studio. @@ -131,15 +131,15 @@ Connect-ServiceFabricCluster -ConnectionEndpoint @('sf-win-cluster.westus2.cloud #Copy $path contents (FO app package) to server: -Copy-ServiceFabricApplicationPackage -ApplicationPackagePath $path -CompressPackage -ApplicationPackagePathInImageStore FO3215 -TimeoutSec 1800 +Copy-ServiceFabricApplicationPackage -ApplicationPackagePath $path -CompressPackage -ApplicationPackagePathInImageStore FO330 -TimeoutSec 1800 #Register FO ApplicationType: -Register-ServiceFabricApplicationType -ApplicationPathInImageStore FO3215 +Register-ServiceFabricApplicationType -ApplicationPathInImageStore FO330 #Create FO application (if not already deployed at lesser version): -New-ServiceFabricApplication -ApplicationName fabric:/FabricObserver -ApplicationTypeName FabricObserverType -ApplicationTypeVersion 3.2.16 +New-ServiceFabricApplication -ApplicationName fabric:/FabricObserver -ApplicationTypeName FabricObserverType -ApplicationTypeVersion 3.3.0 #Create the Service instances (-1 means all nodes, which is what is required for FO): @@ -147,14 +147,14 @@ New-ServiceFabricService -Stateless -PartitionSchemeSingleton -ApplicationName f #OR if updating existing version: -Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationTypeVersion 3.2.16 -Monitored -FailureAction rollback +Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/FabricObserver -ApplicationTypeVersion 3.3.0 -Monitored -FailureAction rollback ``` ## Observer Model FO is composed of Observer objects (instance types) that are designed to observe, record, and report on several machine-level environmental conditions inside a Windows or Linux (Ubuntu) VM hosting a Service Fabric node. -**NOTE:** ```SFConfigurationObserver```, which has been deprecated for several releases has been completely removed in 3.2.16. Further, all related settings have been removed from Settings.xml and ApplicationManifest.xml. +**NOTE:** ```SFConfigurationObserver```, which has been deprecated for several releases has been completely removed in 3.3.0. Further, all related settings have been removed from Settings.xml and ApplicationManifest.xml. Here are the current observers and what they monitor: diff --git a/SampleObserverPlugin/SampleNewObserver.cs b/SampleObserverPlugin/SampleNewObserver.cs index 394d707c..01fe842a 100644 --- a/SampleObserverPlugin/SampleNewObserver.cs +++ b/SampleObserverPlugin/SampleNewObserver.cs @@ -24,34 +24,23 @@ namespace FabricObserver.Observers /// /// An observer plugin muse derive from ObserverBase and implement 2 abstract functions, ObserveAsync and ReportAsync. /// - public class SampleNewObserver : ObserverBase + /// + /// + /// + /// FabricClient instance. This is managed by FabricObserver. + /// StatelessServiceContext instance. This is managed by FabricObserver. + public class SampleNewObserver(FabricClient fabricClient, StatelessServiceContext context) : ObserverBase(fabricClient, context) { - private readonly StringBuilder message; - private readonly AsyncRetryPolicy retryPolicy; - - /// - /// - /// - /// FabricClient instance. This is managed by FabricObserver. - /// StatelessServiceContext instance. This is managed by FabricObserver. - public SampleNewObserver(FabricClient fabricClient, StatelessServiceContext context) : base(fabricClient, context) - { - message = new StringBuilder(); - - // Polly retry policy for when FabricException or TimeoutException is thrown by its Execute predicate. - // The purpose of using Polly here is to show how you deal with external (to FO) dependencies (put them all in the same folder where your - // plugin dll lives, including the dependencies, if any, of the primary dependencies you employ). - retryPolicy = Policy.Handle() + private readonly StringBuilder message = new(); + private readonly AsyncRetryPolicy retryPolicy = Policy.Handle() .Or() .WaitAndRetryAsync( - new[] - { + [ TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), - }); - } + ]); public override async Task ObserveAsync(CancellationToken token) { @@ -118,7 +107,7 @@ await retryPolicy.ExecuteAsync( ConfigurationSettings.AsyncTimeout, Token)); - apps.AddRange(appList.ToList()); + apps.AddRange([.. appList]); // Wait a second before grabbing the next batch of apps.. await Task.Delay(TimeSpan.FromSeconds(1), Token); diff --git a/SampleObserverPlugin/SampleObserverPlugin.csproj b/SampleObserverPlugin/SampleObserverPlugin.csproj index 8dab4e59..29dc3507 100644 --- a/SampleObserverPlugin/SampleObserverPlugin.csproj +++ b/SampleObserverPlugin/SampleObserverPlugin.csproj @@ -1,8 +1,7 @@  - - net6.0 + + net8.0 linux-x64;win-x64 FabricObserver.Observers SampleNewObserver @@ -19,19 +18,19 @@ - - - + - + \ No newline at end of file diff --git a/TelemetryLib/TelemetryEvents.cs b/TelemetryLib/TelemetryEvents.cs index 9101f753..67106c2c 100644 --- a/TelemetryLib/TelemetryEvents.cs +++ b/TelemetryLib/TelemetryEvents.cs @@ -58,7 +58,7 @@ public bool EmitFabricObserverOperationalEvent(FabricObserverOperationalEventDat { _ = TryGetHashStringSha256(nodeName, out string nodeHashString); - IDictionary eventProperties = new Dictionary + Dictionary eventProperties = new() { { "EventName", OperationalEventName}, { "TaskName", FOTaskName}, @@ -82,7 +82,7 @@ public bool EmitFabricObserverOperationalEvent(FabricObserverOperationalEventDat } } - IDictionary metrics = new Dictionary + Dictionary metrics = new() { { "EnabledObserverCount", foData.EnabledObserverCount } }; @@ -126,9 +126,9 @@ public bool EmitFabricObserverOperationalEvent(FabricObserverOperationalEventDat // Since we log the telemetry data to disk, check to make sure we don't send the same data again across FO restarts if the data has not changed. if (File.Exists(logFilePath) && TryDeserializeFOEventData(File.ReadAllText(logFilePath), out FabricObserverOperationalEventData foEventDataFromLogFile)) { - if (foEventDataFromLogFile.ObserverData != null && foEventDataFromLogFile.ObserverData.ContainsKey(obData.Key)) + if (foEventDataFromLogFile.ObserverData != null && foEventDataFromLogFile.ObserverData.TryGetValue(obData.Key, out ObserverData value)) { - if (foEventDataFromLogFile?.ObserverData != null && foEventDataFromLogFile?.ObserverData[obData.Key]?.ServiceData?.MonitoredAppCount == data) + if (value?.ServiceData?.MonitoredAppCount == data) { addMetric = false; } @@ -153,9 +153,9 @@ public bool EmitFabricObserverOperationalEvent(FabricObserverOperationalEventDat // Since we log the telemetry data to disk, check to make sure we don't send the same data again across FO restarts if the data has not changed. if (File.Exists(logFilePath) && TryDeserializeFOEventData(File.ReadAllText(logFilePath), out FabricObserverOperationalEventData foEventDataFromLogFile)) { - if (foEventDataFromLogFile.ObserverData != null && foEventDataFromLogFile.ObserverData.ContainsKey(obData.Key)) + if (foEventDataFromLogFile.ObserverData != null && foEventDataFromLogFile.ObserverData.TryGetValue(obData.Key, out ObserverData value)) { - if (foEventDataFromLogFile.ObserverData[obData.Key].ServiceData.MonitoredServiceProcessCount == data) + if (value.ServiceData.MonitoredServiceProcessCount == data) { addMetric = false; } @@ -239,7 +239,7 @@ public bool EmitCriticalErrorEvent(CriticalErrorEventData errorData, string sour try { - IDictionary eventProperties = new Dictionary + Dictionary eventProperties = new() { { "EventName", CriticalErrorEventName}, { "TaskName", source}, @@ -338,7 +338,7 @@ public bool EmitClusterObserverOperationalEvent(ClusterObserverOperationalEventD try { - IDictionary eventProperties = new Dictionary + Dictionary eventProperties = new() { { "EventName", OperationalEventName}, { "TaskName", COTaskName}, @@ -397,16 +397,12 @@ public static bool TryGetHashStringSha256(string source, out string result) try { StringBuilder Sb = new(); + Encoding enc = Encoding.UTF8; + byte[] byteVal = SHA256.HashData(enc.GetBytes(source)); - using (var hash = SHA256.Create()) + foreach (byte b in byteVal) { - Encoding enc = Encoding.UTF8; - byte[] byteVal = hash.ComputeHash(enc.GetBytes(source)); - - foreach (byte b in byteVal) - { - Sb.Append(b.ToString("x2")); - } + Sb.Append(b.ToString("x2")); } result = Sb.ToString(); diff --git a/TelemetryLib/TelemetryLib.csproj b/TelemetryLib/TelemetryLib.csproj index 0feaf589..556f4b59 100644 --- a/TelemetryLib/TelemetryLib.csproj +++ b/TelemetryLib/TelemetryLib.csproj @@ -1,12 +1,12 @@  - net6.0 + net8.0 Library FabricObserver.TelemetryLib TelemetryLib x64 - 2.1.0.0 - 2.1.0.0 + 2.2.0.0 + 2.2.0.0 Copyright © 2024 TelemetryLib diff --git a/XmlDiffPatchSF/Program.cs b/XmlDiffPatchSF/Program.cs index 2181d0d2..10bdbb4b 100644 --- a/XmlDiffPatchSF/Program.cs +++ b/XmlDiffPatchSF/Program.cs @@ -28,7 +28,7 @@ private static void Main(string[] args) "preceding the file extension.\n\n" + "**Note, if you have observer plugins, then you must supply true for [mergeExistingNodes] as the last argument to pull over your plugin settings as part of the merge.**.\n\n" + "Example:\n\n" + - "DiffPatchXml \"C:\\repos\\FO\\3.1.26\\configs\\ApplicationManifest.xml\" \"C:\\repos\\FO\\3.2.16\\configs\\ApplicationManifest.xml\"\n"); + "DiffPatchXml \"C:\\repos\\FO\\3.1.26\\configs\\ApplicationManifest.xml\" \"C:\\repos\\FO\\3.3.0\\configs\\ApplicationManifest.xml\"\n"); return; } diff --git a/conuget.md b/conuget.md index 911f7b6d..20a4a63a 100644 --- a/conuget.md +++ b/conuget.md @@ -1,7 +1,7 @@ -### ClusterObserver 2.2.8 -#### This version requires SF Runtime >= 9.0 and targets .NET 6. +### ClusterObserver 2.3.0 (.NET 8) +#### This version requires SF Runtime >= 9.1 and targets .NET 8. -[ClusterObserver (CO)](https://github.com/microsoft/service-fabric-observer/tree/main/ClusterObserver) is a stateless singleton Service Fabric .NET 6 service that runs on one node in a cluster. CO observes cluster health (aggregated) +[ClusterObserver (CO)](https://github.com/microsoft/service-fabric-observer/tree/main/ClusterObserver) is a stateless singleton Service Fabric .NET 8 application that runs on one node in a cluster. CO observes cluster health (aggregated) and sends telemetry when a cluster is in Error or Warning. CO shares a very small subset of FabricObserver's (FO) code. It is designed to be completely independent from FO sources, but lives in this repo (and SLN) because it is very useful to have both services deployed, especially for those who want cluster-level health observation and reporting in addition to the node-level user-defined resource monitoring, health event creation, and health reporting done by FO. FabricObserver is designed to generate Service Fabric health events based on user-defined resource usage Warning and Error thresholds which ClusterObserver sends to your log analytics and alerting service. @@ -32,7 +32,7 @@ $appParams = @{ "RunInterval" = "00:10:00"; "MaxTimeNodeStatusNotOk" = "04:00:00 Then execute the application upgrade with ```Powershell -Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/ClusterObserver -ApplicationTypeVersion 2.2.1.960 -ApplicationParameter $appParams -Monitored -FailureAction rollback +Start-ServiceFabricApplicationUpgrade -ApplicationName fabric:/ClusterObserver -ApplicationTypeVersion 2.3.0 -ApplicationParameter $appParams -Monitored -FailureAction rollback ``` Example Configuration: diff --git a/foextlib.md b/foextlib.md index 074b573b..ed5d3ba8 100644 --- a/foextlib.md +++ b/foextlib.md @@ -1,10 +1,10 @@ -## FabricObserver Extensibility Library 3.2.16 +## FabricObserver Extensibility Library 3.3.0 (.NET 8) -FabricObserver.Extensibility is a .NET 6 library for building custom observers that extend FabricObserver's capabilities to match your needs. A custom observer is managed just like a built-in observer. +FabricObserver.Extensibility is a .NET 8 library for building custom observers that extend FabricObserver's and ClusterObserver's capabilities to match your needs. A custom observer is managed just like a built-in observer. ### How to implement an observer using FO's extensibility model -1. Create a new .NET core library project. You should target net6.0 in your csproj because that is the target net SDK version that FabricObserver is built for. +1. Create a new .NET core library project. You should target net8.0 in your csproj because that is the target net SDK version that FabricObserver 3.3.0 is built for. 2. Install the latest Microsoft.ServiceFabricApps.FabricObserver.Extensibility nupkg from https://www.nuget.org/profiles/ServiceFabricApps into your plugin project. diff --git a/fonuget.md b/fonuget.md index 08f26de9..9ef5a187 100644 --- a/fonuget.md +++ b/fonuget.md @@ -1,6 +1,6 @@ -## FabricObserver 3.2.16 +## FabricObserver 3.3.0 (.NET 8) -[**FabricObserver (FO)**](https://github.com/microsoft/service-fabric-observer) is a production-ready watchdog service with an easy-to-use extensibility model, written as a stateless, singleton Service Fabric **.NET 6** application that by default +[**FabricObserver (FO)**](https://github.com/microsoft/service-fabric-observer) is a production-ready watchdog service with an easy-to-use extensibility model, written as a stateless, singleton Service Fabric **.NET 8** application that by default 1. Monitors a broad range of physical machine resources that tend to be very important to all Service Fabric services and maps these metrics to the related Service Fabric entities. 2. Runs on multiple versions of Windows Server and Ubuntu.