Adding diagnostic logging for purchased tab

This commit is contained in:
= 2025-12-14 19:33:33 -05:00
parent 167d6640e3
commit aa1b5fe8f1

View File

@ -33,6 +33,10 @@ public class APIHelper : IAPIHelper
private static DynamicRules? cachedDynamicRules;
private const int MaxAttempts = 30;
private const int DelayBetweenAttempts = 3000;
private static readonly ILogger DiagnosticLog = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.File("logs/purchasedtab-diagnostics.txt", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7)
.CreateLogger();
static APIHelper()
{
@ -118,16 +122,20 @@ public class APIHelper : IAPIHelper
}
private async Task<string?> BuildHeaderAndExecuteRequests(Dictionary<string, string> getParams, string endpoint, HttpClient client)
private async Task<string?> BuildHeaderAndExecuteRequests(Dictionary<string, string> getParams, string endpoint, HttpClient client, ILogger? diagnosticLogger = null)
{
Log.Debug("Calling BuildHeaderAndExecuteRequests");
HttpRequestMessage request = await BuildHttpRequestMessage(getParams, endpoint);
diagnosticLogger?.Information("Diag request | method={Method} url={Url} headers={Headers}", request.Method, request.RequestUri, FlattenHeaders(request.Headers));
using var response = await client.SendAsync(request);
diagnosticLogger?.Information("Diag response | status={StatusCode} reason={ReasonPhrase} requestUrl={Url}", response.StatusCode, response.ReasonPhrase, request.RequestUri);
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync();
Log.Debug(body);
diagnosticLogger?.Information("Diag response body (truncated) | length={Length} body={Body}", body?.Length ?? 0, TruncateString(body, 2000));
return body;
}
@ -177,6 +185,20 @@ public class APIHelper : IAPIHelper
return client;
}
private static string FlattenHeaders(System.Net.Http.Headers.HttpRequestHeaders headers)
{
return string.Join("; ", headers.Select(h => $"{h.Key}={string.Join(",", h.Value)}"));
}
private static string TruncateString(string? value, int maxLength = 1024)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return value.Length <= maxLength ? value : $"{value[..maxLength]}...[truncated]";
}
/// <summary>
/// this one is used during initialization only
@ -248,11 +270,13 @@ public class APIHelper : IAPIHelper
{
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
DiagnosticLog.Error("PurchasedTab exception | ctx={Context} message={Message} stack={Stack}", diagContext, ex.Message, TruncateString(ex.StackTrace ?? string.Empty, 2000));
if (ex.InnerException != null)
{
Console.WriteLine("\nInner Exception:");
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
DiagnosticLog.Error("PurchasedTab inner exception | ctx={Context} message={Message} stack={Stack}", diagContext, ex.InnerException.Message, TruncateString(ex.InnerException.StackTrace ?? string.Empty, 2000));
}
}
return null;
@ -2244,6 +2268,8 @@ public class APIHelper : IAPIHelper
{
Log.Debug($"Calling GetPurchasedTab - {endpoint}");
string diagContext = $"purchasedtab-{DateTime.UtcNow:yyyyMMddTHHmmssfff}-{Guid.NewGuid():N}";
try
{
Dictionary<long, List<Purchased.List>> userPurchases = new Dictionary<long, List<Purchased.List>>();
@ -2258,8 +2284,18 @@ public class APIHelper : IAPIHelper
{ "skip_users", "all" }
};
var body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient(config));
string initialQueryParams = "?" + string.Join("&", getParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));
var initialHeaders = GetDynamicHeaders("/api2/v2" + endpoint, initialQueryParams);
DiagnosticLog.Information("PurchasedTab start | ctx={Context} endpoint={Endpoint} folder={Folder} usersCount={UsersCount} limit={Limit} query={Query} headers={Headers}", diagContext, endpoint, folder, users?.Count ?? 0, post_limit, initialQueryParams, string.Join(", ", initialHeaders.Select(kvp => $"{kvp.Key}={kvp.Value}")));
var body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient(config), DiagnosticLog);
DiagnosticLog.Information("PurchasedTab initial response | ctx={Context} bodyLength={Length} bodyPreview={BodyPreview}", diagContext, body?.Length ?? 0, TruncateString(body, 1500));
purchased = JsonConvert.DeserializeObject<Purchased>(body, m_JsonSerializerSettings);
DiagnosticLog.Information("PurchasedTab parsed initial | ctx={Context} items={Items} hasMore={HasMore}", diagContext, purchased?.list?.Count ?? 0, purchased?.hasMore ?? false);
if (purchased == null || purchased.list == null)
{
DiagnosticLog.Warning("PurchasedTab parsed initial null list | ctx={Context} bodyPreview={BodyPreview}", diagContext, TruncateString(body, 1500));
}
if (purchased != null && purchased.hasMore)
{
getParams["offset"] = purchased.list.Count.ToString();
@ -2276,11 +2312,14 @@ public class APIHelper : IAPIHelper
{
looprequest.Headers.Add(keyValuePair.Key, keyValuePair.Value);
}
DiagnosticLog.Information("PurchasedTab page request | ctx={Context} url={Url} offset={Offset} limit={Limit} headers={Headers}", diagContext, looprequest.RequestUri, getParams.ContainsKey("offset") ? getParams["offset"] : "0", post_limit, FlattenHeaders(looprequest.Headers));
using (var loopresponse = await loopclient.SendAsync(looprequest))
{
DiagnosticLog.Information("PurchasedTab page response | ctx={Context} status={Status} reason={Reason} url={Url}", diagContext, loopresponse.StatusCode, loopresponse.ReasonPhrase, looprequest.RequestUri);
loopresponse.EnsureSuccessStatusCode();
var loopbody = await loopresponse.Content.ReadAsStringAsync();
newPurchased = JsonConvert.DeserializeObject<Purchased>(loopbody, m_JsonSerializerSettings);
DiagnosticLog.Information("PurchasedTab page parsed | ctx={Context} offset={Offset} items={Items} hasMore={HasMore} bodyLength={Length} bodyPreview={BodyPreview}", diagContext, getParams.ContainsKey("offset") ? getParams["offset"] : "0", newPurchased?.list?.Count ?? 0, newPurchased?.hasMore ?? false, loopbody?.Length ?? 0, TruncateString(loopbody, 1500));
}
purchased.list.AddRange(newPurchased.list);
if (!newPurchased.hasMore)
@ -2318,6 +2357,15 @@ public class APIHelper : IAPIHelper
{
PurchasedTabCollection purchasedTabCollection = new PurchasedTabCollection();
JObject userObject = await GetUserInfoById($"/users/list?x[]={user.Key}");
string resolvedUsername = $"Deleted User - {user.Key}";
if (userObject == null || userObject[user.Key.ToString()] == null || userObject[user.Key.ToString()]["username"] == null)
{
DiagnosticLog.Warning("PurchasedTab user lookup missing username | ctx={Context} userId={UserId} rawUser={RawUser}", diagContext, user.Key, userObject != null ? TruncateString(userObject.ToString(), 500) : "null");
}
else
{
resolvedUsername = userObject[user.Key.ToString()]["username"].ToString();
}
purchasedTabCollection.UserId = user.Key;
purchasedTabCollection.Username = userObject is not null && !string.IsNullOrEmpty(userObject[user.Key.ToString()]["username"].ToString()) ? userObject[user.Key.ToString()]["username"].ToString() : $"Deleted User - {user.Key}";
string path = System.IO.Path.Combine(folder, purchasedTabCollection.Username);
@ -2325,6 +2373,11 @@ public class APIHelper : IAPIHelper
{
foreach (Purchased.List purchase in user.Value)
{
DiagnosticLog.Information("PurchasedTab processing purchase | ctx={Context} userId={UserId} username={Username} purchaseId={PurchaseId} responseType={ResponseType} mediaNull={MediaNull} mediaCount={MediaCount} postedAt={PostedAt} createdAt={CreatedAt} fromUserId={FromUserId} authorId={AuthorId}", diagContext, user.Key, resolvedUsername, purchase.id, purchase.responseType, purchase.media == null, purchase.media?.Count ?? 0, purchase.postedAt, purchase.createdAt, purchase.fromUser?.id, purchase.author?.id);
if (purchase.media == null)
{
DiagnosticLog.Warning("PurchasedTab purchase has null media | ctx={Context} userId={UserId} username={Username} purchaseId={PurchaseId} responseType={ResponseType} rawPurchase={RawPurchase}", diagContext, user.Key, resolvedUsername, purchase.id, purchase.responseType, TruncateString(JsonConvert.SerializeObject(purchase), 1500));
}
switch (purchase.responseType)
{
case "post":