Fix monitoring integration with actual prometheus (#3183)

* Downgrade OpenTelemetry.Exporter.Prometheus.AspNetCore due to issues with latest version

* Add unit to asf_bot_farming_minutes_remaining

* Upgrade some packages released last night (already tested to work)

* Don't forget about unit suffix

* Add build and runtime information metrics

It is not recommended to include this information as labels in all
metrics. Instead, we add two special metrics with a constant value of
"1" and restrict those static pieces of information to them

* Remove module version from metrics as it does not work

* Apply feedback

* Deduplicate code

* Reference related issue in upstream repo
This commit is contained in:
Sebastian Göls 2024-04-07 23:56:44 +02:00 committed by GitHub
parent 8e055fe587
commit 9016a5109d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 94 additions and 24 deletions

View file

@ -12,5 +12,13 @@
],
"git-submodules": {
"enabled": true
}
},
"packageRules": [
{
// TODO: <= 1.7.0-rc.1 for invalid response on monitoring endpoint, last failed version 1.8.0-rc.1 - https://github.com/open-telemetry/opentelemetry-dotnet/issues/5506
"allowedVersions": "<= 1.7.0-rc.1",
"matchManagers": [ "nuget" ],
"matchPackageNames": [ "OpenTelemetry.Exporter.Prometheus.AspNetCore" ]
}
]
}

View file

@ -50,6 +50,21 @@ internal sealed class MonitoringPlugin : OfficialPlugin, IWebServiceProvider, IG
private const string MetricNamePrefix = "asf";
private const string UnknownLabelValueFallback = "unknown";
private static readonly Measurement<int> BuildInfo = new(
1,
new KeyValuePair<string, object?>(TagNames.Version, SharedInfo.Version.ToString()),
new KeyValuePair<string, object?>(TagNames.Variant, SharedInfo.BuildInfo.Variant)
);
private static readonly Measurement<int> RuntimeInfo = new(
1,
new KeyValuePair<string, object?>(TagNames.Framework, OS.Framework ?? UnknownLabelValueFallback),
new KeyValuePair<string, object?>(TagNames.Runtime, OS.Runtime ?? UnknownLabelValueFallback),
new KeyValuePair<string, object?>(TagNames.OS, OS.Description ?? UnknownLabelValueFallback)
);
private static bool Enabled => ASF.GlobalConfig?.IPC ?? GlobalConfig.DefaultIPC;
[JsonInclude]
@ -106,6 +121,18 @@ internal sealed class MonitoringPlugin : OfficialPlugin, IWebServiceProvider, IG
Meter = new Meter(MeterName, Version.ToString());
Meter.CreateObservableGauge(
$"{MetricNamePrefix}_build_info",
static () => BuildInfo,
description: "Build information about ASF in form of label values"
);
Meter.CreateObservableGauge(
$"{MetricNamePrefix}_runtime_info",
static () => RuntimeInfo,
description: "Runtime information about ASF in form of label values"
);
Meter.CreateObservableGauge(
$"{MetricNamePrefix}_ipc_banned_ips",
static () => ApiAuthenticationMiddleware.GetCurrentlyBannedIPs().Count(),
@ -150,13 +177,15 @@ internal sealed class MonitoringPlugin : OfficialPlugin, IWebServiceProvider, IG
description: "Number of Steam groups each bot is in"
);
// Keep in mind that we use a unit here and the unit needs to be a suffix to the name
Meter.CreateObservableGauge(
$"{MetricNamePrefix}_bot_farming_minutes_remaining", static () => {
$"{MetricNamePrefix}_bot_farming_time_remaining_{Units.Minutes}", 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"
Units.Minutes,
"Approximate number of minutes remaining until each bot has finished farming all cards"
);
Meter.CreateObservableGauge(

View file

@ -27,5 +27,10 @@ internal static class TagNames {
internal const string BotName = "bot";
internal const string BotState = "state";
internal const string CurrencyCode = "currency";
internal const string Framework = "framework";
internal const string OS = "operating_system";
internal const string Runtime = "runtime";
internal const string SteamID = "steamid";
internal const string Variant = "variant";
internal const string Version = "version";
}

View file

@ -0,0 +1,28 @@
// ----------------------------------------------------------------------------------------------
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// ----------------------------------------------------------------------------------------------
// |
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// 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 Units {
internal const string Minutes = "minutes";
}

View file

@ -37,10 +37,20 @@ using System.Threading.Tasks;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Storage;
using ArchiSteamFarm.Web;
using JetBrains.Annotations;
namespace ArchiSteamFarm.Core;
internal static class OS {
[PublicAPI]
public static string? Description => TrimAndNullifyEmptyString(RuntimeInformation.OSDescription);
[PublicAPI]
public static string? Framework => TrimAndNullifyEmptyString(RuntimeInformation.FrameworkDescription);
[PublicAPI]
public static string? Runtime => TrimAndNullifyEmptyString(RuntimeInformation.RuntimeIdentifier);
// We need to keep this one assigned and not calculated on-demand
internal static readonly string ProcessFileName = Environment.ProcessPath ?? throw new InvalidOperationException(nameof(ProcessFileName));
@ -58,25 +68,7 @@ internal static class OS {
return BackingVersion;
}
string framework = RuntimeInformation.FrameworkDescription.Trim();
if (framework.Length == 0) {
framework = "Unknown Framework";
}
string runtime = RuntimeInformation.RuntimeIdentifier.Trim();
if (runtime.Length == 0) {
runtime = "Unknown Runtime";
}
string description = RuntimeInformation.OSDescription.Trim();
if (description.Length == 0) {
description = "Unknown OS";
}
BackingVersion = $"{framework}; {runtime}; {description}";
BackingVersion = $"{Framework ?? "Unknown Framework"}; {Runtime ?? "Unknown Runtime"}; {Description ?? "Unknown OS"}";
return BackingVersion;
}
@ -296,6 +288,14 @@ internal static class OS {
}
}
private static string? TrimAndNullifyEmptyString(string s) {
ArgumentNullException.ThrowIfNull(s);
s = s.Trim();
return s.Length == 0 ? null : s;
}
[SupportedOSPlatform("Windows")]
private static void WindowsDisableQuickEditMode() {
if (!OperatingSystem.IsWindows()) {

View file

@ -102,7 +102,7 @@ public static class SharedInfo {
internal static string PublicIdentifier => $"{AssemblyName}{(BuildInfo.IsCustomBuild ? "-custom" : PluginsCore.HasCustomPluginsLoaded ? "-modded" : "")}";
internal static Version Version => Assembly.GetExecutingAssembly().GetName().Version ?? throw new InvalidOperationException(nameof(Version));
private static Guid ModuleVersion => Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId;
internal static Guid ModuleVersion => Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId;
private static string? CachedHomeDirectory;

View file

@ -10,7 +10,7 @@
<PackageVersion Include="MSTest" Version="3.3.1" />
<PackageVersion Include="Nito.AsyncEx.Coordination" Version="5.1.2" />
<PackageVersion Include="NLog.Web.AspNetCore" Version="5.3.8" />
<PackageVersion Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.8.0-rc.1" />
<PackageVersion Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.7.0-rc.1" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.8.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.8.0" />