Create rolling flat file per thread

Topics: Logging Application Block
Oct 5, 2011 at 7:13 AM

Hi all,

I have a Windows Service with 2 thread, and i wanna get that:

Thread1_04-10-2011.log -> it saves informations, warnings, errors and trace messages.

Thread2_04-10-2011.log -> it saves informations, warnings, errors and trace messages.

How can i do it?

Oct 5, 2011 at 7:51 AM

Do you want to get:

<ThreadName>_<Date>.log

OR

<ThreadName>.log?

If it's the former, append the timestamp token to the thread name token and provide your valid separator in the message formatter; if it's the latter, simply use the timestamp token.

Oct 5, 2011 at 8:59 AM
Edited Oct 5, 2011 at 8:59 AM

I want to get: <ThreadName>_<Date>.log

I don't want one log containing all the messages. I want one log per thread, so i need to asign the thread name some where, but i don't know where.

Oct 5, 2011 at 9:43 AM

Have you configured the message log formatter to do this? There is a {threadname} token there (see my above post). Then, use the underscore as your separator, and then apply the {timestamp} token.

Oct 5, 2011 at 9:50 AM

Hi,

You can implement a custom trace listener that will check the current thread's name and appends it to the file name. For more details regarding custom trace listener, you can refer here.

 

Noel Angelo Bolasoc
Avanade Software and Cloud Services
Avanade, Inc.
Contact 

 

 

Oct 5, 2011 at 10:11 AM

I found a posible solution here: http://stackoverflow.com/questions/7144063/write-to-multiple-files-enterprise-library-logger

But i don't know how to resolve tokens.

For example,  my log name: {threadName}_{timestamp}.log and then it converts to Thread1_10-05-2011.log

it is possible?

Oct 5, 2011 at 10:45 AM

"But i don't know how to resolve tokens."

Are you using the Enterprise Library 5 Configuration tool? You can easily "resolve" tokens using that.

Oct 5, 2011 at 11:13 AM

This may be possible but still, it will require you to have a custom trace listener (since this functionality is not supported out of the box). Also note that you will also have to interpret those tokens which requires you additional coding. The easiest way I can think of is again, by using a custom trace listener and in the implementation of TraceData method, create your text file and append the threadname and timestamp there something like:

      using (System.IO.FileStream fs = System.IO.File.Create(string.Format("{0}_{1}{2}",System.Threading.Thread.CurrentThread.Name,System.DateTime.Now.ToShortDateString(),fileName)))
            {
                    fs.Write(message);
            }

I hope you get the idea :)

 

Noel Angelo Bolasoc
Avanade Software and Cloud Services
Avanade, Inc.
Contact 

Oct 5, 2011 at 11:53 AM

Thanks AvanadeSupport, it's a good idea.

CodeTrainer, yes i'm using it. How can i do it?

well, when i mean to resolve, i mean to write on config file: {Thread Name}_{Timestamp}.log and get Thread1_10-05-2011.log

Oct 6, 2011 at 10:03 AM
Edited Oct 6, 2011 at 10:07 AM

I have a little problem. I have created a CustomTraceListener inherit of RollingFlatFileTraceListener because I want to keep the functionality to create an log archive per day.

To change the file name I have done the following:

/// <summary>
/// Returns a lambda expression that represents the creation of the trace listener described by this
/// configuration object.
/// </summary>
/// <returns>A lambda expression to create a trace listener.</returns>
protected override Expression<Func<TraceListener>> GetCreationExpression()
{
    // Resolve tokens in FileName                       
    string extension = ".log";
    string folder = System.IO.Path.GetDirectoryName(this.FileName);   

    string fileName = string.Format(@"{0}\{1}_{2}{3}",folder ,                                                                                                       
                                                Thread.CurrentThread.Name,
                                                DateTime.Now.ToString(TimeStampPattern), extension);
    return
        () =>
            new RollingFlatFileTraceListener(               
                fileName,
                this.Header,
                this.Footer,
                Container.ResolvedIfNotNull<ILogFormatter>(this.Formatter),
                this.RollSizeKB,
                this.TimeStampPattern,
                this.RollFileExistsBehavior,
                this.RollInterval,
                this.MaxArchivedFiles);   
}

I am running a TestMethod to verify that everything works properly.

[TestMethod]
public void GetLogFilePerThread()
{
    Thread thdCompresion;
    Thread thdEnvioSFTP;

    TestClass obj = new TestClass();           
    thdCompresion = new Thread(new ThreadStart(delegate() { obj.MetodoCompresion(); }));
    thdCompresion.Name = "CompressionZIP";
    thdCompresion.Priority = System.Threading.ThreadPriority.Normal;

    thdEnvioSFTP = new Thread(new ThreadStart(delegate() { obj.MetodoEnvioSFTP(); }));
    thdEnvioSFTP.Name = "SendSFTP";
    thdEnvioSFTP.Priority = System.Threading.ThreadPriority.Normal;

    thdCompresion.Start();
    thdEnvioSFTP.Start();

    thdCompresion.Join();
    thdEnvioSFTP.Join();
}

