The solutions here did not really work for me. I took user3902302's answer, which again was based on bigtech's answer and wrote a complete class. Also, I am NOT using StreamWriter, you can change the one line (AppendAllText against the StreamWrite aequivalent).
There is little error handling (e. g. re-try access when it is failing, though the lock should catch all internal concurrent access).
This might be enough for some people who had to use a big solution like log4net or nlog before. (And log4net RollingAppender is not even thread-safe, this one is. :) )
public class RollingLogger
{
readonly static string LOG_FILE = @"c:\temp\logfile.log";
readonly static int MaxRolledLogCount = 3;
readonly static int MaxLogSize = 1024; // 1 * 1024 * 1024; <- small value for testing that it works, you can try yourself, and then use a reasonable size, like 1M-10M
public static void LogMessage(string msg)
{
lock (LOG_FILE) // lock is optional, but.. should this ever be called by multiple threads, it is safer
{
RollLogFile(LOG_FILE);
File.AppendAllText(LOG_FILE, msg + Environment.NewLine, Encoding.UTF8);
}
}
private static void RollLogFile(string logFilePath)
{
try
{
var length = new FileInfo(logFilePath).Length;
if (length > MaxLogSize)
{
var path = Path.GetDirectoryName(logFilePath);
var wildLogName = Path.GetFileNameWithoutExtension(logFilePath) + "*" + Path.GetExtension(logFilePath);
var bareLogFilePath = Path.Combine(path, Path.GetFileNameWithoutExtension(logFilePath));
string[] logFileList = Directory.GetFiles(path, wildLogName, SearchOption.TopDirectoryOnly);
if (logFileList.Length > 0)
{
// only take files like logfilename.log and logfilename.0.log, so there also can be a maximum of 10 additional rolled files (0..9)
var rolledLogFileList = logFileList.Where(fileName => fileName.Length == (logFilePath.Length + 2)).ToArray();
Array.Sort(rolledLogFileList, 0, rolledLogFileList.Length);
if (rolledLogFileList.Length >= MaxRolledLogCount)
{
File.Delete(rolledLogFileList[MaxRolledLogCount - 1]);
var list = rolledLogFileList.ToList();
list.RemoveAt(MaxRolledLogCount - 1);
rolledLogFileList = list.ToArray();
}
// move remaining rolled files
for (int i = rolledLogFileList.Length; i > 0; --i)
File.Move(rolledLogFileList[i - 1], bareLogFilePath + "." + i + Path.GetExtension(logFilePath));
var targetPath = bareLogFilePath + ".0" + Path.GetExtension(logFilePath);
// move original file
File.Move(logFilePath, targetPath);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
}
edit:
Since I just noticed that you asked a slightly different question: should your lines vary greatly in size, this would be a variation (, that in 90% of cases does not improve over yours, though, and might be very slightly faster, also introduced a new unhandled error (\n not being present)):
private static void PerformFileTrim(string filename)
{
var fileSize = (new System.IO.FileInfo(filename)).Length;
if (fileSize > 5000000)
{
var text = File.ReadAllText(filename);
var amountToCull = (int)(text.Length * 0.33);
amountToCull = text.IndexOf('\n', amountToCull);
var trimmedText = text.Substring(amountToCull + 1);
File.WriteAllText(filename, trimmedText);
}
}