Resolve service logs under interactive user profile
Resolves %LocalAppData% from the logged-on console user and separates client/backend log folders.feature/centralized-offline-canteen
parent
42cd473e73
commit
4d8ee06b04
|
|
@ -0,0 +1,58 @@
|
|||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace UtopiaCanteenSystem.Services.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the logged-on Windows user's profile paths when the app runs as a service (SYSTEM).
|
||||
/// </summary>
|
||||
internal static class InteractiveUserPath
|
||||
{
|
||||
public static string? TryGetLocalAppDataPath()
|
||||
{
|
||||
var profile = TryGetProfileDirectory();
|
||||
if (string.IsNullOrWhiteSpace(profile))
|
||||
return null;
|
||||
|
||||
var localAppData = Path.Combine(profile, "AppData", "Local");
|
||||
return Directory.Exists(localAppData) ? localAppData : null;
|
||||
}
|
||||
|
||||
private static string? TryGetProfileDirectory()
|
||||
{
|
||||
var sessionId = WTSGetActiveConsoleSessionId();
|
||||
if (sessionId == 0xFFFFFFFF)
|
||||
return null;
|
||||
|
||||
if (!WTSQueryUserToken(sessionId, out var userToken) || userToken == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var sb = new StringBuilder(512);
|
||||
var size = sb.Capacity;
|
||||
if (!GetUserProfileDirectory(userToken, sb, ref size, out _))
|
||||
return null;
|
||||
|
||||
var path = sb.ToString();
|
||||
return string.IsNullOrWhiteSpace(path) ? null : path;
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseHandle(userToken);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern uint WTSGetActiveConsoleSessionId();
|
||||
|
||||
[DllImport("wtsapi32.dll", SetLastError = true)]
|
||||
private static extern bool WTSQueryUserToken(uint sessionId, out IntPtr phToken);
|
||||
|
||||
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern bool GetUserProfileDirectory(IntPtr hToken, StringBuilder lpProfileDir, ref int lpcchSize, out int lpDwFlags);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using System.IO;
|
||||
|
||||
namespace UtopiaCanteenSystem.Services.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// File log directories. Client uses the current user's LocalAppData.
|
||||
/// Backend (Windows Service) uses the interactive user's LocalAppData when possible
|
||||
/// so logs appear under the logged-on user's profile instead of systemprofile.
|
||||
/// </summary>
|
||||
public static class LogPaths
|
||||
{
|
||||
private static string? _backendLogsDirectoryOverride;
|
||||
private static string? _cachedLocalAppDataRoot;
|
||||
|
||||
/// <summary>
|
||||
/// Optional absolute path (e.g. from appsettings LogsDirectory). Used by the backend service only.
|
||||
/// </summary>
|
||||
public static void SetBackendLogsDirectory(string? directory)
|
||||
{
|
||||
_backendLogsDirectoryOverride = string.IsNullOrWhiteSpace(directory)
|
||||
? null
|
||||
: directory.Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client logs folder: %LocalAppData%\UtopiaCanteenClient\Logs.
|
||||
/// </summary>
|
||||
public static string ClientLogsDirectory =>
|
||||
Path.Combine(ResolveLocalAppDataRoot(), "UtopiaCanteenClient", "Logs");
|
||||
|
||||
/// <summary>
|
||||
/// Backend logs folder: %LocalAppData%\UtopiaCanteenBackend\Logs
|
||||
/// (interactive user when running as a service).
|
||||
/// </summary>
|
||||
public static string BackendLogsDirectory =>
|
||||
_backendLogsDirectoryOverride ?? Path.Combine(ResolveLocalAppDataRoot(), "UtopiaCanteenBackend", "Logs");
|
||||
|
||||
public static string GetBackendLogFilePath(DateTime? date = null)
|
||||
{
|
||||
var d = date ?? DateTime.Now;
|
||||
return Path.Combine(BackendLogsDirectory, $"backend-{d:yyyy-MM-dd}.log");
|
||||
}
|
||||
|
||||
public static string GetClientLogFilePath(DateTime? date = null)
|
||||
{
|
||||
var d = date ?? DateTime.Now;
|
||||
return Path.Combine(ClientLogsDirectory, $"client-{d:yyyy-MM-dd}.log");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User's LocalAppData, or SYSTEM profile when no interactive session (service at login screen).
|
||||
/// </summary>
|
||||
public static string ResolveLocalAppDataRoot()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_cachedLocalAppDataRoot))
|
||||
return _cachedLocalAppDataRoot;
|
||||
|
||||
var interactive = InteractiveUserPath.TryGetLocalAppDataPath();
|
||||
if (!string.IsNullOrWhiteSpace(interactive))
|
||||
return _cachedLocalAppDataRoot = interactive;
|
||||
|
||||
return _cachedLocalAppDataRoot =
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue