-
Notifications
You must be signed in to change notification settings - Fork 124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow Log File to be Deleted at Runtime #96
Comments
Hi - It seems like there are possibly two separate requirements here:
I think we could allow (1) without initially worrying about (2) - this might be handy, for instance, in the situation where unbounded log file growth occurs and the file needs to be removed before the process can be (safely?) stopped. I'm not sure how widely valued this scenario is, though - any thoughts? |
+1 for this issue and the related #128 . I have migrated a large solution from log4net to Serilog only to discover this issue when moving to production. Back to log4net until this gets resolved. |
This is definitely an issue I wanted to switch to serilog but found this issue. When the app is allowing the log file to be deleted means there is less to sift through to find a problem. We do this all the time and it saves time finding issues. Why are you locking the log file? |
Thanks for the nudge! Nothing's happened here because between my:
and any response was four years, which somewhat slowed thing down :-) I think the next step would be for someone to investigate the impact of making this change (part 1, or parts 1 & 2) and can share some info on what would be required, and which other parts of the code might break (especially around rolling, perhaps?). If the change is small and the impact low and well-understood, I don't think anyone has been opposed to supporting this - it's just a prioritization issue currently. HTH! |
I have just found this to replace Log4Net due to versioning issues. My use case is that I have a windows service that is performing a data sync and logging is required to be able to monitor it. I have the service and a monitoring app that will reload the log files found in a specific location. I have also found while replacing log4net, the FileSystemWatcher fails to fire events on the log files in the folder because they are locked. My work around for now (I really don't want to roll back to log4net) Instead of creating the logger at the app start, I have a method I call to setup the logger and write out the information then re-setup
While this is not ideal , it solves the problem in that the watcher detects changes and you can delete the file as you want and it will get re-created next time its needed Shared = true did not work with the watcher either My app settings if your interested
|
+1 to this issue - I have a scenario in my desktop app where I need a new logger each time a "robot" is launched. Once it's done, I display the executions historical data to the users and they can decide whether to keep the log files and other data pertinent to the specific robot run. The issue is they cannot remove the last entry without restarting the app as it's locked. |
@JakubTracz disposing the per-robot logger before attempting to delete the file should sort this out. |
@nblumhardt I'm not sure how to achieve it with DI. This is my setup: I use Map to create 2 loggers: 1/app run & 1 per each robot run When running the robot I'm creating a scope by adding the robot key property so it's mapped correctly and creates a robot-run-specific file: Inside Robot.Run, there a lot of different injected services, all of them using an ILogger from the DI and writing logs to a separate log file. I'm not sure how to dispose of only the logger for this scope, if you know exact steps, it would be much appreciated :) |
It's a bit roundabout, but setting https://github.com/serilog/serilog-sinks-map?tab=readme-ov-file#limiting-the-number-of-open-sinks and logging at least one non-robot-specific event after each run would do the job. |
we had another issue, marked as duplicate, adding here for context and to showcase more common and practical scenario where this is an issue. While app is running if the log file is recreated say from external logrotate built into linux or manually by hand eg the log file is recreated with same permission, serilog completely stops logging infinitely until full restart of application. |
@starkapandan if the rolling interval is infinite, the file should be reopened within 30 minutes; otherwise, it'll be reopened at the next roll point. Are you seeing different behavior? |
@nblumhardt gonna try it out and see if it reopens in 30 mins |
@nblumhardt recreated the file with same permission and same name, waited around 6+ hours, serilog does not manage to log anything anymore (System: Linux Ubuntu 24.04, Runtime: Asp Net Core 9). A full restart of the asp.net core application, and the logging starts working again |
saw a mentioning with serilog file sink having option "shared", the documentation was not very detailed but sounds like it tries to append instead of having always an open filehandle, which could potentially fix the solution, but this is mainly a theory. Could we get a little more info for this? An exclusive file handle that reopens in x interval sounds like it would be quite more effecient than "reopening" the file handle for every write? is this the case or does it work in some other way in the background? |
@starkapandan my best guess is that it's a file ownership problem or similar; checking out the |
@nblumhardt just checked the source, it was small indeed. seems filesink.cs opens one permanent filehandle/stream, which would explain why this issue occurs when the log file is recreated, it tries to write to a non existing file handle. for filesink, would it be possible to add some form of simple check before logging to see if the opened file stream is stil valid/usable? if not then just reopen it again with same filename basically? |
@starkapandan in There's an active PR in the repository that we'll merge soon which tweaks this a little. If there's a problem in there somewhere it'd be good to know. Worth checking |
Ah, sorry, now I understand why this isn't kicking in; we'll only use |
@nblumhardt Sounds like RollingFileSink class has some more nice additions sprinkled on top of it. On linux servers (cross platform usage of asp net core) that relies on built in system logrotate for standardization across different frameworks/programs is it possible to also get this features into the standard FileSink.cs? It would be nice to just use serilog for logging to the file and let the system handle the log rotation (this is where the 30 min log interval reopening would be very useful here) to avoid the scenario of it stopping logging infinitely. Otherwise as i understand when using serilog even if we dont need the rotating functionality of RotatingFileSink we would need to still use that class specifically just to get the feature where it reopens the file handle? Which i guess could be used as a work around but would be nice to avoid needing this work around hopefully. In general sounds like a nice functionality for regular FileSink aswell? Eg when logging check previous datetime of last opening, if more than 30 mins passed just reopening of the same filestream again would mitigate this issue fully (potentially missing 30 mins of logging but thats not to big of an issue to be fair, maybe if this is to basic a little more advanced setting to control the interval of reopening of the filestream could later be introduced, so for some users where logging is crucial they could set this time much lower of 5 min or even 0 which would always open a new filestream and guaranteed always make logging work) Not sure if dotnet core 9 supports this out of the box but the obvious superior approach would be if it was possible to check if simply the active filestream is valid, and if its not then reopen the filestream, but here i have no clue if dotnet core 9 actually has such a functionality available? did a quick experimenting, in c# (windows). opened a new filestream, wrote to it, then deleted the file and recreated the file. The filestream in c#, when trying to write it looks like it writes successfully, eg no exceptions thrown and property CanWrite is still true, but nothing is ever actually written out to the file system. Could not find a property or something similar to detect that the filestream was invalid and that we were in reality writing to thin air. So seems like the solution of reopening the file in x minute interval might be the proper approach to be implemented? |
short update, seems that this issue is rather straightforward to implement with a custom sink that implements retry interval (one single class does it), where it reopens the stream after x amount of minutes. Which solves this threads issue, not as nice or streamlined as a real integrated file sink, but quickly sketched out something like this and tried it, solves the issue at hand, if wished feel free to take parts of it into the repository as a separate filesink type or integrate it into existing. If anyone is in a hurry to have this type of feature then feel free to just paste this one class in, and refactor the startup code to instead use then appsettings: and lastly the modded sink class itself: using System;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Extensions.Configuration;
using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting;
using Serilog.Formatting.Json;
namespace Serilog.Sinks.File;
/**
* ---------- custom serilog file sink -------------------
* Fixes issue with serilog, when the file is externally recreated such as from logrotate etc
* reopens filestream every 30 minutes to ensure we recover back a valid filestream and continuing to write to it
*/
/// <summary>
/// Write log events to a disk file.
/// </summary>
public sealed class _Serilog_Custom_FileSink : IDisposable, ILogEventSink
{
TextWriter? _output;
readonly ITextFormatter _textFormatter;
readonly object _syncRoot = new();
readonly string path;
DateTime _streamCreationTime = DateTime.Now;
TimeSpan _reopenInterval = TimeSpan.FromMinutes(30);
public _Serilog_Custom_FileSink(IConfiguration configuration)
{
_textFormatter = new JsonFormatter();
this.path = configuration.GetSection("Serilog:WriteTo")
.GetChildren()
.First(x => x["Name"] == "_Custom_FileSink")?
.GetSection("Args")["path"]!;
if (this.path == null) throw new ArgumentNullException(nameof(path));
var directory = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
OpenStream();
}
/// <summary>
/// Emit the provided log event to the sink.
/// </summary>
/// <param name="logEvent">The log event to write.</param>
/// <exception cref="ArgumentNullException">When <paramref name="logEvent"/> is <code>null</code></exception>
public void Emit(LogEvent logEvent)
{
lock (_syncRoot)
{
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
if ((DateTime.Now - _streamCreationTime) > _reopenInterval)
{
_streamCreationTime = DateTime.Now;
this.Dispose();
this.OpenStream();
}
if (_output == null)
return;
_textFormatter.Format(logEvent, _output);
_output.Flush();
}
}
public void Dispose()
{
if (_output == null)
return;
_output.Dispose();
_output = null;
}
private void OpenStream()
{
try
{
Stream outputStream = System.IO.File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
outputStream.Seek(0, SeekOrigin.End);
// Parameter reassignment.
var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
_output = new StreamWriter(outputStream, encoding);
}
catch (Exception)
{
_output = null;
//failed opening stream atm...
}
}
} |
I use sinks-file on windows system, when web app runing, log write to file, now I want to delete the log file at runtime, windows report:
File is in use
The operation could not be completed because the file was opened in dotnet.exe
I use shared parameter, set true, is not working.
my code :
The text was updated successfully, but these errors were encountered: