NLog - Cleaning up log files that have dynamic file names - c#

I have an application that will be receiving messages through a queuing system, I would like to log each message to its own file, with the file name being the message id. I figured out how to accomplish this using the event-context within the filename.
Though the maxArchiveFiles setting does not have any affect, probably because I'm not archiving any files. Using this configuration is there any way I can leverage NLog to limit the number of files either by date or count?
<target name="testfile" xsi:type="File"
layout="${message}"
fileName="c:\SupportLogs\${event-context:item=MessageId}.txt"
maxArchiveFiles="50"
keepFileOpen="false"
encoding="iso-8859-2" />
NLog.Logger oLogger = NLog.LogManager.GetLogger("Test");
NLog.LogEventInfo oEvent = new NLog.LogEventInfo(NLog.LogLevel.Debug, "", "My Message");
oEvent.Properties["MessageId"] = Guid.NewGuid().ToString();
oLogger.Log(oEvent);

Unfortunately this is not possible in the NLog at the moment. You have to clean up the log files yourself.

Related

Writing each json message to unique file with NLog

I want to log each json message received from the network into a unique file.
At first I thought to do File.WriteAllText($"{Guid.NewGuid()}.json", jsonMsg);. But, I need to be able to archive and delete old log files. So, I have to watch the folder when the application starts up and do all the things that NLog already knows how to do.
With NLog, I can create and add a new Target for each message from code. But I'm not sure how to remove those targets after the message has been written. Otherwise, it will create memory leak.
So, should I just stick with my first idea and just implement the logic to archive and delete files, or is there a way to do this with NLog?
I recommend that you use a single filetarget for doing the writing, but avoid using MaxArchiveFiles for archive-cleanup:
var myguid = Guid.NewGuid();
var logEvent = NLog.LogEventInfo.Create(NLog.LogLevel.Info, null, jsonMsg);
logEvent.Properties["msguid"] = myguid;
logger.Log(logEvent);
And then use ${event-properties} in the FileName-option:
<target type="file" name="fileNetworkMessages"
fileName="messages/Archive/Output-${event-properties:myguid}.json"
layout="${message}" keepFileOpen="false" />
When including Guid in the filename, then you should avoid using MaxArchiveFiles, because it will introduce a performance-hit for every new file created, and cleanup will not work (HEX-letters will disturb file-wildcard).
NLog FileTarget MaxArchiveFiles has an overhead when rolling to a new file, where it scans all existing files to see if cleanup is necessary. This works fine when only rolling once every hour/day. But when using Guid in the filename, then NLog FileTarget will trigger a cleanup-check for every new file created. This will introduce a performance overhead.

NLog ReconfigExistingLoggers creating new log?

I'm using Nlog and I need to change the name of the default log to include information such as a company name. I've used this code a long time ago on an a console app and it renamed the file as expected.
I'm now trying to use the same code in a new app and it's creating a new log file instead of just renaming the current one. For example, I now have two files (2019-10.07.log and 2019-10-07_CompanyName.log). The default log will have few initial log entries and then it the remainder of the logs go into the new one.
Looking for any suggestions. I've been searching for fixes but everything points me back to the code I'm already using.
NLog v4.6.7
fileNameOnly = "CompanyName";
FileTarget defaultTarget = FindNLogTargetByName("DefaultTarget");
defaultTarget.FileName = logDirectory + string.Format("{0:yyyy-MM-dd}", DateTime.Now) + "_" + fileNameOnly + ".log";
LogManager.ReconfigExistingLoggers();
NLog doesn't support renaming an existing file. If a new file name is used, all the logs will be appended to the new file.
So for the file name you need to use System.IO.File.Move(path, pathnew) and change NLog.
Unfortunately it's a bit tricky when doing high volume logging, as NLog will recreate the old log file until the target is changed.
NLog can load settings (like Company name) from app.config or appsettings.json.
Just update your NLog.config to reference the setting. Ex.
<target type="file" name="myfile" fileName="${appsetting:CompanyName}${shortdate}.log" />
See also: https://github.com/NLog/NLog/wiki/AppSetting-Layout-Renderer (Net Framework)
See also: https://github.com/NLog/NLog/wiki/ConfigSetting-Layout-Renderer (Net Core)

Unittesting Nlog's archive feature

