Rolling Flat File Listener Keeps File Handle Open

Topics: Logging Application Block
Aug 22, 2010 at 4:24 PM
Edited Aug 22, 2010 at 4:26 PM

I've configured entlib 5 on my ASP.NET 3.5 website to have a "trace" category that routes to a rolling flat file listener with a daily roll interval. Tracing works fine, except...

When I've deployed my website on the production shared hosting server, I am unable to FTP the currently active logfile from that server to my development machine so
I can inspect what's going on because the rolling flat file listener has an exclusive lock on the file (or, if not exclusive, at least not a lock that will allow the FTP server to
get at it). When I run locally, I can open the logfile with Notepad but I can't open it in VS2010.

So to work around that, I put the code that gets a LogWriter instance in a using block so it would get disposed immediately like so:

                using (LogWriter writer = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>())
                {
                    writer.Write(logEntry);
                }

That works fine about 90% of the time, but I routinely get errors from the logging system (routed to me via an email listener) complaining that the file handle has already been disposed:

Exception Information Details:
======================================
Exception Type: System.ObjectDisposedException
Message: Cannot access a closed file.
ObjectName:
Data: System.Collections.ListDictionaryInternal
TargetSite: Void FileNotOpen()
HelpLink: NULL
Source: mscorlib

StackTrace Information Details:
======================================
at System.IO.__Error.FileNotOpen()
at System.IO.FileStream.Write(Byte[] array, Int32 offset, Int32 count)
at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
at System.IO.StreamWriter.Flush()
at System.Diagnostics.TextWriterTraceListener.Flush()
at Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.TraceListenerWrapper.Flush()
at Microsoft.Practices.EnterpriseLibrary.Logging.LogSource.TraceData(TraceEventType eventType, Int32 id, LogEntry logEntry, TraceListenerFilter traceListenerFilter, TraceEventCache traceEventCache)
at Microsoft.Practices.EnterpriseLibrary.Logging.LogWriterImpl.ProcessLog(LogEntry log, TraceEventCache traceEventCache)





So is there some way to configure the logging application block to automatically flush the log on every write *and* release the file handle?
I'm less concerned about the performance impact of opening and closing the file handle repeatedly than
about having immediate FTP access to the active tracelog file.







Don't know if it's relevant or not, but here's my <loggingConfiguration> section from web.config:

  <loggingConfiguration name="" tracingEnabled="true" defaultCategory="Trace">
    <listeners>
      <add name="Trace 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" fileName="log\TraceLog.txt" footer="" formatter="Text Formatter" header="" rollInterval="Day" maxArchivedFiles="21" traceOutputOptions="LogicalOperationStack, DateTime" />
      <add name="Email Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.EmailTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.EmailTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" toAddress="xxx@xxx.com" fromAddress="xxx@xx.com" subjectLineStarter="[ERROR]" smtpServer="smtp.xxx.com" smtpPort="587" formatter="Error Message Formatter" authenticationMode="UserNameAndPassword" userName="xxx@xxx.com" password="xxx" traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, Callstack" />
    </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(FixedFormatUSDate)} {timestamp(FixedFormatTime)}|{message}|{category}|{priority}|{severity}|Extended Properties: {dictionary({key} - {value} )}" name="Text Formatter" />
      <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({tab}{key} - {value}{newline})}" name="Error Message Formatter" />
    </formatters>
    <categorySources>
      <add switchValue="All" name="Trace">
        <listeners>
          <add name="Trace Rolling Flat File Trace Listener" />
        </listeners>
      </add>
      <add switchValue="Warning" name="Error">
        <listeners>
          <add name="Email Trace Listener" />
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events" />
      <notProcessed switchValue="All" name="Unprocessed Category">
        <listeners>
          <add name="Email Trace Listener" />
        </listeners>
      </notProcessed>
      <errors switchValue="All" name="Logging Errors &amp; Warnings">
        <listeners>
          <add name="Email Trace Listener" />
        </listeners>
      </errors>
    </specialSources>
  </loggingConfiguration>

Aug 23, 2010 at 2:15 AM

Will this work for you?

1. Remove the using statement, meaning not disposing the LogWriter object.

2. Copy the contents of the log file using this code:

FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader reader = new StreamReader(fs);
string log =  reader.ReadToEnd();

3. Write the contents of the file (log variable) to a new file where you wish to copy it.

I might've suggested to dispose the LogWriter instance in previous posts but I've just read Tom Hollander's advice not to dispose it unless you won't use it again.

 

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

Aug 27, 2010 at 1:46 AM

Thanks for the suggestion, but I don't think the solution is to make a copy of the log file every time a new event is written... I could just as easily write every event to a logfile of my own and skip the whole enterprise logging application block.

Upon further investigation, I don't think there's really an answer for this problem, as it's the System.Diagnostics.TextWriterTraceListener that opens the log file and hangs onto it, and it doesn't seem to offer any options for closing and reopening the file.

 

Aug 27, 2010 at 4:21 AM

I thought  you want to copy the log file at certain point of time while the application is still running., my mistake   If you only want to monitor the log file, you can use the SMS Trace to open it.  It allows you to view the contents of the file while it is being written to.

 

Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

Aug 28, 2010 at 12:37 AM
I guess I wasn't clear in my original post.

The log file is on a shared hosting server. The only access I have to the file is via FTP. I can't FTP the file because the LogWriter keeps an open handle.
Aug 31, 2010 at 1:55 PM

Hi,

I'm just curious if the problem happens in a single session or multi session of your web app? Honestly, I don't have any other idea what could probably solve your problem just had a thought if multi session/thread could probably be the cause.

Gino Terrado
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com