Improve logging and async handling in ResourceServer

ResourceServer now defaults to using TraceLogger if no logger is provided and properly awaits FetchBlobConfig. FetchBlobConfig is refactored to return a Task and handle missing configuration more gracefully. TraceLogger now writes logs to a file named after its namespace and sets up a TextWriterTraceListener for persistent logging.
This commit is contained in:
Namhyeon Go 2025-08-17 20:12:38 +09:00
parent 8ecdf99d78
commit cc08c46886
2 changed files with 28 additions and 8 deletions

View File

@ -44,13 +44,13 @@ namespace WelsonJS.Launcher
public ResourceServer(string prefix, string resourceName, ICompatibleLogger logger = null) public ResourceServer(string prefix, string resourceName, ICompatibleLogger logger = null)
{ {
_logger = logger; _logger = logger ?? new TraceLogger();
_prefix = prefix; _prefix = prefix;
_listener = new HttpListener(); _listener = new HttpListener();
_resourceName = resourceName; _resourceName = resourceName;
// Fetch a blob config from Internet // Fetch a blob config from Internet
FetchBlobConfig(); FetchBlobConfig().ConfigureAwait(false);
// Add resource tools // Add resource tools
_tools.Add(new ResourceTools.Completion(this, _httpClient)); _tools.Add(new ResourceTools.Completion(this, _httpClient));
@ -167,7 +167,11 @@ namespace WelsonJS.Launcher
using (var client = new HttpClient()) using (var client = new HttpClient())
{ {
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.UserAgent.ParseAdd(context.Request.UserAgent); var ua = context?.Request?.UserAgent;
if (!string.IsNullOrEmpty(ua))
{
request.Headers.UserAgent.ParseAdd(ua);
}
HttpResponseMessage response = await client.SendAsync(request); HttpResponseMessage response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
@ -430,16 +434,21 @@ namespace WelsonJS.Launcher
} }
} }
private async void FetchBlobConfig() private async Task FetchBlobConfig()
{ {
try try
{ {
string url = Program.GetAppConfig("BlobConfigUrl"); string url = Program.GetAppConfig("BlobConfigUrl");
var response = await _httpClient.GetStreamAsync(url); if (string.IsNullOrWhiteSpace(url))
{
_logger?.Warn("BlobConfigUrl is not configured.");
return;
}
var serializer = new XmlSerializer(typeof(BlobConfig)); using (var response = await _httpClient.GetStreamAsync(url))
using (var reader = new StreamReader(response)) using (var reader = new StreamReader(response))
{ {
var serializer = new XmlSerializer(typeof(BlobConfig));
_blobConfig = (BlobConfig)serializer.Deserialize(reader); _blobConfig = (BlobConfig)serializer.Deserialize(reader);
} }
@ -447,7 +456,7 @@ namespace WelsonJS.Launcher
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger?.Error($"Failed to fetch a blob config. Exception: {ex.Message}"); _logger?.Error($"Failed to fetch a blob config. Exception: {ex}");
} }
} }
} }

View File

@ -12,6 +12,17 @@ namespace WelsonJS.Launcher
{ {
public class TraceLogger : ICompatibleLogger public class TraceLogger : ICompatibleLogger
{ {
private static readonly string _logFileName;
static TraceLogger()
{
_logFileName = typeof(TraceLogger).Namespace + ".log";
var textWriterTraceListener = new TextWriterTraceListener(_logFileName);
Trace.Listeners.Add(textWriterTraceListener);
Trace.AutoFlush = true;
}
public void Info(string message) => Trace.TraceInformation(message); public void Info(string message) => Trace.TraceInformation(message);
public void Warn(string message) => Trace.TraceWarning(message); public void Warn(string message) => Trace.TraceWarning(message);
public void Error(string message) => Trace.TraceError(message); public void Error(string message) => Trace.TraceError(message);