|
|
@@ -321,11 +321,18 @@ namespace molilian.core
|
|
|
}
|
|
|
|
|
|
|
|
|
- private static async Task SaveParseCacheAsync(string channel, int accountId, string accountName,
|
|
|
- bool success, string message, string reason, string deeplink)
|
|
|
- {
|
|
|
- string dp_flag = deeplink switch
|
|
|
- {
|
|
|
+ private static async Task SaveParseCacheAsync(string channel, int accountId, string accountName,
|
|
|
+ bool success, string message, string reason, string deeplink)
|
|
|
+ {
|
|
|
+ // Account reports read this dimension directly. Record it first so failures in
|
|
|
+ // secondary aggregate dimensions cannot leave the account row incomplete.
|
|
|
+ if (accountId != 0)
|
|
|
+ {
|
|
|
+ await SaveParseAccountCacheAsync($"{channel}_{accountId}", success, message, reason);
|
|
|
+ }
|
|
|
+
|
|
|
+ string dp_flag = deeplink switch
|
|
|
+ {
|
|
|
"" => "none",
|
|
|
"tbopen://m.taobao.com/tbopen/index.html" or
|
|
|
"pinduoduo://com.xunmeng.pinduoduo/" or
|
|
|
@@ -354,11 +361,47 @@ namespace molilian.core
|
|
|
{
|
|
|
await SaveParseAccountCacheAsync($"{accountName}", success, message, reason);
|
|
|
}
|
|
|
- if (accountId != 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task RecordParseMetricsAsync(
|
|
|
+ string channel,
|
|
|
+ int accountId,
|
|
|
+ string accountName,
|
|
|
+ bool success,
|
|
|
+ string message,
|
|
|
+ string reason,
|
|
|
+ string deeplink,
|
|
|
+ string riskStrategy,
|
|
|
+ int launchScene)
|
|
|
+ {
|
|
|
+ // The new parse_metric counters must not depend on the legacy Redis statistics
|
|
|
+ // completing successfully. The MySQL log queue is also independent of both.
|
|
|
+ await TracksCore.RecordParseResultAsync(
|
|
|
+ channel,
|
|
|
+ riskStrategy,
|
|
|
+ launchScene,
|
|
|
+ accountId,
|
|
|
+ success);
|
|
|
+
|
|
|
+ try
|
|
|
{
|
|
|
- await SaveParseAccountCacheAsync($"{channel}_{accountId}", success, message, reason);
|
|
|
+ await SaveParseCacheAsync(
|
|
|
+ channel,
|
|
|
+ accountId,
|
|
|
+ accountName,
|
|
|
+ success,
|
|
|
+ message,
|
|
|
+ reason,
|
|
|
+ deeplink);
|
|
|
}
|
|
|
- }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _ = new LoggerLibrary("TkLogCore", "RecordParseMetrics")
|
|
|
+ .Info($"channel={channel}, accountId={accountId}, riskStrategy={riskStrategy}, launchScene={launchScene}")
|
|
|
+ .Info(ex.Message, ex.StackTrace)
|
|
|
+ .SaveAsync();
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
private static async Task SaveParseAccountCacheAsync(string accountName, bool success,
|
|
|
string message, string reason)
|
|
|
@@ -441,26 +484,44 @@ namespace molilian.core
|
|
|
string message, string reason)
|
|
|
{
|
|
|
var now = DateTime.Now;
|
|
|
- string month = now.ToString("yyyyMM");
|
|
|
- string day = now.ToString("yyyyMMdd");
|
|
|
- string hour = now.ToString("yyyyMMddHH");
|
|
|
-
|
|
|
- await SaveStatsCountAsync(prefix, accountName, month);
|
|
|
- await SaveStatsCountAsync(prefix, accountName, day);
|
|
|
- await SaveStatsCountAsync(prefix, accountName, hour);
|
|
|
-
|
|
|
string result = success ? "success" : "fail";
|
|
|
- await SaveStatsCountAsync(prefix, accountName, month, result);
|
|
|
- await SaveStatsCountAsync(prefix, accountName, day, result);
|
|
|
- await SaveStatsCountAsync(prefix, accountName, hour, result);
|
|
|
+ string[] timeKeys =
|
|
|
+ [
|
|
|
+ now.ToString("yyyyMM"),
|
|
|
+ now.ToString("yyyyMMdd"),
|
|
|
+ now.ToString("yyyyMMddHH")
|
|
|
+ ];
|
|
|
+ Exception? firstError = null;
|
|
|
+
|
|
|
+ async Task TrySaveAsync(Func<Task> action)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ await action();
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ firstError ??= ex;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- await SaveStatsDimensionAsync(prefix, accountName, "message", message, month);
|
|
|
- await SaveStatsDimensionAsync(prefix, accountName, "message", message, day);
|
|
|
- await SaveStatsDimensionAsync(prefix, accountName, "message", message, hour);
|
|
|
+ foreach (string timeKey in timeKeys)
|
|
|
+ {
|
|
|
+ // Keep total and result writes adjacent and isolate every dimension. A single
|
|
|
+ // transient Redis error must not suppress all success/fail counters that follow.
|
|
|
+ await TrySaveAsync(() => SaveStatsCountAsync(prefix, accountName, timeKey));
|
|
|
+ await TrySaveAsync(() => SaveStatsCountAsync(prefix, accountName, timeKey, result));
|
|
|
+ await TrySaveAsync(() => SaveStatsDimensionAsync(prefix, accountName, "message", message, timeKey));
|
|
|
+ await TrySaveAsync(() => SaveStatsDimensionAsync(prefix, accountName, "reason", reason, timeKey));
|
|
|
+ }
|
|
|
|
|
|
- await SaveStatsDimensionAsync(prefix, accountName, "reason", reason, month);
|
|
|
- await SaveStatsDimensionAsync(prefix, accountName, "reason", reason, day);
|
|
|
- await SaveStatsDimensionAsync(prefix, accountName, "reason", reason, hour);
|
|
|
+ if (firstError != null)
|
|
|
+ {
|
|
|
+ _ = new LoggerLibrary("TkLogCore", "SaveStatsAccountCache")
|
|
|
+ .Info($"prefix={prefix}, accountName={accountName}")
|
|
|
+ .Info(firstError.Message, firstError.StackTrace)
|
|
|
+ .SaveAsync();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private static void SaveStatsCount(string prefix, string accountName, string timeKey, string? dimension = null)
|