The method MetodoCompresion () is as follows:

public void MetodoCompresion()
{
    Single i=0;
    while (i < 500)
    {
        log = LoggerFactory.CreateLog();
        log.LogTraceMessages("{0}", "I'm tracing on Zip");
        i++;
    }
}

The method MetodoEnvioSFTP() is similar.

This works fine except that I only creates one log file and not two as it should be, since I have 2 threads.

The option of override the TraceData could be good, but I want to keep RollingFlatFileTraceListener functionality.

How I can keep RollingFlatFileTraceListener functionality and write in two log files?
Is there something wrong on my approach?

Oct 6, 2011 at 10:49 AM
Edited Oct 6, 2011 at 10:57 AM

Hi,

We can't really say base on the code above. Maybe it would be better if you can send us your sample solution so we can have a better view of your approach. You can drop us an email at entlib.support@avanade.com

 

Noel Angelo Bolasoc
Avanade Software and Cloud Services
Avanade, Inc.
Contact 

 

Oct 9, 2011 at 10:43 PM

Hi Adri,

The issue on your approach is with the static facade, Logger.Writer method. The issue is in this specific method (of Logger class):

 

 public static LogWriter Writer
        {
            get
            {
                if (writer == null)
                {
                    lock (sync)
                    {
                        if (writer == null)
                        {
                            try
                            {
                                writer = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
                            }
                            catch (ActivationException configurationException)
                            {
                                TryLogConfigurationFailure(configurationException);

                                throw;
                            }
                        }
                    }
                }
                return writer;
            }
        }

 

Note that when the LogWriter is resolved, the trace listener is also initialized and the expression function you have overriden will be evaluated. Thus, the current thread that acquired the lock from the code above will have the thread name appended to the file name. The next thread that will invoke the Writer method will check if the writer == null, but since it has been already been initialized by the previous thread, it will just only return the initialized writer. 

If you are only using two threads in your application, then I guess the easiest way is to create different rolling trace listener for each thread.

 

Noel Angelo Bolasoc
Avanade Software and Cloud Services
Avanade, Inc.
Contact 

Oct 10, 2011 at 10:17 AM

Ok, i've understood.. I'm going to try to develop it with other implementation to make it more generic but maintaining the philosophy.

Thanks.

PD: My apologies for my english. It is not very good.

Jun 28, 2012 at 3:59 AM

I would also like to create a rolling flat file per process ID?  I would like to create a custom trace listener that inherits from RollingFlatFileTraceListener.  I was thinking that I could manipulate the fileName within the constructor.  I have not created a custom trace listener before and I am not sure if this will work.  There is a high possibility that multiple processes will be running and we don't want it all to go to one log.  Have multiple logs with the process id appended to it would resolve the issue of the log files being locked if another process tries to write to the same file.  Can you provide me with code that can help me get started?

Here is the code that I have so far, but I noticed that I don't seem to get to this custom trace listener using the Entlib Configuration tool.

 

[ConfigurationElementType(typeof(RollingFlatFileTraceListenerData))]
    public class CustomRollingFlatFileTraceListener : Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener //TODO: Inherit from CustomTraceListener class
    {
        public CustomRollingFlatFileTraceListener(string fileName)
            : base(fileName, null, null, null, 0, null, RollFileExistsBehavior.Increment, RollInterval.None)
        {
            string processId = Process.GetCurrentProcess().Id.ToString();
            fileName = processId + "_" + fileName;
        }
}

Jun 29, 2012 at 6:27 AM

The posted code has a few areas to look at: 

  • The ConfigurationElementType shouldn't be RollingFlatFileTraceListenerData since that configuration class will create a RollingFlatFileTraceListener.  You will need to duplicate the code in RollingFlatFileTraceListenerData in your own CustomRollingFlatFileTraceListenerData class and change the GetCreationExpression method to return the custom trace listener:
        protected override Expression<Func<TraceListener>> GetCreationExpression()
        {
            return
                () =>
                    new CustomRollingFlatFileTraceListener(
                        this.FileName,
                        this.Header,
                        this.Footer,
                        Container.ResolvedIfNotNull<ILogFormatter>(this.Formatter),
                        this.RollSizeKB,
                        this.TimeStampPattern,
                        this.RollFileExistsBehavior,
                        this.RollInterval,
                        this.MaxArchivedFiles);
        }
  • The fileName is passed to the base class constructor; file locking occurs in a base class so you would still have locking issues.  You should use : base( Process.GetCurrentProcess().Id.ToString() + "_" + fileName, ...) instead.
  • You can definitely extend RollingFlatFileTraceListener and not use CustomTraceListener

I would recommend looking at the Extensibility Hands on Labs and/or looking at the Rolling XML Trace Listener Sample on the Samples Page for examples with full integration.  Also remember to either deploy your trace listener to the same directory that the configuration tool is running out of so that the configuration tool can locate your custom type.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Jun 29, 2012 at 3:52 PM