In my project I have created a logging system which is basically a shell on top of nLog. I am trying to unittest the archive feature of the logging system. It is currently setup to do rolling archives with a max archive files of 5 (Nlog is setup via code, no configuration file is used):
var myFileTarget = new FileTarget();
LogConfig.AddTarget("file", myFileTarget);
myFileTarget.FileName = LogFile;
myFileTarget.Layout = LogFileLayout;
myFileTarget.AutoFlush = true;
//Archive specifics
var token = "{#}";
var archiveFileName = $"{Path.GetFileNameWithoutExtension(LogFile)}.{token}.{Path.GetExtension(LogFile)}";
myFileTarget.ArchiveFileName = archiveFileName;
myFileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling;
myFileTarget.ArchiveEvery = toFileArchivePeriod(LogStyle);
myFileTarget.EnableFileDelete = true;
myFileTarget.MaxArchiveFiles = TTL; //Time to Live
myFileTarget.DeleteOldFileOnStartup = true;
To simulate that a lot of logs already exists, I create a range of logs with the same structure as the ArchiveFileName above:
LogFileName = "LogTTLDailyTest.log";
LogStyle = "daily";
LogTTL = 5; //Time To Live
//Arrange old filelogs
for (int i = 0; i < 100; i++)
{
var filename = $"LogTTLDailyTest.{i}.log";
File.WriteAllText(TestLogsDirectory + filename, "UNITTEST");
var creationTime = DateTime.Now.AddDays((i + 1)* -1);
File.SetCreationTime(TestLogsDirectory + filename, creationTime);
File.SetLastWriteTime(TestLogsDirectory + filename, creationTime);
}
But when I write a log to nlog via my Log system it does not see the old log files I created and therefore do not delete them. It does however clean up the old current log file, so deleting files work:
NLog: 2017-07-20 12:54:20.7434 Info Closing old configuration.
NLog: 2017-07-20 12:54:20.7434 Info Found 36 configuration items
NLog: 2017-07-20 12:54:20.7594 Info Found 36 configuration items
NLog: 2017-07-20 12:54:26.9157 Info Deleting old archive file: 'C:\<projectpath>\bin\Debug\unittestlogsea984b05-3c33-4142-9d1a-c900bad89006\LogTTLDailyTest.log'.
My current theory is that the nlog sees the old logs but have some kind of validation process of the contents of the files which I only fill up with "UNITTEST" as content, but I haven't been able to "restart" nlog or force it to see the logs.
Hope you can help me
This is no issue here, just me not setting the test up right. What I forgot is that rolling does not delete, but merely rename all files to the next rolling number. The last file in this rolling manner is deleted. Any other file is ignored by nlog. I also forgot to set my current log file a day back. This meant that nlog saw I had a current file that was not a day old. But because i had DeleteOldFileOnStartup activated it deleted that file.
So, to fix my mistake I made sure I only create as many files as I needed or less and made sure the latest file did not have a rolling number and was a day old. I also removed the DeleteOldFileOnStartup option. The Nlog is now doing what I expect it to do flawlessly.

Log4Net: How to handle concurrents write to the same file?

We have a big solution composed of several application. One of the application is ran very regularly( every 10minutes), and sometimes if the computer is busy, two executions may ran in parallel. (The question is not about if it's a good idea or not).
The only issue we currently have is that sometimes, the two overlapping process are both having a ILogger for the same file, we have an error from log4net, indicating that it cannot access to the file(file already used by another process or something like that).
Here is how we have the log configured:
RollingFileAppender appender = new RollingFileAppender
{
Name = appenderName,
File = fileName,
AppendToFile = true,
MaxSizeRollBackups = 10,
MaximumFileSize = "10MB",
RollingStyle = RollingFileAppender.RollingMode.Size,
StaticLogFileName = false,
LockingModel = new FileAppender.MinimalLock(),
ImmediateFlush = true
};
What would be the best way to handle this issue? We cannot have one file per execution.
EDIT
Here is the error I get:
log4net:ERROR [RollingFileAppender] Unable to acquire lock on file XXXX. The process cannot acces the file because it is being used by another process.
When you have multiple files writing to the same file, you can use the FileAppender.InterProcessLock model. This will lock and unlock the file based on a Mutex instead of trying to fix it with a minimal locking time.

How to make config files in Bin folder into non human-readable formats?(c#)

After I setup a project, there are some .config files in bin folder. I want to make them non human-readable formats.
1. I know I can use command such as asp net_ reg i i s -p e ..., but the project is a Win Form Project, and in my config file there is no parts like Web.config. My config file formats is like below:
<...>
<add key="..." value="...." />
<add key="..." value="..." />
</...>
I also tried to encrypt the config files, however there are so many places that call the config files. It seems too complex to do so.
So, is there some easy way to make my config files in bin folder into non human-readable formats using C#?
You have two options.. If you are interested in protecting some special parameter, e.g. a password, you can just encrypt it, and only have the encrypted value. If you really want to make the whole thing protected, you can use SectionInformation.ProtectSection.
MSDN Sample code for that:
static public void ProtectSection()
{
// Get the current configuration file.
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
// Get the section.
UrlsSection section =
(UrlsSection)config.GetSection("MyUrls");
// Protect (encrypt)the section.
section.SectionInformation.ProtectSection(
"RsaProtectedConfigurationProvider");
// Save the encrypted section.
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Full);
// Display decrypted configuration
// section. Note, the system
// uses the Rsa provider to decrypt
// the section transparently.
string sectionXml =
section.SectionInformation.GetRawXml();
Console.WriteLine("Decrypted section:");
Console.WriteLine(sectionXml);
}

Categories

Resources