-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add official monitoring plugin (#3160)
* Add Monitoring plugin * Prepare pipeline * Fix Rider stupidity * Fix Windows build * Remove translation files * Apply feedback * Add steam id as additional tag to metrics * Apply feedback * Add runtime metrics * Fix my brain not braining * Use extension methods to add instrumentation and Add monitoring for outbound HTTP traffic * Upgrade OpenTelemetry.Extensions.Hosting to prerelease due to runtime exception * Remove config and add file that was supposed to be committed yesterday to fix the runtime exception * Revert changes to publish.yml * Remove localization * Apply feedback * Apply feedback * Fix version number * Revert use of property in Kestrel (even tho it's an outside caller to the source class)
- Loading branch information
Showing
10 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
21 changes: 21 additions & 0 deletions
21
ArchiSteamFarm.OfficialPlugins.Monitoring/ArchiSteamFarm.OfficialPlugins.Monitoring.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<OutputType>Library</OutputType> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="ConfigureAwaitChecker.Analyzer" PrivateAssets="all" /> | ||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="all" /> | ||
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" /> | ||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" /> | ||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" /> | ||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" /> | ||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" /> | ||
<PackageReference Include="SteamKit2" IncludeAssets="compile" /> | ||
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" ExcludeAssets="all" Private="false" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// ---------------------------------------------------------------------------------------------- | ||
// _ _ _ ____ _ _____ | ||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ | ||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ | ||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | | ||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| | ||
// ---------------------------------------------------------------------------------------------- | ||
// | | ||
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki | ||
// Contact: [email protected] | ||
// | | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using System; | ||
|
||
[assembly: CLSCompliant(true)] |
190 changes: 190 additions & 0 deletions
190
ArchiSteamFarm.OfficialPlugins.Monitoring/MonitoringPlugin.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
// ---------------------------------------------------------------------------------------------- | ||
// _ _ _ ____ _ _____ | ||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ | ||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ | ||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | | ||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| | ||
// ---------------------------------------------------------------------------------------------- | ||
// | | ||
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki | ||
// Contact: [email protected] | ||
// | | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel.DataAnnotations; | ||
using System.Composition; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Diagnostics.Metrics; | ||
using System.Linq; | ||
using System.Text.Json.Serialization; | ||
using System.Threading.Tasks; | ||
using ArchiSteamFarm.Core; | ||
using ArchiSteamFarm.IPC.Integration; | ||
using ArchiSteamFarm.Plugins; | ||
using ArchiSteamFarm.Plugins.Interfaces; | ||
using ArchiSteamFarm.Steam; | ||
using ArchiSteamFarm.Storage; | ||
using Microsoft.AspNetCore.Builder; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using OpenTelemetry.Metrics; | ||
using SteamKit2; | ||
|
||
namespace ArchiSteamFarm.OfficialPlugins.Monitoring; | ||
|
||
[Export(typeof(IPlugin))] | ||
[SuppressMessage("ReSharper", "MemberCanBeFileLocal")] | ||
internal sealed class MonitoringPlugin : OfficialPlugin, IWebServiceProvider, IGitHubPluginUpdates, IDisposable { | ||
private const string MeterName = SharedInfo.AssemblyName; | ||
|
||
private const string MetricNamePrefix = "asf"; | ||
|
||
private static bool Enabled => ASF.GlobalConfig?.IPC ?? GlobalConfig.DefaultIPC; | ||
|
||
[JsonInclude] | ||
[Required] | ||
public override string Name => nameof(MonitoringPlugin); | ||
|
||
/// <inheritdoc /> | ||
public string RepositoryName => SharedInfo.GithubRepo; | ||
|
||
[JsonInclude] | ||
[Required] | ||
public override Version Version => typeof(MonitoringPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version)); | ||
|
||
private Meter? Meter; | ||
|
||
public void Dispose() => Meter?.Dispose(); | ||
|
||
public void OnConfiguringEndpoints(IApplicationBuilder app) { | ||
ArgumentNullException.ThrowIfNull(app); | ||
|
||
if (!Enabled) { | ||
return; | ||
} | ||
|
||
app.UseEndpoints(static builder => builder.MapPrometheusScrapingEndpoint()); | ||
} | ||
|
||
public void OnConfiguringServices(IServiceCollection services) { | ||
ArgumentNullException.ThrowIfNull(services); | ||
|
||
if (!Enabled) { | ||
return; | ||
} | ||
|
||
InitializeMeter(); | ||
|
||
services.AddOpenTelemetry().WithMetrics( | ||
builder => { | ||
builder.AddPrometheusExporter(static config => config.ScrapeEndpointPath = "/Api/metrics"); | ||
builder.AddRuntimeInstrumentation(); | ||
builder.AddAspNetCoreInstrumentation(); | ||
builder.AddHttpClientInstrumentation(); | ||
builder.AddMeter(Meter.Name); | ||
} | ||
); | ||
} | ||
|
||
public override Task OnLoaded() => Task.CompletedTask; | ||
|
||
[MemberNotNull(nameof(Meter))] | ||
private void InitializeMeter() { | ||
if (Meter != null) { | ||
return; | ||
} | ||
|
||
Meter = new Meter(MeterName, Version.ToString()); | ||
|
||
Meter.CreateObservableGauge( | ||
$"{MetricNamePrefix}_ipc_banned_ips", | ||
static () => ApiAuthenticationMiddleware.GetCurrentlyBannedIPs().Count(), | ||
description: "Number of IP addresses currently banned by ASFs IPC module" | ||
); | ||
|
||
Meter.CreateObservableGauge( | ||
$"{MetricNamePrefix}_active_plugins", | ||
static () => PluginsCore.ActivePluginsCount, | ||
description: "Number of plugins currently loaded in ASF" | ||
); | ||
|
||
Meter.CreateObservableGauge( | ||
$"{MetricNamePrefix}_bots", static () => { | ||
ICollection<Bot> bots = Bot.Bots?.Values ?? Array.Empty<Bot>(); | ||
|
||
return new List<Measurement<int>>(4) { | ||
new(bots.Count, new KeyValuePair<string, object?>(TagNames.BotState, "configured")), | ||
new(bots.Count(static bot => bot.IsConnectedAndLoggedOn), new KeyValuePair<string, object?>(TagNames.BotState, "online")), | ||
new(bots.Count(static bot => !bot.IsConnectedAndLoggedOn), new KeyValuePair<string, object?>(TagNames.BotState, "offline")), | ||
new(bots.Count(static bot => bot.CardsFarmer.NowFarming), new KeyValuePair<string, object?>(TagNames.BotState, "farming")) | ||
}; | ||
}, | ||
description: "Number of bots that are currently loaded in ASF" | ||
); | ||
|
||
Meter.CreateObservableGauge( | ||
$"{MetricNamePrefix}_bot_friends", static () => { | ||
ICollection<Bot> bots = Bot.Bots?.Values ?? Array.Empty<Bot>(); | ||
|
||
return bots.Where(static bot => bot.IsConnectedAndLoggedOn).Select(static bot => new Measurement<int>(bot.SteamFriends.GetFriendCount(), new KeyValuePair<string, object?>(TagNames.BotName, bot.BotName), new KeyValuePair<string, object?>(TagNames.SteamID, bot.SteamID))); | ||
}, | ||
description: "Number of friends each bot has on Steam" | ||
); | ||
|
||
Meter.CreateObservableGauge( | ||
$"{MetricNamePrefix}_bot_clans", static () => { | ||
ICollection<Bot> bots = Bot.Bots?.Values ?? Array.Empty<Bot>(); | ||
|
||
return bots.Where(static bot => bot.IsConnectedAndLoggedOn).Select(static bot => new Measurement<int>(bot.SteamFriends.GetClanCount(), new KeyValuePair<string, object?>(TagNames.BotName, bot.BotName), new KeyValuePair<string, object?>(TagNames.SteamID, bot.SteamID))); | ||
}, | ||
description: "Number of Steam groups each bot is in" | ||
); | ||
|
||
Meter.CreateObservableGauge( | ||
$"{MetricNamePrefix}_bot_farming_minutes_remaining", static () => { | ||
ICollection<Bot> bots = Bot.Bots?.Values ?? Array.Empty<Bot>(); | ||
|
||
return bots.Select(static bot => new Measurement<double>(bot.CardsFarmer.TimeRemaining.TotalMinutes, new KeyValuePair<string, object?>(TagNames.BotName, bot.BotName), new KeyValuePair<string, object?>(TagNames.SteamID, bot.SteamID))); | ||
}, | ||
description: "Approximate number of minutes remaining until each bot has finished farming all cards" | ||
); | ||
|
||
Meter.CreateObservableGauge( | ||
$"{MetricNamePrefix}_bot_heartbeat_failures", static () => { | ||
ICollection<Bot> bots = Bot.Bots?.Values ?? Array.Empty<Bot>(); | ||
|
||
return bots.Select(static bot => new Measurement<byte>(bot.HeartBeatFailures, new KeyValuePair<string, object?>(TagNames.BotName, bot.BotName), new KeyValuePair<string, object?>(TagNames.SteamID, bot.SteamID))); | ||
}, | ||
description: "Number of times a bot has failed to reach Steam servers" | ||
); | ||
|
||
Meter.CreateObservableGauge( | ||
$"{MetricNamePrefix}_bot_wallet_balance", static () => { | ||
ICollection<Bot> bots = Bot.Bots?.Values ?? Array.Empty<Bot>(); | ||
|
||
return bots.Where(static bot => bot.WalletCurrency != ECurrencyCode.Invalid).Select(static bot => new Measurement<long>(bot.WalletBalance, new KeyValuePair<string, object?>(TagNames.BotName, bot.BotName), new KeyValuePair<string, object?>(TagNames.SteamID, bot.SteamID), new KeyValuePair<string, object?>(TagNames.CurrencyCode, bot.WalletCurrency.ToString()))); | ||
}, | ||
description: "Current Steam wallet balance of each bot" | ||
); | ||
|
||
Meter.CreateObservableGauge( | ||
$"{MetricNamePrefix}_bot_bgr_keys_remaining", static () => { | ||
ICollection<Bot> bots = Bot.Bots?.Values ?? Array.Empty<Bot>(); | ||
|
||
return bots.Select(static bot => new Measurement<long>(bot.GamesToRedeemInBackgroundCount, new KeyValuePair<string, object?>(TagNames.BotName, bot.BotName), new KeyValuePair<string, object?>(TagNames.SteamID, bot.SteamID))); | ||
}, | ||
description: "Remaining games to redeem in background per bot" | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// ---------------------------------------------------------------------------------------------- | ||
// _ _ _ ____ _ _____ | ||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ | ||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ | ||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | | ||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| | ||
// ---------------------------------------------------------------------------------------------- | ||
// | | ||
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki | ||
// Contact: [email protected] | ||
// | | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
namespace ArchiSteamFarm.OfficialPlugins.Monitoring; | ||
|
||
internal static class TagNames { | ||
internal const string BotName = "bot"; | ||
internal const string BotState = "state"; | ||
internal const string CurrencyCode = "currency"; | ||
internal const string SteamID = "steamid"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.