Skip to content

Commit

Permalink
add anonymized metrics for SMAPI and game versions
Browse files Browse the repository at this point in the history
  • Loading branch information
Pathoschild committed Aug 20, 2024
1 parent 2f0db74 commit 26cfac0
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 6 deletions.
4 changes: 4 additions & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
* `CustomLocations` entries which use the new [unique string ID](https://stardewvalleywiki.com/Modding:Common_data_field_types#Unique_string_ID) format;
* `AddWarps` warps when a location name contains a dot.

* For the web API:
* The [anonymized metrics for update check requests](technical/web.md#modsmetrics) now counts requests by SMAPI and game version.

## 4.0.8
Released 21 April 2024 for Stardew Valley 1.6.4 or later.

Expand Down Expand Up @@ -165,6 +168,7 @@ Released 19 March 2024 for Stardew Valley 1.6.0 or later. See [release highlight

* For the web UI:
* Updated JSON validator for Content Patcher 2.0.0.
* Added [anonymized metrics for update check requests](technical/web.md#modsmetrics).
* Fixed uploaded log/JSON file expiry alway shown as renewed.
* Fixed update check for mods with a prerelease version tag not recognized by the ModDrop API. SMAPI now parses the version itself if needed.

Expand Down
8 changes: 4 additions & 4 deletions docs/technical/web.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ may be useful to external tools.

Example request:
```js
POST https://smapi.io/api/v3.0/mods
POST https://smapi.io/api/v4.0.0/mods
{
"mods": [
{
Expand All @@ -199,8 +199,8 @@ POST https://smapi.io/api/v3.0/mods
"isBroken": false
}
],
"apiVersion": "3.0.0",
"gameVersion": "1.4.0",
"apiVersion": "4.0.0",
"gameVersion": "1.6.9",
"platform": "Windows",
"includeExtendedMetadata": true
}
Expand Down Expand Up @@ -329,7 +329,7 @@ deployed or restarted.

Example request:
```js
GET https://smapi.io/api/v3.0/mods/metrics
GET https://smapi.io/api/v4.0.0/mods/metrics
```

## Short URLs
Expand Down
2 changes: 1 addition & 1 deletion src/SMAPI.Web/Controllers/ModsApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public ModsApiController(IWebHostEnvironment environment, IWikiCacheRepository w
public async Task<IEnumerable<ModEntryModel>> PostAsync([FromBody] ModSearchModel? model, [FromRoute] string version)
{
ApiMetricsModel metrics = MetricsManager.GetMetricsForNow();
metrics.TrackRequest();
metrics.TrackRequest(model?.ApiVersion, model?.GameVersion);

if (model?.Mods == null)
return Array.Empty<ModEntryModel>();
Expand Down
17 changes: 16 additions & 1 deletion src/SMAPI.Web/Framework/Metrics/ApiMetricsModel.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using StardewModdingAPI.Toolkit.Framework.UpdateData;

Expand All @@ -15,14 +16,28 @@ internal class ApiMetricsModel
/// <summary>The metrics by mod site.</summary>
public Dictionary<ModSiteKey, MetricsModel> Sites { get; } = new();

/// <summary>The number of update-check requests by SMAPI version.</summary>
public Dictionary<string, long> ByApiVersion { get; } = new(StringComparer.OrdinalIgnoreCase);

/// <summary>The number of update-check requests by game version.</summary>
public Dictionary<string, long> ByGameVersion { get; } = new(StringComparer.OrdinalIgnoreCase);


/*********
** Public methods
*********/
/// <summary>Track an update-check request received by the API.</summary>
public void TrackRequest()
/// <param name="apiVersion">The SMAPI version installed by the player.</param>
/// <param name="gameVersion">The game version installed by the player.</param>
public void TrackRequest(ISemanticVersion? apiVersion, ISemanticVersion? gameVersion)
{
this.ApiRequests++;

string apiVersionStr = apiVersion?.ToString() ?? "<none specified>";
string gameVersionStr = gameVersion?.ToString() ?? "<none specified>";

this.ByApiVersion[apiVersionStr] = this.ByApiVersion.GetValueOrDefault(apiVersionStr) + 1;
this.ByGameVersion[gameVersionStr] = this.ByGameVersion.GetValueOrDefault(gameVersionStr) + 1;
}

/// <summary>Track the update-check result for a specific update key.</summary>
Expand Down
10 changes: 10 additions & 0 deletions src/SMAPI.Web/Framework/Metrics/MetricsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public static MetricsSummary GetSummary(ModUpdateCheckConfig config)
var totals = new MetricsModel();
var bySite = new Dictionary<ModSiteKey, MetricsModel>();
var byDate = new Dictionary<string, ApiMetricsModel>();
var byApiVersion = new Dictionary<string, long>(StringComparer.OrdinalIgnoreCase);
var byGameVersion = new Dictionary<string, long>(StringComparer.OrdinalIgnoreCase);
foreach ((string hourlyKey, ApiMetricsModel hourly) in MetricsManager.Metrics)
{
// totals
Expand All @@ -68,6 +70,12 @@ public static MetricsSummary GetSummary(ModUpdateCheckConfig config)
if (!byDate.TryGetValue(dailyKey, out ApiMetricsModel? daily))
byDate[dailyKey] = daily = new ApiMetricsModel();

// by version
foreach ((string apiVersion, long count) in hourly.ByApiVersion)
byApiVersion[apiVersion] = byApiVersion.GetValueOrDefault(apiVersion) + count;
foreach ((string gameVersion, long count) in hourly.ByGameVersion)
byGameVersion[gameVersion] = byGameVersion.GetValueOrDefault(gameVersion) + count;

daily.AggregateFrom(hourly);
}

Expand All @@ -80,6 +88,8 @@ public static MetricsSummary GetSummary(ModUpdateCheckConfig config)
TotalCacheHits: totals.CacheHits,
TotalSuccessCacheMisses: totals.SuccessCacheMisses,
TotalErrorCacheMisses: totals.ErrorCacheMisses,
ByApiVersion: byApiVersion,
ByGameVersion: byGameVersion,
BySite: bySite,
ByDate: byDate
);
Expand Down
4 changes: 4 additions & 0 deletions src/SMAPI.Web/Framework/Metrics/MetricsSummary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ namespace StardewModdingAPI.Web.Framework.Metrics
/// <param name="TotalCacheHits">The number of times an update key returned data from the cache.</param>
/// <param name="TotalSuccessCacheMisses">The number of times an update key successfully fetched data from a remote mod site.</param>
/// <param name="TotalErrorCacheMisses">The number of times an update key could not fetch data from a remote mod site (e.g. mod page didn't exist or mod site returned an API error).</param>
/// <param name="ByApiVersion">The number of update-check request by SMAPI version.</param>
/// <param name="ByGameVersion">The number of update-check request by game version.</param>
/// <param name="BySite">The metrics grouped by site.</param>
/// <param name="ByDate">The metrics grouped by UTC date.</param>
internal record MetricsSummary(
Expand All @@ -24,6 +26,8 @@ internal record MetricsSummary(
int TotalCacheHits,
int TotalSuccessCacheMisses,
int TotalErrorCacheMisses,
IDictionary<string, long> ByApiVersion,
IDictionary<string, long> ByGameVersion,
IDictionary<ModSiteKey, MetricsModel> BySite,
IDictionary<string, ApiMetricsModel> ByDate
);
Expand Down

0 comments on commit 26cfac0

Please sign in to comment.