Wire two-tier FileLogger channels and template sync direction flags
Extend FileLogger with Biz/Ops/Diag writers and service start/stop blocks; add EnableTemplateDeviceToDbSync / EnableTemplateDbToDeviceSync so only the enabled template direction is logged and run.main
parent
f783537917
commit
672fa2a89d
|
|
@ -39,6 +39,8 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
|
|
||||||
var config = HikvisionServiceConfig.Load(configPath);
|
var config = HikvisionServiceConfig.Load(configPath);
|
||||||
var logger = new FileLogger(config.LogDirectory);
|
var logger = new FileLogger(config.LogDirectory);
|
||||||
|
logger.Ops(OpsMarkers.Service, "WINDOWS SERVICE OnStart time=" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") +
|
||||||
|
" config=\"" + configPath + "\"");
|
||||||
|
|
||||||
_manager = new HikvisionAttendanceManager(config, logger);
|
_manager = new HikvisionAttendanceManager(config, logger);
|
||||||
_mainTask = _manager.RunAsync(_cts.Token);
|
_mainTask = _manager.RunAsync(_cts.Token);
|
||||||
|
|
@ -93,6 +95,11 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public bool EnableTemplateDbPersistence { get; set; }
|
public bool EnableTemplateDbPersistence { get; set; }
|
||||||
|
|
||||||
|
/// <summary>When true, pull face templates from device and save to DB (DEVICE -> DATABASE). Prefer only one template direction enabled at a time.</summary>
|
||||||
|
[DataMember]
|
||||||
|
public bool EnableTemplateDeviceToDbSync { get; set; }
|
||||||
|
|
||||||
|
/// <summary>When true, push face templates from DB to device (DATABASE -> DEVICE). Prefer only one template direction enabled at a time.</summary>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public bool EnableTemplateDbToDeviceSync { get; set; }
|
public bool EnableTemplateDbToDeviceSync { get; set; }
|
||||||
|
|
||||||
|
|
@ -193,14 +200,29 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public int TemplateFetchIntervalHours { get; set; } = 4;
|
public int TemplateFetchIntervalHours { get; set; } = 4;
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string MachineScopeMode { get; set; } = "CENTRAL";
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public List<string> ScopedMachineIps { get; set; } = new List<string>();
|
||||||
|
|
||||||
/// <summary>DeviceId (from <see cref="DeviceConfig.DeviceId"/>) to treat as the authoritative user source.</summary>
|
/// <summary>DeviceId (from <see cref="DeviceConfig.DeviceId"/>) to treat as the authoritative user source.</summary>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public string SourceDeviceId { get; set; } = "";
|
public string SourceDeviceId { get; set; } = "";
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string SourceMachineIp { get; set; } = "";
|
||||||
|
|
||||||
/// <summary>DeviceIds to push missing users/faces onto.</summary>
|
/// <summary>DeviceIds to push missing users/faces onto.</summary>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public List<string> TargetDeviceIds { get; set; } = new List<string>();
|
public List<string> TargetDeviceIds { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public List<string> TargetMachineIps { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public List<string> SyncEmployeeIds { get; set; } = new List<string>();
|
||||||
|
|
||||||
/// <summary>Optional directory to persist downloaded source face images for auditing or re-upload. Empty = skip file save.</summary>
|
/// <summary>Optional directory to persist downloaded source face images for auditing or re-upload. Empty = skip file save.</summary>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public string UserSyncFaceCacheDirectory { get; set; } = "";
|
public string UserSyncFaceCacheDirectory { get; set; } = "";
|
||||||
|
|
@ -224,6 +246,7 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
PreferDbMachinesOverConfig = false,
|
PreferDbMachinesOverConfig = false,
|
||||||
EnableAttendanceDbPersistence = false,
|
EnableAttendanceDbPersistence = false,
|
||||||
EnableTemplateDbPersistence = false,
|
EnableTemplateDbPersistence = false,
|
||||||
|
EnableTemplateDeviceToDbSync = false,
|
||||||
EnableTemplateDbToDeviceSync = false,
|
EnableTemplateDbToDeviceSync = false,
|
||||||
KeepAttendanceFileExport = true,
|
KeepAttendanceFileExport = true,
|
||||||
KeepTemplateFiles = true,
|
KeepTemplateFiles = true,
|
||||||
|
|
@ -249,10 +272,15 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
IsapiHttpPort = 8000,
|
IsapiHttpPort = 8000,
|
||||||
EnableTemplateFetch = false,
|
EnableTemplateFetch = false,
|
||||||
TemplateFetchIntervalHours = 4,
|
TemplateFetchIntervalHours = 4,
|
||||||
|
MachineScopeMode = "CENTRAL",
|
||||||
|
ScopedMachineIps = new List<string>(),
|
||||||
EnableUserSync = false,
|
EnableUserSync = false,
|
||||||
SyncIntervalMinutes = 60,
|
SyncIntervalMinutes = 60,
|
||||||
SourceDeviceId = "",
|
SourceDeviceId = "",
|
||||||
|
SourceMachineIp = "",
|
||||||
TargetDeviceIds = new List<string>(),
|
TargetDeviceIds = new List<string>(),
|
||||||
|
TargetMachineIps = new List<string>(),
|
||||||
|
SyncEmployeeIds = new List<string>(),
|
||||||
UserSyncFaceCacheDirectory = "",
|
UserSyncFaceCacheDirectory = "",
|
||||||
SyncPolicies = new UserSyncPoliciesConfig()
|
SyncPolicies = new UserSyncPoliciesConfig()
|
||||||
};
|
};
|
||||||
|
|
@ -296,6 +324,7 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
PreferDbMachinesOverConfig = false,
|
PreferDbMachinesOverConfig = false,
|
||||||
EnableAttendanceDbPersistence = false,
|
EnableAttendanceDbPersistence = false,
|
||||||
EnableTemplateDbPersistence = false,
|
EnableTemplateDbPersistence = false,
|
||||||
|
EnableTemplateDeviceToDbSync = false,
|
||||||
EnableTemplateDbToDeviceSync = false,
|
EnableTemplateDbToDeviceSync = false,
|
||||||
KeepAttendanceFileExport = true,
|
KeepAttendanceFileExport = true,
|
||||||
KeepTemplateFiles = true,
|
KeepTemplateFiles = true,
|
||||||
|
|
@ -321,10 +350,15 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
IsapiHttpPort = 80,
|
IsapiHttpPort = 80,
|
||||||
EnableTemplateFetch = false,
|
EnableTemplateFetch = false,
|
||||||
TemplateFetchIntervalHours = 4,
|
TemplateFetchIntervalHours = 4,
|
||||||
|
MachineScopeMode = "CENTRAL",
|
||||||
|
ScopedMachineIps = new List<string>(),
|
||||||
EnableUserSync = false,
|
EnableUserSync = false,
|
||||||
SyncIntervalMinutes = 60,
|
SyncIntervalMinutes = 60,
|
||||||
SourceDeviceId = "",
|
SourceDeviceId = "",
|
||||||
|
SourceMachineIp = "",
|
||||||
TargetDeviceIds = new List<string>(),
|
TargetDeviceIds = new List<string>(),
|
||||||
|
TargetMachineIps = new List<string>(),
|
||||||
|
SyncEmployeeIds = new List<string>(),
|
||||||
UserSyncFaceCacheDirectory = "",
|
UserSyncFaceCacheDirectory = "",
|
||||||
SyncPolicies = new UserSyncPoliciesConfig()
|
SyncPolicies = new UserSyncPoliciesConfig()
|
||||||
};
|
};
|
||||||
|
|
@ -452,11 +486,18 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
if (cfg == null)
|
if (cfg == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
cfg.MachineScopeMode = string.IsNullOrWhiteSpace(cfg.MachineScopeMode)
|
||||||
|
? "CENTRAL"
|
||||||
|
: cfg.MachineScopeMode.Trim().ToUpperInvariant();
|
||||||
cfg.SourceDeviceId = DeviceIdentity.NormalizeConfigured(cfg.SourceDeviceId);
|
cfg.SourceDeviceId = DeviceIdentity.NormalizeConfigured(cfg.SourceDeviceId);
|
||||||
|
cfg.SourceMachineIp = string.IsNullOrWhiteSpace(cfg.SourceMachineIp) ? "" : cfg.SourceMachineIp.Trim();
|
||||||
cfg.UserSyncFaceCacheDirectory = string.IsNullOrWhiteSpace(cfg.UserSyncFaceCacheDirectory)
|
cfg.UserSyncFaceCacheDirectory = string.IsNullOrWhiteSpace(cfg.UserSyncFaceCacheDirectory)
|
||||||
? ""
|
? ""
|
||||||
: cfg.UserSyncFaceCacheDirectory.Trim();
|
: cfg.UserSyncFaceCacheDirectory.Trim();
|
||||||
cfg.SyncPolicies ??= new UserSyncPoliciesConfig();
|
cfg.SyncPolicies ??= new UserSyncPoliciesConfig();
|
||||||
|
cfg.ScopedMachineIps ??= new List<string>();
|
||||||
|
for (int i = 0; i < cfg.ScopedMachineIps.Count; i++)
|
||||||
|
cfg.ScopedMachineIps[i] = (cfg.ScopedMachineIps[i] ?? "").Trim();
|
||||||
if (cfg.TargetDeviceIds == null)
|
if (cfg.TargetDeviceIds == null)
|
||||||
cfg.TargetDeviceIds = new List<string>();
|
cfg.TargetDeviceIds = new List<string>();
|
||||||
else
|
else
|
||||||
|
|
@ -464,6 +505,12 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
for (int i = 0; i < cfg.TargetDeviceIds.Count; i++)
|
for (int i = 0; i < cfg.TargetDeviceIds.Count; i++)
|
||||||
cfg.TargetDeviceIds[i] = DeviceIdentity.NormalizeConfigured(cfg.TargetDeviceIds[i]);
|
cfg.TargetDeviceIds[i] = DeviceIdentity.NormalizeConfigured(cfg.TargetDeviceIds[i]);
|
||||||
}
|
}
|
||||||
|
cfg.TargetMachineIps ??= new List<string>();
|
||||||
|
for (int i = 0; i < cfg.TargetMachineIps.Count; i++)
|
||||||
|
cfg.TargetMachineIps[i] = (cfg.TargetMachineIps[i] ?? "").Trim();
|
||||||
|
cfg.SyncEmployeeIds ??= new List<string>();
|
||||||
|
for (int i = 0; i < cfg.SyncEmployeeIds.Count; i++)
|
||||||
|
cfg.SyncEmployeeIds[i] = (cfg.SyncEmployeeIds[i] ?? "").Trim();
|
||||||
|
|
||||||
if (cfg.Devices == null)
|
if (cfg.Devices == null)
|
||||||
return;
|
return;
|
||||||
|
|
@ -556,10 +603,12 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
{
|
{
|
||||||
private readonly string _directory;
|
private readonly string _directory;
|
||||||
private readonly string _internalLogsDirectory;
|
private readonly string _internalLogsDirectory;
|
||||||
private readonly string _attendanceJobLogsDirectory;
|
private readonly string _attendanceBizDirectory;
|
||||||
private readonly string _templateJobLogsDirectory;
|
private readonly string _templateBizDirectory;
|
||||||
private readonly string _userSyncJobLogsDirectory;
|
private readonly string _userSyncBizDirectory;
|
||||||
|
private readonly string _unreachableBizDirectory;
|
||||||
private readonly object _sync = new();
|
private readonly object _sync = new();
|
||||||
|
public readonly BizSessionTotals Totals = new BizSessionTotals();
|
||||||
|
|
||||||
public FileLogger(string directory)
|
public FileLogger(string directory)
|
||||||
{
|
{
|
||||||
|
|
@ -567,69 +616,163 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
Directory.CreateDirectory(_directory);
|
Directory.CreateDirectory(_directory);
|
||||||
var logsRoot = Path.Combine(_directory, "logs");
|
var logsRoot = Path.Combine(_directory, "logs");
|
||||||
_internalLogsDirectory = Path.Combine(_directory, "internal_logs");
|
_internalLogsDirectory = Path.Combine(_directory, "internal_logs");
|
||||||
_attendanceJobLogsDirectory = Path.Combine(logsRoot, "attendance_logs");
|
_attendanceBizDirectory = Path.Combine(logsRoot, "attendance_logs");
|
||||||
_templateJobLogsDirectory = Path.Combine(logsRoot, "template_fetching_logs");
|
_templateBizDirectory = Path.Combine(logsRoot, "template_fetching_logs");
|
||||||
_userSyncJobLogsDirectory = Path.Combine(logsRoot, "user_sync_logs");
|
_userSyncBizDirectory = Path.Combine(logsRoot, "user_sync_logs");
|
||||||
|
_unreachableBizDirectory = Path.Combine(_internalLogsDirectory, "unreachable_devices");
|
||||||
Directory.CreateDirectory(_internalLogsDirectory);
|
Directory.CreateDirectory(_internalLogsDirectory);
|
||||||
Directory.CreateDirectory(_attendanceJobLogsDirectory);
|
Directory.CreateDirectory(_attendanceBizDirectory);
|
||||||
Directory.CreateDirectory(_templateJobLogsDirectory);
|
Directory.CreateDirectory(_templateBizDirectory);
|
||||||
Directory.CreateDirectory(_userSyncJobLogsDirectory);
|
Directory.CreateDirectory(_userSyncBizDirectory);
|
||||||
|
Directory.CreateDirectory(_unreachableBizDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Info(string message) => Write("INFO", message);
|
public string LogDirectory => _directory;
|
||||||
public void Warn(string message) => Write("WARN", message);
|
|
||||||
public void Error(string message, Exception? ex = null) => Write("ERROR", message + (ex is null ? "" : $" | {ex}"));
|
|
||||||
public void JobInfo(string jobName, string message) => WriteJob(jobName, "INFO", message);
|
|
||||||
public void JobWarn(string jobName, string message) => WriteJob(jobName, "WARN", message);
|
|
||||||
public void JobError(string jobName, string message) => WriteJob(jobName, "ERROR", message);
|
|
||||||
|
|
||||||
private void Write(string level, string message)
|
public void Info(string message) => WriteInternal("INFO", message);
|
||||||
|
public void Warn(string message) => WriteInternal("WARN", message);
|
||||||
|
public void Error(string message, Exception? ex = null) =>
|
||||||
|
WriteInternal("ERROR", message + (ex is null ? "" : $" | {ex}"));
|
||||||
|
|
||||||
|
/// <summary>Technical diagnostics only — never written to business logs/.</summary>
|
||||||
|
public void JobInfo(string jobName, string message) => WriteInternalDiag(jobName, "INFO", message);
|
||||||
|
public void JobWarn(string jobName, string message) => WriteInternalDiag(jobName, "WARN", message);
|
||||||
|
public void JobError(string jobName, string message) => WriteInternalDiag(jobName, "ERROR", message);
|
||||||
|
public void Diag(string jobName, string message) => WriteInternalDiag(jobName, "INFO", message);
|
||||||
|
|
||||||
|
public void Ops(string marker, string message) =>
|
||||||
|
WriteInternal("INFO", "[" + (marker ?? "OPS") + "] " + (message ?? ""));
|
||||||
|
public void OpsWarn(string marker, string message) =>
|
||||||
|
WriteInternal("WARN", "[" + (marker ?? "OPS") + "] " + (message ?? ""));
|
||||||
|
public void OpsError(string marker, string message) =>
|
||||||
|
WriteInternal("ERROR", "[" + (marker ?? "OPS") + "] " + (message ?? ""));
|
||||||
|
|
||||||
|
public void Banner(params string[] lines)
|
||||||
{
|
{
|
||||||
var line = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{level}] {message}{Environment.NewLine}";
|
if (lines == null || lines.Length == 0)
|
||||||
var logName = "internal_logs_" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
|
return;
|
||||||
var path = Path.Combine(_internalLogsDirectory, logName);
|
|
||||||
lock (_sync)
|
lock (_sync)
|
||||||
{
|
{
|
||||||
File.AppendAllText(path, line);
|
var sb = new StringBuilder();
|
||||||
|
var stamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||||
|
sb.Append(stamp).Append(" [INFO] ============================================================").Append(Environment.NewLine);
|
||||||
|
foreach (var line in lines)
|
||||||
|
sb.Append(stamp).Append(" [INFO] ").Append(line ?? "").Append(Environment.NewLine);
|
||||||
|
sb.Append(stamp).Append(" [INFO] ============================================================").Append(Environment.NewLine);
|
||||||
|
File.AppendAllText(InternalLogPath(), sb.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteJob(string jobName, string level, string message)
|
// ---- Business / operations logs (human-readable, no SDK) ----
|
||||||
|
|
||||||
|
public void Biz(BizChannel channel, params string[] lines)
|
||||||
{
|
{
|
||||||
string dir;
|
if (lines == null || lines.Length == 0)
|
||||||
string prefix;
|
return;
|
||||||
switch ((jobName ?? "").Trim().ToLowerInvariant())
|
var sb = new StringBuilder();
|
||||||
|
foreach (var line in lines)
|
||||||
|
sb.AppendLine(line ?? "");
|
||||||
|
AppendBiz(channel, sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BizBlank(BizChannel channel) => AppendBiz(channel, Environment.NewLine);
|
||||||
|
|
||||||
|
public void BizSeparator(BizChannel channel) =>
|
||||||
|
AppendBiz(channel, "------------------------------------------------------------" + Environment.NewLine + Environment.NewLine);
|
||||||
|
|
||||||
|
public void WriteBizServiceStarted()
|
||||||
|
{
|
||||||
|
Totals.StartedAt = DateTime.Now;
|
||||||
|
var block =
|
||||||
|
"------------------------------------------------------------" + Environment.NewLine +
|
||||||
|
"Service started at " + BizFriendlyReasons.FormatTime(Totals.StartedAt) + Environment.NewLine +
|
||||||
|
"------------------------------------------------------------" + Environment.NewLine +
|
||||||
|
Environment.NewLine;
|
||||||
|
AppendBiz(BizChannel.Attendance, block);
|
||||||
|
AppendBiz(BizChannel.UserSync, block);
|
||||||
|
AppendBiz(BizChannel.Template, block);
|
||||||
|
AppendBiz(BizChannel.Unreachable, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteBizServiceStopped()
|
||||||
|
{
|
||||||
|
var stopped =
|
||||||
|
Environment.NewLine +
|
||||||
|
"------------------------------------------------------------" + Environment.NewLine +
|
||||||
|
"Service stopped at " + BizFriendlyReasons.FormatTime(DateTime.Now) + Environment.NewLine +
|
||||||
|
Environment.NewLine +
|
||||||
|
Totals.FormatSummaryBlock() +
|
||||||
|
Environment.NewLine;
|
||||||
|
AppendBiz(BizChannel.Attendance, stopped);
|
||||||
|
AppendBiz(BizChannel.UserSync, stopped);
|
||||||
|
AppendBiz(BizChannel.Template, stopped);
|
||||||
|
AppendBiz(BizChannel.Unreachable, stopped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteBizDatabaseFailed(string mysqlErrorCode, string friendlyReason)
|
||||||
|
{
|
||||||
|
Biz(BizChannel.Attendance,
|
||||||
|
"Database connection failed.",
|
||||||
|
"",
|
||||||
|
"Reason :",
|
||||||
|
"",
|
||||||
|
string.IsNullOrWhiteSpace(mysqlErrorCode) ? "[MySQL Error]" : "[MySQL Error " + mysqlErrorCode + "]",
|
||||||
|
"",
|
||||||
|
string.IsNullOrWhiteSpace(friendlyReason) ? "Unable to connect to MySQL server." : friendlyReason,
|
||||||
|
"",
|
||||||
|
"Attendance synchronization cannot continue.",
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendBiz(BizChannel channel, string text)
|
||||||
|
{
|
||||||
|
string path;
|
||||||
|
switch (channel)
|
||||||
{
|
{
|
||||||
case "attendance":
|
case BizChannel.Attendance:
|
||||||
case "attendance_logs":
|
path = Path.Combine(_attendanceBizDirectory, "attendance_logs_" + DateStamp() + ".txt");
|
||||||
dir = _attendanceJobLogsDirectory;
|
|
||||||
prefix = "attendance_logs_";
|
|
||||||
break;
|
break;
|
||||||
case "template":
|
case BizChannel.UserSync:
|
||||||
case "template_fetch":
|
path = Path.Combine(_userSyncBizDirectory, "user_sync_logs_" + DateStamp() + ".txt");
|
||||||
case "template_fetching_logs":
|
|
||||||
dir = _templateJobLogsDirectory;
|
|
||||||
prefix = "template_fetching_logs_";
|
|
||||||
break;
|
break;
|
||||||
case "user_sync":
|
case BizChannel.Template:
|
||||||
case "user_sync_logs":
|
path = Path.Combine(_templateBizDirectory, "template_fetching_logs_" + DateStamp() + ".txt");
|
||||||
dir = _userSyncJobLogsDirectory;
|
break;
|
||||||
prefix = "user_sync_logs_";
|
case BizChannel.Unreachable:
|
||||||
|
path = Path.Combine(_unreachableBizDirectory, "unreachable_devices_" + DateStamp() + ".txt");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dir = _internalLogsDirectory;
|
path = InternalLogPath();
|
||||||
prefix = "internal_logs_";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var line = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{level}] {message}{Environment.NewLine}";
|
|
||||||
var path = Path.Combine(dir, prefix + DateTime.Now.ToString("yyyy-MM-dd") + ".txt");
|
|
||||||
lock (_sync)
|
lock (_sync)
|
||||||
{
|
{
|
||||||
File.AppendAllText(path, line);
|
File.AppendAllText(path, text, Encoding.UTF8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string DateStamp() => DateTime.Now.ToString("yyyy-MM-dd");
|
||||||
|
|
||||||
|
private string InternalLogPath() =>
|
||||||
|
Path.Combine(_internalLogsDirectory, "internal_logs_" + DateStamp() + ".txt");
|
||||||
|
|
||||||
|
private void WriteInternal(string level, string message)
|
||||||
|
{
|
||||||
|
var line = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{level}] {message}{Environment.NewLine}";
|
||||||
|
lock (_sync)
|
||||||
|
{
|
||||||
|
File.AppendAllText(InternalLogPath(), line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteInternalDiag(string jobName, string level, string message)
|
||||||
|
{
|
||||||
|
var tag = (jobName ?? "diag").Trim().ToUpperInvariant();
|
||||||
|
if (tag.Length == 0) tag = "DIAG";
|
||||||
|
WriteInternal(level, "[DIAG][" + tag + "] " + message);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue