Updated
This commit is contained in:
+31
-16
@@ -7,28 +7,43 @@ using EonaCat.LogStack.Core;
|
||||
var logger = new LogBuilder("MyApp")
|
||||
.WithMinimumLevel(LogLevel.Information)
|
||||
.WriteToConsole()
|
||||
.WriteToFile("C:\\tesss", maxFileSize: 50 * 1024 * 1024)
|
||||
//.WriteToJsonFile("./logs", maxFileSize: 50 * 1024 * 1024)
|
||||
//.WriteToHttp("https://127.0.0.1")
|
||||
//.WriteToUdp("127.0.0.1", 514)
|
||||
//.WriteToTcp("127.0.0.1", 514)
|
||||
//.WriteToDatabase(null)
|
||||
//.WriteToDiscord("https://discord.com/api/webhooks/...")
|
||||
//.WriteToMicrosoftTeams("https://outlook.office.com/webhook/...")
|
||||
//.WriteToElasticSearch("http://localhost:9200/logs")
|
||||
//.WriteToGraylogFlow(null)
|
||||
//.WriteToZabbixFlow(null)
|
||||
.WriteToFile("./testlogs", maxFileSize: 50 * 1024 * 1024)
|
||||
.BoostWithCorrelationId()
|
||||
.BoostWithProcessId()
|
||||
.Build();
|
||||
|
||||
Console.WriteLine("Logger created successfully");
|
||||
Console.WriteLine("Writing log entries...");
|
||||
|
||||
|
||||
while (true)
|
||||
// Test: Write a few log entries and then dispose
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
logger.Information("Application started");
|
||||
logger.Error(new Exception("Nerd!"), "Something went wrong");
|
||||
await Task.Delay(1);
|
||||
logger.Information("Application started - iteration " + i);
|
||||
logger.Error(new Exception("Nerd!"), "Something went wrong - iteration " + i);
|
||||
}
|
||||
|
||||
Console.WriteLine("Wrote 10 log entries");
|
||||
|
||||
// Wait a bit for the flush to happen
|
||||
await Task.Delay(3000);
|
||||
|
||||
Console.WriteLine("Disposing logger...");
|
||||
await logger.DisposeAsync(); // Flushes all logs
|
||||
|
||||
Console.WriteLine("Logging complete. Check ./testlogs for log files.");
|
||||
|
||||
// List files that were created
|
||||
var logdir = new System.IO.DirectoryInfo("./testlogs");
|
||||
if (logdir.Exists)
|
||||
{
|
||||
var files = logdir.GetFiles();
|
||||
Console.WriteLine($"Found {files.Length} log files:");
|
||||
foreach (var file in files)
|
||||
{
|
||||
Console.WriteLine($" - {file.Name} ({file.Length} bytes)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("./testlogs directory does not exist!");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
|
||||
<Copyright>EonaCat (Jeroen Saey)</Copyright>
|
||||
<PackageTags>EonaCat;Logger;EonaCatLogStack;Log;Writer;Flows;LogStack;Memory;Speed;Jeroen;Saey</PackageTags>
|
||||
<PackageIconUrl />
|
||||
<FileVersion>0.1.4</FileVersion>
|
||||
<FileVersion>0.1.6</FileVersion>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
@@ -25,7 +25,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EVRevisionFormat>0.1.4+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||
<EVRevisionFormat>0.1.6+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||
<EVDefault>true</EVDefault>
|
||||
<EVInfo>true</EVInfo>
|
||||
<EVTagMatch>v[0-9]*</EVTagMatch>
|
||||
@@ -36,7 +36,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>0.1.4</Version>
|
||||
<Version>0.1.6</Version>
|
||||
<PackageId>EonaCat.LogStack</PackageId>
|
||||
<Product>EonaCat.LogStack</Product>
|
||||
<RepositoryUrl>https://git.saey.me/EonaCat/EonaCat.LogStack</RepositoryUrl>
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -200,6 +201,18 @@ namespace EonaCat.LogStack.Flows
|
||||
_outputFormat = outputFormat;
|
||||
_compressionFormat = compression;
|
||||
|
||||
// Use the cascading fallback strategy to resolve the final logging directory
|
||||
try
|
||||
{
|
||||
_directory = ResolveLoggingDirectory(_directory);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteToConsoleError($"[FileFlow] Critical error in directory resolution: {ex.Message}");
|
||||
_directory = Path.GetTempPath();
|
||||
OnDirectoryException?.Invoke(this, $"FileFlow: Critical failure in directory resolution: {ex.Message}. Falling back to temp: '{_directory}'");
|
||||
}
|
||||
|
||||
_logLevelsForSeparateFiles = logLevelsForSeparateFiles != null
|
||||
? new HashSet<LogLevel>(logLevelsForSeparateFiles)
|
||||
: new HashSet<LogLevel>();
|
||||
@@ -207,58 +220,13 @@ namespace EonaCat.LogStack.Flows
|
||||
SetFileExtension(outputFormat);
|
||||
CompileTemplate(template);
|
||||
|
||||
// Resolve relative path
|
||||
if (_directory.StartsWith("./", StringComparison.Ordinal))
|
||||
{
|
||||
_directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _directory.Substring(2));
|
||||
}
|
||||
|
||||
// Never allow logging initialization to crash the host because of a bad
|
||||
// directory (ACLs, antivirus locks, read-only locations, etc.).
|
||||
// Fall back to a per-user temp directory.
|
||||
try
|
||||
{
|
||||
// Use DirectoryPermissionHelper to ensure directory with proper permissions
|
||||
if (!Helpers.DirectoryPermissionHelper.EnsureDirectoryHierarchy(_directory))
|
||||
{
|
||||
throw new UnauthorizedAccessException($"Cannot write to directory: {Helpers.DirectoryPermissionHelper.GetAccessIssueDiagnosis(_directory)}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var processId = Process.GetCurrentProcess().Id;
|
||||
var newDirectory = Path.Combine(Path.GetTempPath(), "EonaCat.LogStack", processId.ToString());
|
||||
|
||||
if (Helpers.DirectoryPermissionHelper.EnsureDirectoryHierarchy(newDirectory))
|
||||
{
|
||||
OnDirectoryException?.Invoke(this, $"FileFlow: Could not create directory: '{_directory}' ({ex.Message}), using directory '{newDirectory}' instead");
|
||||
_directory = newDirectory;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to create fallback directory: {newDirectory}");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
var newDirectory = Path.GetTempPath();
|
||||
OnDirectoryException?.Invoke(this, $"FileFlow: Could not create any writable directory, falling back to temp: '{newDirectory}'. Original error: {ex.Message}");
|
||||
_directory = newDirectory;
|
||||
|
||||
// Last resort: disable file output by pointing to temp path.
|
||||
// The writer thread still runs and swallows failures.
|
||||
}
|
||||
}
|
||||
|
||||
// BlockingCollection with bounded capacity
|
||||
_queue = new BlockingCollection<LogEvent>(new ConcurrentQueue<LogEvent>(), QueueCapacity);
|
||||
|
||||
// Dedicated writer thread
|
||||
_writerThread = new Thread(WriterThreadBody)
|
||||
{
|
||||
IsBackground = true,
|
||||
IsBackground = false,
|
||||
Name = "FileFlow.Writer[" + filePrefix + "]",
|
||||
Priority = ThreadPriority.AboveNormal,
|
||||
};
|
||||
@@ -830,6 +798,14 @@ namespace EonaCat.LogStack.Flows
|
||||
_isDisposing = true; // Signal all threads that disposal is in progress
|
||||
|
||||
_queue.CompleteAdding();
|
||||
|
||||
// Give the writer thread time to drain the queue before canceling
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
while (_queue.Count > 0 && sw.Elapsed < TimeSpan.FromSeconds(5))
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
_cts.Cancel();
|
||||
|
||||
// Give threads more time to gracefully exit (increased from 2000ms to 5000ms)
|
||||
@@ -873,6 +849,7 @@ namespace EonaCat.LogStack.Flows
|
||||
}
|
||||
private void WriterThreadBody()
|
||||
{
|
||||
WriteToConsoleError("[FileFlow] Writer thread started");
|
||||
try
|
||||
{
|
||||
while (!_queue.IsCompleted)
|
||||
@@ -940,6 +917,8 @@ namespace EonaCat.LogStack.Flows
|
||||
|
||||
try
|
||||
{
|
||||
// Write to both stdout and stderr to ensure visibility
|
||||
Console.WriteLine(text);
|
||||
Console.Error.WriteLine(text);
|
||||
}
|
||||
catch
|
||||
@@ -950,7 +929,7 @@ namespace EonaCat.LogStack.Flows
|
||||
|
||||
private void WriteLogEvent(LogEvent log)
|
||||
{
|
||||
long size = 0;
|
||||
long size =0;
|
||||
try
|
||||
{
|
||||
size = EstimateSize(log);
|
||||
@@ -1015,12 +994,28 @@ namespace EonaCat.LogStack.Flows
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureFileOpen(path, log);
|
||||
|
||||
if (!EnsureFileOpen(path, log))
|
||||
{
|
||||
// File open failed; skip write operation but continue with other processing
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ShouldRotate(path, line.Length))
|
||||
{
|
||||
string archived = RotateFile(path);
|
||||
EnsureFileOpen(path, log);
|
||||
if (!EnsureFileOpen(path, log))
|
||||
{
|
||||
// File reopen after rotation failed; skip this write
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenFile of;
|
||||
if (_openFiles.TryGetValue(path, out of))
|
||||
{
|
||||
of.Writer.WriteLine(line);
|
||||
of.Size += line.Length + Environment.NewLine.Length;
|
||||
}
|
||||
}
|
||||
if (archived != null)
|
||||
{
|
||||
Action<string> onRotate = _onRotate;
|
||||
@@ -1031,13 +1026,21 @@ namespace EonaCat.LogStack.Flows
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
OpenFile of;
|
||||
if (_openFiles.TryGetValue(path, out of))
|
||||
{
|
||||
of.Writer.WriteLine(line);
|
||||
of.Size += line.Length + Environment.NewLine.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
// File handle disappeared between EnsureFileOpen and write attempt
|
||||
WriteToConsoleError("[FileFlow] File handle for '" + path + "' missing after EnsureFileOpen");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1522,7 +1525,7 @@ namespace EonaCat.LogStack.Flows
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureFileOpen(string path, LogEvent logEvent)
|
||||
private bool EnsureFileOpen(string path, LogEvent logEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1533,7 +1536,7 @@ namespace EonaCat.LogStack.Flows
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -1584,6 +1587,7 @@ namespace EonaCat.LogStack.Flows
|
||||
try
|
||||
{
|
||||
of.Writer.Write(CsvHeader);
|
||||
of.Writer.Flush();
|
||||
of.HasCsvHeader = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1600,6 +1604,7 @@ namespace EonaCat.LogStack.Flows
|
||||
{
|
||||
of.Writer.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
of.Writer.WriteLine("<logs>");
|
||||
of.Writer.Flush();
|
||||
of.HasXmlHeader = true;
|
||||
}
|
||||
else
|
||||
@@ -1614,6 +1619,8 @@ namespace EonaCat.LogStack.Flows
|
||||
WriteToConsoleError("[FileFlow] XML header write error for '" + path + "': " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1640,6 +1647,7 @@ namespace EonaCat.LogStack.Flows
|
||||
}
|
||||
}
|
||||
WriteToConsoleError("[FileFlow] Failed to open '" + path + "': " + ex.Message + diagnosis);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1650,6 +1658,7 @@ namespace EonaCat.LogStack.Flows
|
||||
WriteToConsoleError("[FileFlow] EnsureFileOpen unhandled error: " + ex.Message);
|
||||
}
|
||||
catch { /* ignore */ }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1870,6 +1879,7 @@ namespace EonaCat.LogStack.Flows
|
||||
|
||||
private void PeriodicFlushLoop()
|
||||
{
|
||||
WriteToConsoleError("[FileFlow] Periodic flush loop started");
|
||||
try
|
||||
{
|
||||
while (!_isDisposing && !_cts.Token.IsCancellationRequested)
|
||||
@@ -2032,6 +2042,141 @@ namespace EonaCat.LogStack.Flows
|
||||
string.Concat(prefix, "_", CachedMachineName, "_", date.ToString(_dateFormat), _fileExtension));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the logging directory with a cascading fallback strategy:
|
||||
/// 1. Try the requested directory
|
||||
/// 2. Try to fix permissions on the requested directory
|
||||
/// 3. Fall back to %appdata%/EonaCat.LogStack/
|
||||
/// 4. Fall back to TEMP directory
|
||||
/// </summary>
|
||||
private string ResolveLoggingDirectory(string requestedDirectory)
|
||||
{
|
||||
// Resolve relative paths first
|
||||
string workingDirectory = requestedDirectory;
|
||||
if (workingDirectory.StartsWith("./", StringComparison.Ordinal))
|
||||
{
|
||||
workingDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, workingDirectory.Substring(2));
|
||||
}
|
||||
|
||||
// Attempt 1: Use the requested directory
|
||||
WriteToConsoleError($"[FileFlow] Attempting to use log directory: {workingDirectory}");
|
||||
if (Helpers.DirectoryPermissionHelper.EnsureDirectoryHierarchy(workingDirectory))
|
||||
{
|
||||
WriteToConsoleError($"[FileFlow] Successfully using log directory: {workingDirectory}");
|
||||
return workingDirectory;
|
||||
}
|
||||
|
||||
WriteToConsoleError($"[FileFlow] Failed to use log directory: {workingDirectory}. Diagnosis: {Helpers.DirectoryPermissionHelper.GetAccessIssueDiagnosis(workingDirectory)}");
|
||||
|
||||
// Attempt 2: Try to fix permissions on the requested directory
|
||||
WriteToConsoleError($"[FileFlow] Attempting to fix permissions on: {workingDirectory}");
|
||||
if (TryFixDirectoryPermissions(workingDirectory) && Helpers.DirectoryPermissionHelper.EnsureDirectoryHierarchy(workingDirectory))
|
||||
{
|
||||
WriteToConsoleError($"[FileFlow] Successfully fixed permissions and using log directory: {workingDirectory}");
|
||||
return workingDirectory;
|
||||
}
|
||||
|
||||
WriteToConsoleError($"[FileFlow] Failed to fix permissions on: {workingDirectory}");
|
||||
|
||||
// Attempt 3: Fall back to AppData directory
|
||||
string appDataDirectory = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"EonaCat.LogStack");
|
||||
|
||||
WriteToConsoleError($"[FileFlow] Attempting to use AppData log directory: {appDataDirectory}");
|
||||
if (Helpers.DirectoryPermissionHelper.EnsureDirectoryHierarchy(appDataDirectory))
|
||||
{
|
||||
WriteToConsoleError($"[FileFlow] Successfully using AppData log directory: {appDataDirectory}");
|
||||
OnDirectoryException?.Invoke(this, $"FileFlow: Could not use requested directory '{requestedDirectory}', fell back to '{appDataDirectory}'");
|
||||
return appDataDirectory;
|
||||
}
|
||||
|
||||
WriteToConsoleError($"[FileFlow] Failed to use AppData log directory: {appDataDirectory}. Diagnosis: {Helpers.DirectoryPermissionHelper.GetAccessIssueDiagnosis(appDataDirectory)}");
|
||||
|
||||
// Attempt 4: Fall back to TEMP directory
|
||||
string tempDirectory = Path.GetTempPath();
|
||||
WriteToConsoleError($"[FileFlow] Attempting to use TEMP log directory: {tempDirectory}");
|
||||
|
||||
try
|
||||
{
|
||||
if (Helpers.DirectoryPermissionHelper.CanWrite(tempDirectory))
|
||||
{
|
||||
WriteToConsoleError($"[FileFlow] Successfully using TEMP log directory: {tempDirectory}");
|
||||
OnDirectoryException?.Invoke(this, $"FileFlow: Could not use requested directory '{requestedDirectory}' or AppData, fell back to TEMP directory '{tempDirectory}'");
|
||||
return tempDirectory;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Temp directory check failed
|
||||
}
|
||||
|
||||
WriteToConsoleError($"[FileFlow] Failed to use TEMP log directory: {tempDirectory}. This is a critical error - logging may not work.");
|
||||
|
||||
// Final fallback: return the temp path anyway, even if it might not work
|
||||
OnDirectoryException?.Invoke(this, $"FileFlow: Critical - could not find any writable directory. Attempting to use TEMP: '{tempDirectory}'");
|
||||
return tempDirectory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to fix directory permissions across all platforms (Windows, Linux, Mac).
|
||||
/// On Windows: Clears read-only attributes.
|
||||
/// On Linux/Mac: Uses chmod to set full permissions.
|
||||
/// Returns true if fix was successful or attempted, false if it failed or is not applicable.
|
||||
/// </summary>
|
||||
private bool TryFixDirectoryPermissions(string dirPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(dirPath))
|
||||
{
|
||||
// Try to create it first
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dirPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to set everyone permissions (handles all platforms)
|
||||
if (Helpers.DirectoryPermissionHelper.TrySetEveryonePermissions(dirPath))
|
||||
{
|
||||
WriteToConsoleError($"[FileFlow] Set permissions on: {dirPath}");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback: On Windows, try clearing the read-only attribute directly
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
try
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(dirPath);
|
||||
if ((dirInfo.Attributes & FileAttributes.ReadOnly) != 0)
|
||||
{
|
||||
dirInfo.Attributes &= ~FileAttributes.ReadOnly;
|
||||
WriteToConsoleError($"[FileFlow] Cleared read-only attribute on: {dirPath}");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteToConsoleError($"[FileFlow] Failed to fix permissions: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// On Unix-like systems, we've already attempted chmod via TrySetEveryonePermissions
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetFileExtension(FileOutputFormat fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -35,7 +36,7 @@ namespace EonaCat.LogStack.Helpers
|
||||
}
|
||||
|
||||
// Try to create a temporary file to verify write access
|
||||
var testFile = Path.Combine(dirPath, ".permission_test_" + Guid.NewGuid().ToString("."));
|
||||
var testFile = Path.Combine(dirPath, ".permission_test_" + Guid.NewGuid().ToString("N"));
|
||||
try
|
||||
{
|
||||
using (var fs = File.Create(testFile, 1, FileOptions.DeleteOnClose))
|
||||
@@ -150,6 +151,115 @@ namespace EonaCat.LogStack.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to set full permissions for everyone on the directory.
|
||||
/// Falls back to clearing the read-only attribute if needed on Windows.
|
||||
/// Uses chmod on Linux/Mac and Windows permission APIs on Windows.
|
||||
/// This is a best-effort operation for all platforms.
|
||||
/// </summary>
|
||||
public static bool TrySetEveryonePermissions(string dirPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dirPath) || !Directory.Exists(dirPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return TrySetWindowsPermissions(dirPath);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return TrySetUnixPermissions(dirPath);
|
||||
}
|
||||
|
||||
// Unknown platform - try whatever we can
|
||||
return CanWrite(dirPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to set Windows directory permissions by clearing the read-only attribute.
|
||||
/// </summary>
|
||||
private static bool TrySetWindowsPermissions(string dirPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(dirPath);
|
||||
|
||||
// Clear read-only attribute if set
|
||||
if ((dirInfo.Attributes & FileAttributes.ReadOnly) != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
dirInfo.Attributes &= ~FileAttributes.ReadOnly;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Continue even if attribute clearing fails
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the directory is now writable
|
||||
return CanWrite(dirPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to set Unix (Linux/Mac) directory permissions using chmod.
|
||||
/// Sets full permissions (777) for the directory.
|
||||
/// </summary>
|
||||
private static bool TrySetUnixPermissions(string dirPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dirPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Use chmod to set full permissions (777)
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "chmod",
|
||||
Arguments = $"777 \"{dirPath}\"",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
process.WaitForExit(5000); // Wait up to 5 seconds
|
||||
|
||||
if (process.ExitCode == 0)
|
||||
{
|
||||
// Verify the directory is now writable
|
||||
return CanWrite(dirPath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// chmod not available or failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to grant write permissions on Windows by clearing the read-only attribute.
|
||||
/// This is a best-effort operation and may fail on restricted systems.
|
||||
|
||||
@@ -51,21 +51,24 @@ namespace EonaCat.LogStack.Test.Web
|
||||
var logBuilder = new LogBuilder();
|
||||
logBuilder.WithTimestampMode(TimestampMode.Local);
|
||||
logBuilder.WriteToConsole();
|
||||
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.Csv);
|
||||
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.StructuredJson);
|
||||
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.Text);
|
||||
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.Json);
|
||||
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.Xml);
|
||||
|
||||
// Use absolute path to ensure directory creation works
|
||||
var logDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "EonaCat.LogStack", "Logs");
|
||||
Directory.CreateDirectory(logDirectory);
|
||||
|
||||
logBuilder.WriteToFile(logDirectory, outputFormat: FileOutputFormat.Csv);
|
||||
logBuilder.WriteToFile(logDirectory, outputFormat: FileOutputFormat.StructuredJson);
|
||||
logBuilder.WriteToFile(logDirectory, outputFormat: FileOutputFormat.Text);
|
||||
logBuilder.WriteToFile(logDirectory, outputFormat: FileOutputFormat.Json);
|
||||
logBuilder.WriteToFile(logDirectory, outputFormat: FileOutputFormat.Xml);
|
||||
logBuilder.WriteToTcp("127.0.0.1", 514);
|
||||
//logBuilder.WriteToEncryptedFile("./logs");
|
||||
//logBuilder.WriteDiagnostics();
|
||||
logBuilder.WriteToAudit(
|
||||
directory: "./audit",
|
||||
directory: Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "EonaCat.LogStack", "Audit"),
|
||||
auditLevel: AuditLevel.WarningAndAbove, // only Warn+ go to audit trail
|
||||
includeProperties: true);
|
||||
//logBuilder.WriteToRollingBuffer();
|
||||
var consoleFlow = logBuilder.GetFlow(typeof(ConsoleFlow));
|
||||
logBuilder.WriteToThrottled(consoleFlow);
|
||||
logBuilder.WriteToStatusFlow(new List<ServiceMonitoring.ServiceStatus> { new ServiceMonitoring.ServiceStatus { Host = "google.com", Port = 443, ServiceType = ServiceMonitoring.ServiceType.HTTPS } }, checkInterval: TimeSpan.FromSeconds(5));
|
||||
//logBuilder.WriteToSignalR();
|
||||
//logBuilder.WriteToEmailFlow();
|
||||
|
||||
Reference in New Issue
Block a user