14
votes

As the title implies how can I create a new log file each day in C#? Now the program may not necessarily run all day and night but only get invoked during business hours. So I need to do two things.

  1. How can I create a new log file each day? The log file will be have the name in a format like MMDDYYYY.txt
  2. How can I create it just after midnight in case it is running into all hours of the night?
10
Please take a look at stackoverflow.com/a/8255588/900284 It is also very nice.Frank Myat Thu
I'd upvote this if you selected an accepted answer.Joel

10 Answers

39
votes

Update 2018: I prefer to use NLog now

Previous answer about log4net:

This example shows how to configure the RollingFileAppender to roll log files on a date period. This example will roll the log file every minute! To change the rolling period adjust the DatePattern value. For example, a date pattern of "yyyyMMdd" will roll every day.
See System.Globalization.DateTimeFormatInfo for a list of available patterns.

<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="C:\temp\rolling.log" />
    <appendToFile value="true" />
    <rollingStyle value="Date" />
    <datePattern value="yyyyMMdd-HHmm" />
    <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
    </layout>
</appender>
9
votes

I'd recommend something like this:

string logFile = DateTime.Now.ToString("yyyyMMdd") + ".txt";
if (!System.IO.File.Exists(logFile))
{
    System.IO.File.Create(logFile);   
}
//append to logFile here...

Is there a reason you want something to create it after midnight? Why not just create it if it doesn't exist when you go to log the error?

Also noticed that I changed the date format. This will allow you to sort files by name and get them in order. I always use this format when messing with dates in any way.

8
votes

Others have mentioned Log4Net, so I'll go ahead and pimp the Enterprise Library Logging Block, which is also quite capable of doing what you want.

Could you please include some code that shows how easy it would be to make this roll every day? Is it easier than the log4Net example? – Daniel Dyson

Sure. Typically, one would use Enterprise Library Configuration Tool to build the configuration; this tool takes a little getting used to, but once you understand how it works, it's pretty powerful. That said, you can also edit the app.config by hand.

Here is the output of the tool I mentioned, which dumps pretty much everything into a rolling flat file that rolls every day (or if it exceeds 2MB). The formatting is the default provided by the tool.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
    </configSections>
    <loggingConfiguration name="" tracingEnabled="true" defaultCategory="Category">
        <listeners>
            <add name="Rolling Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                formatter="Text Formatter" rollInterval="Day" rollSizeKB="2000" />
        </listeners>
        <formatters>
            <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                template="Timestamp: {timestamp}{newline}&#xA;Message: {message}{newline}&#xA;Category: {category}{newline}&#xA;Priority: {priority}{newline}&#xA;EventId: {eventid}{newline}&#xA;Severity: {severity}{newline}&#xA;Title:{title}{newline}&#xA;Machine: {localMachine}{newline}&#xA;App Domain: {localAppDomain}{newline}&#xA;ProcessId: {localProcessId}{newline}&#xA;Process Name: {localProcessName}{newline}&#xA;Thread Name: {threadName}{newline}&#xA;Win32 ThreadId:{win32ThreadId}{newline}&#xA;Extended Properties: {dictionary({key} - {value}{newline})}"
                name="Text Formatter" />
        </formatters>
        <categorySources>
            <add switchValue="All" name="Category">
                <listeners>
                    <add name="Rolling Flat File Trace Listener" />
                </listeners>
            </add>
        </categorySources>
        <specialSources>
            <allEvents switchValue="All" name="All Events">
                <listeners>
                    <add name="Rolling Flat File Trace Listener" />
                </listeners>
            </allEvents>
            <notProcessed switchValue="All" name="Unprocessed Category">
                <listeners>
                    <add name="Rolling Flat File Trace Listener" />
                </listeners>
            </notProcessed>
            <errors switchValue="All" name="Logging Errors &amp; Warnings">
                <listeners>
                    <add name="Rolling Flat File Trace Listener" />
                </listeners>
            </errors>
        </specialSources>
    </loggingConfiguration>
</configuration>
6
votes

Try out NLog (nlog-project.org). It is very flexible and easier to work with than Log4Net in my opinion.

Example NLog.config:

<?xml version="1.0" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <targets>
        <target name="file" xsi:type="File"
            layout="${longdate} ${logger} ${message}" 
            fileName="${basedir}/${shortdate}/${windows-identity:domain=false}.${level}.log" />
    </targets>

    <rules>
        <logger name="*" minlevel="Debug" writeTo="file" />
    </rules>
</nlog>

For more examples (including other logging targets besides File) see NLog configuration examples on Github

3
votes

You don't need to create it at a particular time- in the simplest case you can just check if there is a logfile with today's date as it's name when you start the logging service for the app and if there isn't you can create one before you start appending to it.

The only case you need to be particularly aware of with this setup is when the application runs past midnight.

3
votes

use log4net. This is one of the most commonly used library for logging.

It can be easily configured as you like, please refer to samples.

3
votes

Below is the appender XML that I am currently using.

Based on your requirements of
1) creating a log file for once a day and,
2) to have the extension of txt,
you should use an XML similar to what is below.
The XML below will create a log file called system-20121106.txt.

The only issue is that since file value is logs/system- your file while it is writing for current day will be system-. To get around this, you'd have to set your file value to logs/system.txt, but then you'd get system.txt.20121106.txt as the final file.

