Skip to content

Commit

Permalink
Fix monitoring integration with actual prometheus (#3183)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Abrynos committed Apr 7, 2024
1 parent 8e055fe commit 9016a51
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 24 deletions.
10 changes: 9 additions & 1 deletion .github/renovate.json5
Expand Up @@ -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" ]
}
]
}
33 changes: 31 additions & 2 deletions ArchiSteamFarm.OfficialPlugins.Monitoring/MonitoringPlugin.cs
Expand Up @@ -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]
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(
Expand Down
5 changes: 5 additions & 0 deletions ArchiSteamFarm.OfficialPlugins.Monitoring/TagNames.cs
Expand Up @@ -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";
}
28 changes: 28 additions & 0 deletions ArchiSteamFarm.OfficialPlugins.Monitoring/Units.cs
@@ -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";
}
38 changes: 19 additions & 19 deletions ArchiSteamFarm/Core/OS.cs
Expand Up @@ -37,10 +37,20 @@
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));

Expand All @@ -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;
}
Expand Down Expand Up @@ -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()) {
Expand Down
2 changes: 1 addition & 1 deletion ArchiSteamFarm/SharedInfo.cs
Expand Up @@ -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;

Check notice on line 105 in ArchiSteamFarm/SharedInfo.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Member can be made private (non-private accessibility)

Property 'ModuleVersion' can be made private

private static string? CachedHomeDirectory;

Expand Down
2 changes: 1 addition & 1 deletion Directory.Packages.props
Expand Up @@ -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" />
Expand Down

0 comments on commit 9016a51

Please sign in to comment.