Can I use the existing RollingFlatFileTraceListener that comes with Entlib 5.0 and just create a custom RollingFlatFileTraceListenerData class and override the GetCreateExpression method to have a file name that contains the process ID?

Linh Ly | Software Engineer | Cloud Engineering

Unisys | 9701 Jeronimo Road | Irvine CA, 92618 | Net2 656-5334 | 949-380-5334


THIS COMMUNICATION MAY CONTAIN CONFIDENTIAL AND/OR OTHERWISE PROPRIETARY MATERIAL and is thus for use only by the intended recipient. If you received this in error, please contact the sender and delete the e-mail and its attachments from all computers.

Jun 29, 2012 at 4:14 PM
Edited Jun 29, 2012 at 4:16 PM

Yes, you can do that.  That's an easy solution.  The upside is that it's quite simple to do but the downside is that it won't integrate with the configuration tool.  So, you will have to manually enter the information into the configuration file.  Also, in order to open the configuration file you will have to deploy the new configuration Data assembly (as you would anyway have to do for a custom trace listener).  However, since you did not create a custom trace listener there will be no "Rolling processID trace listener" appearing in the configuration tool in the list of potential trace listeners.  

It depends on how much config tool integration is important to you.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Jul 27, 2012 at 11:53 PM

Hi Randy,

I got the rolling flat file to generate for multiple process ID at one time, but now when I run my program I seem to be getting a very strange result. I will get one rolling file that will just get overwritten when the size it reached. I use to get multiple rolling flat files, but now things don’t seem to be working. I am not sure what is wrong and where to look to fix the problem.

Here is my inherited RollingFlatFIleTraceListenerData that modifies the GetCreationExpression.

class Clearpath_RollingFlatFileTraceListenerData : Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData

{

public Clearpath_RollingFlatFileTraceListenerData()

{

}

/// <summary>

/// Returns a lambda expression that represents the creation of the trace listener described by this

/// configuration object.

/// </summary>

/// <returns>A lambda expression to create a trace listener.</returns>

protected override Expression<Func<TraceListener>> GetCreationExpression()

{

string processId = Process.GetCurrentProcess().Id.ToString();

string logDirectoryPath = Path.GetDirectoryName(base.FileName);

string logfileName = logDirectoryPath + "\\" + processId + "\\" + Path.GetFileNameWithoutExtension(base.FileName) + "_" + DateTime.Now.ToString("(yyyy-MM-dd.hh)") + "_"+ processId + Path.GetExtension(base.FileName);

return

() =>

new RollingFlatFileTraceListener(

logfileName,

this.Header,

this.Footer,

Container.ResolvedIfNotNull<ILogFormatter>(this.Formatter),

this.RollSizeKB,

this.TimeStampPattern,

this.RollFileExistsBehavior,

this.RollInterval,

this.MaxArchivedFiles);

}

}

This is what I have in my configuration file for the listener.

<listeners>

<add name="Unisys ClearPathInterface TraceFile Listeners" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

listenerDataType="Unisys.ClearPath.Interface.Clearpath_RollingFlatFileTraceListenerData, ClearPathInterface"

fileName="Unisys\ClearPath Interface\logs\ClearpathInterface.log"

footer="" formatter="Unisys ClearPathInterface Trace Log Text Formatter"

header="" rollFileExistsBehavior="Increment" rollSizeKB="2048"

timeStampPattern="yyyy-MM-dd" traceOutputOptions="None" filter="All" />

<add name="Unisys ClearPathInterface Event Log Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

source="Unisys ClearPath Interface" log="Unisys ClearPath Interface"

traceOutputOptions="None" filter="Warning" />

</listeners>

If you can please help me figure out what I am doing wrong. I know this was working before, but now I can’t seem to get the rolling file to work correctly.

Linh Ly | Software Engineer | Cloud Engineering

Unisys | 9701 Jeronimo Road | Irvine CA, 92618 | Net2 656-5334 | 949-380-5334


THIS COMMUNICATION MAY CONTAIN CONFIDENTIAL AND/OR OTHERWISE PROPRIETARY MATERIAL and is thus for use only by the intended recipient. If you received this in error, please contact the sender and delete the e-mail and its attachments from all computers.

Jul 28, 2012 at 3:58 AM

It looks like you are running up against a known issue.  Your filename has parentheses which causes the pattern matching for calculating archive numbers to always return 0 so only one archive filename is generated and always overwritten.  If you remove the parentheses then you should be fine.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Jul 28, 2012 at 4:33 PM

Randy,

Thanks for the quick response and solution to my problem.

Linh Ly | Software Engineer | Cloud Engineering

Unisys | 9701 Jeronimo Road | Irvine CA, 92618 | Net2 656-5334 | 949-380-5334


THIS COMMUNICATION MAY CONTAIN CONFIDENTIAL AND/OR OTHERWISE PROPRIETARY MATERIAL and is thus for use only by the intended recipient. If you received this in error, please contact the sender and delete the e-mail and its attachments from all computers.