<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="logs/system-" />
  <appendToFile value="true"/>
  <countDirection value="-1"/>
  <rollingStyle value="Date" />
  <datePattern value="yyyyMMdd'.txt'" />
  <maxSizeRollBackups value="0" />
  <maximumFileSize value="10000KB" />
  <staticLogFileName value="false" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%d [%t] %-5p %c - %m%n" />
  </layout>
</appender>
1
votes

When you log something, check to see if a file with the current date exists, if not - create it. Simple as that :)

if(fileExists(todaysDate + ".txt")){
  appendToLogFile(message);
}else{
  createFile(todaysDate + ".txt");
  appendToLogFile(message);
}
0
votes

There is no need to create it until you need it, use:

 file = new StreamWriter(path, true, new UTF8Encoding(false));

(or maybe a different encoding.) This will create the file is it does not exist, or start to append to it.

Then it is a matter of creating the filename, and just using that.

0
votes

If you only need a simple TraceListener, I have a mini implementation here: https://github.com/datvm/DailyTraceListener

The output is also in CSV format so you can read it with Excel or any CSV reader.

Source code for the TraceListener:

public class DailyTraceListener : TraceListener
{

    public bool UseUtcTime { get; private set; }
    public string LogFolder { get; private set; }
    public bool Disposed { get; private set; }
    public bool HasHeader { get; private set; }

    public string CurrentLogFilePath { get; private set; }

    protected DateTime? CurrentLogDate { get; set; }
    protected FileStream LogFileStream { get; set; }
    protected StreamWriter LogFileWriter { get; set; }

    private SemaphoreSlim LogLocker { get; set; } = new SemaphoreSlim(1, 1);

    public DailyTraceListener(string logFolder)
    {
        this.LogFolder = logFolder;
    }

    public DailyTraceListener UseUtc()
    {
        this.UseUtcTime = true;

        return this;
    }

    public DailyTraceListener UseHeader()
    {
        this.HasHeader = true;
        return this;
    }

    protected virtual void WriteHeader()
    {
        this.LogFileWriter.WriteLine(string.Format("{0},{1},{2},{3},{4}",
            "Time",
            "Type",
            "Source",
            "ID",
            "Message"));
    }

    protected virtual string FormatTime(DateTime time)
    {
        return time.ToString("o");
    }

    private DateTime GetCurrentTime()
    {
        if (this.UseUtcTime)
        {
            return DateTime.UtcNow;
        }
        else
        {
            return DateTime.Now;
        }
    }

    private void InitializeLogFile()
    {
        this.CheckDisposed();

        try
        {
            if (this.LogFileWriter != null)
            {
                this.LogFileWriter.Dispose();
            }

            if (this.LogFileStream != null)
            {
                this.LogFileWriter.Dispose();
            }
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
        }

        var currentTime = this.GetCurrentTime();

        var fileName = $"{currentTime.ToString("yyyy-MM-dd")}.log";
        this.CurrentLogFilePath = Path.Combine(this.LogFolder, fileName);

        // Ensure the folder is there
        Directory.CreateDirectory(this.LogFolder);

        // Create/Open log file
        this.LogFileStream = new FileStream(this.CurrentLogFilePath, FileMode.Append);
        this.LogFileWriter = new StreamWriter(this.LogFileStream);

        // Write Header if needed
        if (this.LogFileStream.Length == 0 && this.HasHeader)
        {
            this.WriteHeader();
        }
    }

    private void CheckFile()
    {
        this.CheckDisposed();

        var currentTime = this.GetCurrentTime();

        if (this.CurrentLogDate == null || currentTime.Date != this.CurrentLogDate)
        {
            this.InitializeLogFile();
            this.CurrentLogDate = currentTime.Date;
        }
    }

    private void CheckDisposed()
    {
        if (this.Disposed)
        {
            throw new InvalidOperationException("The Trace Listener is Disposed.");
        }
    }

    public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
    {
        var time = this.FormatTime(this.GetCurrentTime());
        this.WriteLine(string.Format("{0},{1},{2},{3},{4}",
            time,
            eventType,
            EscapeCsv(source),
            id.ToString(),
            EscapeCsv(message)));
    }

    public override void Write(string message)
    {
        try
        {
            this.LogLocker.Wait();

            this.CheckDisposed();
            this.CheckFile();

            var currentTime = this.GetCurrentTime();
            this.LogFileWriter.Write(message);
            this.LogFileWriter.Flush();
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
        }
        finally
        {
            this.LogLocker.Release();
        }
    }

    public override void WriteLine(string message)
    {
        this.Write(message + Environment.NewLine);
    }

    protected string EscapeCsv(string input)
    {
        for (int i = 0; i < input.Length; i++)
        {
            if (input[i] == ',' || input[i] == '\n')
            {
                input = input.Replace("\"", "\"\"");
                return $"\"{input}\"";
            }
        }

        return input;
    }

    protected override void Dispose(bool disposing)
    {
        this.Disposed = true;

        try
        {
            this.LogFileWriter?.Dispose();
            this.LogFileStream?.Dispose();
            this.LogLocker.Dispose();
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
        }

        base.Dispose(disposing);
    }

}