#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion
using System;
using System.IO;
#if !NETCF && !NETSTANDARD1_3
using System.Runtime.Serialization;
#endif
using System.Text;
using System.Threading;
using log4net.Util;
using log4net.Layout;
using log4net.Core;
#if NET_4_5 || NETSTANDARD
using System.Threading.Tasks;
#endif
namespace log4net.Appender
{
#if !NETCF
///
/// Appends logging events to a file.
///
///
///
/// Logging events are sent to the file specified by
/// the property.
///
///
/// The file can be opened in either append or overwrite mode
/// by specifying the property.
/// If the file path is relative it is taken as relative from
/// the application base directory. The file encoding can be
/// specified by setting the property.
///
///
/// The layout's and
/// values will be written each time the file is opened and closed
/// respectively. If the property is
/// then the file may contain multiple copies of the header and footer.
///
///
/// This appender will first try to open the file for writing when
/// is called. This will typically be during configuration.
/// If the file cannot be opened for writing the appender will attempt
/// to open the file again each time a message is logged to the appender.
/// If the file cannot be opened for writing when a message is logged then
/// the message will be discarded by this appender.
///
///
/// The supports pluggable file locking models via
/// the property.
/// The default behavior, implemented by
/// is to obtain an exclusive write lock on the file until this appender is closed.
/// The alternative models only hold a
/// write lock while the appender is writing a logging event ()
/// or synchronize by using a named system wide Mutex ().
///
///
/// All locking strategies have issues and you should seriously consider using a different strategy that
/// avoids having multiple processes logging to the same file.
///
///
/// Nicko Cadell
/// Gert Driesen
/// Rodrigo B. de Oliveira
/// Douglas de la Torre
/// Niall Daley
#else
///
/// Appends logging events to a file.
///
///
///
/// Logging events are sent to the file specified by
/// the property.
///
///
/// The file can be opened in either append or overwrite mode
/// by specifying the property.
/// If the file path is relative it is taken as relative from
/// the application base directory. The file encoding can be
/// specified by setting the property.
///
///
/// The layout's and
/// values will be written each time the file is opened and closed
/// respectively. If the property is
/// then the file may contain multiple copies of the header and footer.
///
///
/// This appender will first try to open the file for writing when
/// is called. This will typically be during configuration.
/// If the file cannot be opened for writing the appender will attempt
/// to open the file again each time a message is logged to the appender.
/// If the file cannot be opened for writing when a message is logged then
/// the message will be discarded by this appender.
///
///
/// The supports pluggable file locking models via
/// the property.
/// The default behavior, implemented by
/// is to obtain an exclusive write lock on the file until this appender is closed.
/// The alternative model only holds a
/// write lock while the appender is writing a logging event ().
///
///
/// All locking strategies have issues and you should seriously consider using a different strategy that
/// avoids having multiple processes logging to the same file.
///
///
/// Nicko Cadell
/// Gert Driesen
/// Rodrigo B. de Oliveira
/// Douglas de la Torre
/// Niall Daley
#endif
public class FileAppender : TextWriterAppender
{
#region LockingStream Inner Class
///
/// Write only that uses the
/// to manage access to an underlying resource.
///
private sealed class LockingStream : Stream, IDisposable
{
#if !NETCR
[Serializable]
#endif
public sealed class LockStateException : LogException
{
public LockStateException(string message)
: base(message)
{
}
public LockStateException()
{
}
public LockStateException(string message, Exception innerException) : base(message, innerException)
{
}
#if !NETCR && !NETSTANDARD1_3
private LockStateException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endif
}
private Stream m_realStream = null;
private LockingModelBase m_lockingModel = null;
private int m_lockLevel = 0;
public LockingStream(LockingModelBase locking)
: base()
{
if (locking == null)
{
throw new ArgumentException("Locking model may not be null", "locking");
}
m_lockingModel = locking;
}
#region Override Implementation of Stream
#if NETSTANDARD
protected override void Dispose(bool disposing)
{
m_lockingModel.CloseFile();
base.Dispose(disposing);
}
#else
private int m_readTotal = -1;
// Methods
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
AssertLocked();
IAsyncResult ret = m_realStream.BeginRead(buffer, offset, count, callback, state);
m_readTotal = EndRead(ret);
return ret;
}
///
/// True asynchronous writes are not supported, the implementation forces a synchronous write.
///
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
AssertLocked();
IAsyncResult ret = m_realStream.BeginWrite(buffer, offset, count, callback, state);
EndWrite(ret);
return ret;
}
public override void Close()
{
m_lockingModel.CloseFile();
}
public override int EndRead(IAsyncResult asyncResult)
{
AssertLocked();
return m_readTotal;
}
public override void EndWrite(IAsyncResult asyncResult)
{
//No-op, it has already been handled
}
#endif
#if NET_4_5 || NETSTANDARD
public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
AssertLocked();
return m_realStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
AssertLocked();
return base.WriteAsync(buffer, offset, count, cancellationToken);
}
#endif
public override void Flush()
{
AssertLocked();
m_realStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return m_realStream.Read(buffer, offset, count);
}
public override int ReadByte()
{
return m_realStream.ReadByte();
}
public override long Seek(long offset, SeekOrigin origin)
{
AssertLocked();
return m_realStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
AssertLocked();
m_realStream.SetLength(value);
}
void IDisposable.Dispose()
{
#if NETSTANDARD
Dispose(true);
#else
Close();
#endif
}
public override void Write(byte[] buffer, int offset, int count)
{
AssertLocked();
m_realStream.Write(buffer, offset, count);
}
public override void WriteByte(byte value)
{
AssertLocked();
m_realStream.WriteByte(value);
}
// Properties
public override bool CanRead
{
get { return false; }
}
public override bool CanSeek
{
get
{
AssertLocked();
return m_realStream.CanSeek;
}
}
public override bool CanWrite
{
get
{
AssertLocked();
return m_realStream.CanWrite;
}
}
public override long Length
{
get
{
AssertLocked();
return m_realStream.Length;
}
}
public override long Position
{
get
{
AssertLocked();
return m_realStream.Position;
}
set
{
AssertLocked();
m_realStream.Position = value;
}
}
#endregion Override Implementation of Stream
#region Locking Methods
private void AssertLocked()
{
if (m_realStream == null)
{
throw new LockStateException("The file is not currently locked");
}
}
public bool AcquireLock()
{
bool ret = false;
lock (this)
{
if (m_lockLevel == 0)
{
// If lock is already acquired, nop
m_realStream = m_lockingModel.AcquireLock();
}
if (m_realStream != null)
{
m_lockLevel++;
ret = true;
}
}
return ret;
}
public void ReleaseLock()
{
lock (this)
{
m_lockLevel--;
if (m_lockLevel == 0)
{
// If already unlocked, nop
m_lockingModel.ReleaseLock();
m_realStream = null;
}
}
}
#endregion Locking Methods
}
#endregion LockingStream Inner Class
#region Locking Models
///
/// Locking model base class
///
///
///
/// Base class for the locking models available to the derived loggers.
///
///
public abstract class LockingModelBase
{
private FileAppender m_appender = null;
///
/// Open the output file
///
/// The filename to use
/// Whether to append to the file, or overwrite
/// The encoding to use
///
///
/// Open the file specified and prepare for logging.
/// No writes will be made until is called.
/// Must be called before any calls to ,
/// and .
///
///
public abstract void OpenFile(string filename, bool append, Encoding encoding);
///
/// Close the file
///
///
///
/// Close the file. No further writes will be made.
///
///
public abstract void CloseFile();
///
/// Initializes all resources used by this locking model.
///
public abstract void ActivateOptions();
///
/// Disposes all resources that were initialized by this locking model.
///
public abstract void OnClose();
///
/// Acquire the lock on the file
///
/// A stream that is ready to be written to.
///
///
/// Acquire the lock on the file in preparation for writing to it.
/// Return a stream pointing to the file.
/// must be called to release the lock on the output file.
///
///
public abstract Stream AcquireLock();
///
/// Release the lock on the file
///
///
///
/// Release the lock on the file. No further writes will be made to the
/// stream until is called again.
///
///
public abstract void ReleaseLock();
///
/// Gets or sets the for this LockingModel
///
///
/// The for this LockingModel
///
///
///
/// The file appender this locking model is attached to and working on
/// behalf of.
///
///
/// The file appender is used to locate the security context and the error handler to use.
///
///
/// The value of this property will be set before is
/// called.
///
///
public FileAppender CurrentAppender
{
get { return m_appender; }
set { m_appender = value; }
}
///
/// Helper method that creates a FileStream under CurrentAppender's SecurityContext.
///
///
///
/// Typically called during OpenFile or AcquireLock.
///
///
/// If the directory portion of the does not exist, it is created
/// via Directory.CreateDirecctory.
///
///
///
///
///
///
protected Stream CreateStream(string filename, bool append, FileShare fileShare)
{
using (CurrentAppender.SecurityContext.Impersonate(this))
{
// Ensure that the directory structure exists
string directoryFullName = Path.GetDirectoryName(filename);
// Only create the directory if it does not exist
// doing this check here resolves some permissions failures
if (!Directory.Exists(directoryFullName))
{
Directory.CreateDirectory(directoryFullName);
}
FileMode fileOpenMode = append ? FileMode.Append : FileMode.Create;
return new FileStream(filename, fileOpenMode, FileAccess.Write, fileShare);
}
}
///
/// Helper method to close under CurrentAppender's SecurityContext.
///
///
/// Does not set to null.
///
///
protected void CloseStream(Stream stream)
{
using (CurrentAppender.SecurityContext.Impersonate(this))
{
stream.Dispose();
}
}
}
///
/// Hold an exclusive lock on the output file
///
///
///
/// Open the file once for writing and hold it open until is called.
/// Maintains an exclusive lock on the file during this time.
///
///
public class ExclusiveLock : LockingModelBase
{
private Stream m_stream = null;
///
/// Open the file specified and prepare for logging.
///
/// The filename to use
/// Whether to append to the file, or overwrite
/// The encoding to use
///
///
/// Open the file specified and prepare for logging.
/// No writes will be made until is called.
/// Must be called before any calls to ,
/// and .
///
///
public override void OpenFile(string filename, bool append, Encoding encoding)
{
try
{
m_stream = CreateStream(filename, append, FileShare.Read);
}
catch (Exception e1)
{
CurrentAppender.ErrorHandler.Error("Unable to acquire lock on file " + filename + ". " + e1.Message);
}
}
///
/// Close the file
///
///
///
/// Close the file. No further writes will be made.
///
///
public override void CloseFile()
{
CloseStream(m_stream);
m_stream = null;
}
///
/// Acquire the lock on the file
///
/// A stream that is ready to be written to.
///
///
/// Does nothing. The lock is already taken
///
///
public override Stream AcquireLock()
{
return m_stream;
}
///
/// Release the lock on the file
///
///
///
/// Does nothing. The lock will be released when the file is closed.
///
///
public override void ReleaseLock()
{
//NOP
}
///
/// Initializes all resources used by this locking model.
///
public override void ActivateOptions()
{
//NOP
}
///
/// Disposes all resources that were initialized by this locking model.
///
public override void OnClose()
{
//NOP
}
}
///
/// Acquires the file lock for each write
///
///
///
/// Opens the file once for each / cycle,
/// thus holding the lock for the minimal amount of time. This method of locking
/// is considerably slower than but allows
/// other processes to move/delete the log file whilst logging continues.
///
///
public class MinimalLock : LockingModelBase
{
private string m_filename;
private bool m_append;
private Stream m_stream = null;
///
/// Prepares to open the file when the first message is logged.
///
/// The filename to use
/// Whether to append to the file, or overwrite
/// The encoding to use
///
///
/// Open the file specified and prepare for logging.
/// No writes will be made until is called.
/// Must be called before any calls to ,
/// and .
///
///
public override void OpenFile(string filename, bool append, Encoding encoding)
{
m_filename = filename;
m_append = append;
}
///
/// Close the file
///
///
///
/// Close the file. No further writes will be made.
///
///
public override void CloseFile()
{
// NOP
}
///
/// Acquire the lock on the file
///
/// A stream that is ready to be written to.
///
///
/// Acquire the lock on the file in preparation for writing to it.
/// Return a stream pointing to the file.
/// must be called to release the lock on the output file.
///
///
public override Stream AcquireLock()
{
if (m_stream == null)
{
try
{
m_stream = CreateStream(m_filename, m_append, FileShare.Read);
m_append = true;
}
catch (Exception e1)
{
CurrentAppender.ErrorHandler.Error("Unable to acquire lock on file " + m_filename + ". " + e1.Message);
}
}
return m_stream;
}
///
/// Release the lock on the file
///
///
///
/// Release the lock on the file. No further writes will be made to the
/// stream until is called again.
///
///
public override void ReleaseLock()
{
CloseStream(m_stream);
m_stream = null;
}
///
/// Initializes all resources used by this locking model.
///
public override void ActivateOptions()
{
//NOP
}
///
/// Disposes all resources that were initialized by this locking model.
///
public override void OnClose()
{
//NOP
}
}
#if !NETCF
///
/// Provides cross-process file locking.
///
/// Ron Grabowski
/// Steve Wranovsky
public class InterProcessLock : LockingModelBase
{
private Mutex m_mutex = null;
private Stream m_stream = null;
private int m_recursiveWatch = 0;
///
/// Open the file specified and prepare for logging.
///
/// The filename to use
/// Whether to append to the file, or overwrite
/// The encoding to use
///
///
/// Open the file specified and prepare for logging.
/// No writes will be made until is called.
/// Must be called before any calls to ,
/// - and .
///
///
#if NET_4_0 || MONO_4_0 || NETSTANDARD
[System.Security.SecuritySafeCritical]
#endif
public override void OpenFile(string filename, bool append, Encoding encoding)
{
try
{
m_stream = CreateStream(filename, append, FileShare.ReadWrite);
}
catch (Exception e1)
{
CurrentAppender.ErrorHandler.Error("Unable to acquire lock on file " + filename + ". " + e1.Message);
}
}
///
/// Close the file
///
///
///
/// Close the file. No further writes will be made.
///
///
public override void CloseFile()
{
try
{
CloseStream(m_stream);
m_stream = null;
}
finally
{
ReleaseLock();
}
}
///
/// Acquire the lock on the file
///
/// A stream that is ready to be written to.
///
///
/// Does nothing. The lock is already taken
///
///
public override Stream AcquireLock()
{
if (m_mutex != null)
{
// TODO: add timeout?
m_mutex.WaitOne();
// increment recursive watch
m_recursiveWatch++;
// should always be true (and fast) for FileStream
if (m_stream != null)
{
if (m_stream.CanSeek)
{
m_stream.Seek(0, SeekOrigin.End);
}
}
else
{
// this can happen when the file appender cannot open a file for writing
}
}
else
{
CurrentAppender.ErrorHandler.Error("Programming error, no mutex available to acquire lock! From here on things will be dangerous!");
}
return m_stream;
}
///
/// Releases the lock and allows others to acquire a lock.
///
public override void ReleaseLock()
{
if (m_mutex != null)
{
if (m_recursiveWatch > 0)
{
m_recursiveWatch--;
m_mutex.ReleaseMutex();
}
}
else
{
CurrentAppender.ErrorHandler.Error("Programming error, no mutex available to release the lock!");
}
}
///
/// Initializes all resources used by this locking model.
///
public override void ActivateOptions()
{
if (m_mutex == null)
{
string mutexFriendlyFilename = CurrentAppender.File
.Replace("\\", "_")
.Replace(":", "_")
.Replace("/", "_");
m_mutex = new Mutex(false, mutexFriendlyFilename);
}
else
{
CurrentAppender.ErrorHandler.Error("Programming error, mutex already initialized!");
}
}
///
/// Disposes all resources that were initialized by this locking model.
///
public override void OnClose()
{
if (m_mutex != null)
{
#if NET_4_0 || MONO_4_0 || NETSTANDARD
m_mutex.Dispose();
#else
m_mutex.Close();
#endif
m_mutex = null;
}
else
{
CurrentAppender.ErrorHandler.Error("Programming error, mutex not initialized!");
}
}
}
#endif
#endregion Locking Models
#region Public Instance Constructors
///
/// Default constructor
///
///
///
/// Default constructor
///
///
public FileAppender()
{
}
///
/// Construct a new appender using the layout, file and append mode.
///
/// the layout to use with this appender
/// the full path to the file to write to
/// flag to indicate if the file should be appended to
///
///
/// Obsolete constructor.
///
///
[Obsolete("Instead use the default constructor and set the Layout, File & AppendToFile properties")]
public FileAppender(ILayout layout, string filename, bool append)
{
Layout = layout;
File = filename;
AppendToFile = append;
ActivateOptions();
}
///
/// Construct a new appender using the layout and file specified.
/// The file will be appended to.
///
/// the layout to use with this appender
/// the full path to the file to write to
///
///
/// Obsolete constructor.
///
///
[Obsolete("Instead use the default constructor and set the Layout & File properties")]
public FileAppender(ILayout layout, string filename)
: this(layout, filename, true)
{
}
#endregion Public Instance Constructors
#region Public Instance Properties
///
/// Gets or sets the path to the file that logging will be written to.
///
///
/// The path to the file that logging will be written to.
///
///
///
/// If the path is relative it is taken as relative from
/// the application base directory.
///
///
public virtual string File
{
get { return m_fileName; }
set { m_fileName = value; }
}
///
/// Gets or sets a flag that indicates whether the file should be
/// appended to or overwritten.
///
///
/// Indicates whether the file should be appended to or overwritten.
///
///
///
/// If the value is set to false then the file will be overwritten, if
/// it is set to true then the file will be appended to.
///
/// The default value is true.
///
public bool AppendToFile
{
get { return m_appendToFile; }
set { m_appendToFile = value; }
}
///
/// Gets or sets used to write to the file.
///
///
/// The used to write to the file.
///
///
///
/// The default encoding set is
/// which is the encoding for the system's current ANSI code page.
///
///
public Encoding Encoding
{
get { return m_encoding; }
set { m_encoding = value; }
}
///
/// Gets or sets the used to write to the file.
///
///
/// The used to write to the file.
///
///
///
/// Unless a specified here for this appender
/// the is queried for the
/// security context to use. The default behavior is to use the security context
/// of the current thread.
///
///
public SecurityContext SecurityContext
{
get { return m_securityContext; }
set { m_securityContext = value; }
}
#if NETCF
///
/// Gets or sets the used to handle locking of the file.
///
///
/// The used to lock the file.
///
///
///
/// Gets or sets the used to handle locking of the file.
///
///
/// There are two built in locking models, and .
/// The first locks the file from the start of logging to the end, the
/// second locks only for the minimal amount of time when logging each message
/// and the last synchronizes processes using a named system wide Mutex.
///
///
/// The default locking model is the .
///
///
#else
///
/// Gets or sets the used to handle locking of the file.
///
///
/// The used to lock the file.
///
///
///
/// Gets or sets the used to handle locking of the file.
///
///
/// There are three built in locking models, , and .
/// The first locks the file from the start of logging to the end, the
/// second locks only for the minimal amount of time when logging each message
/// and the last synchronizes processes using a named system wide Mutex.
///
///
/// The default locking model is the .
///
///
#endif
public FileAppender.LockingModelBase LockingModel
{
get { return m_lockingModel; }
set { m_lockingModel = value; }
}
#endregion Public Instance Properties
#region Override implementation of AppenderSkeleton
///
/// Activate the options on the file appender.
///
///
///
/// This is part of the delayed object
/// activation scheme. The method must
/// be called on this object after the configuration properties have
/// been set. Until is called this
/// object is in an undefined state and must not be used.
///
///
/// If any of the configuration properties are modified then
/// must be called again.
///
///
/// This will cause the file to be opened.
///
///
public override void ActivateOptions()
{
base.ActivateOptions();
if (m_securityContext == null)
{
m_securityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
}
if (m_lockingModel == null)
{
m_lockingModel = new FileAppender.ExclusiveLock();
}
m_lockingModel.CurrentAppender = this;
m_lockingModel.ActivateOptions();
if (m_fileName != null)
{
using (SecurityContext.Impersonate(this))
{
m_fileName = ConvertToFullPath(m_fileName.Trim());
}
SafeOpenFile(m_fileName, m_appendToFile);
}
else
{
LogLog.Warn(declaringType, "FileAppender: File option not set for appender [" + Name + "].");
LogLog.Warn(declaringType, "FileAppender: Are you using FileAppender instead of ConsoleAppender?");
}
}
#endregion Override implementation of AppenderSkeleton
#region Override implementation of TextWriterAppender
///
/// Closes any previously opened file and calls the parent's .
///
///
///
/// Resets the filename and the file stream.
///
///
protected override void Reset()
{
base.Reset();
m_fileName = null;
}
///
/// Close this appender instance. The underlying stream or writer is also closed.
///
protected override void OnClose()
{
base.OnClose();
m_lockingModel.OnClose();
}
///
/// Called to initialize the file writer
///
///
///
/// Will be called for each logged message until the file is
/// successfully opened.
///
///
protected override void PrepareWriter()
{
SafeOpenFile(m_fileName, m_appendToFile);
}
///
/// This method is called by the
/// method.
///
/// The event to log.
///
///
/// Writes a log statement to the output stream if the output stream exists
/// and is writable.
///
///
/// The format of the output will depend on the appender's layout.
///
///
protected override void Append(LoggingEvent loggingEvent)
{
if (m_stream.AcquireLock())
{
try
{
base.Append(loggingEvent);
}
finally
{
m_stream.ReleaseLock();
}
}
}
///
/// This method is called by the
/// method.
///
/// The array of events to log.
///
///
/// Acquires the output file locks once before writing all the events to
/// the stream.
///
///
protected override void Append(LoggingEvent[] loggingEvents)
{
if (m_stream.AcquireLock())
{
try
{
base.Append(loggingEvents);
}
finally
{
m_stream.ReleaseLock();
}
}
}
///
/// Writes a footer as produced by the embedded layout's property.
///
///
///
/// Writes a footer as produced by the embedded layout's property.
///
///
protected override void WriteFooter()
{
if (m_stream != null)
{
//WriteFooter can be called even before a file is opened
m_stream.AcquireLock();
try
{
base.WriteFooter();
}
finally
{
m_stream.ReleaseLock();
}
}
}
///
/// Writes a header produced by the embedded layout's property.
///
///
///
/// Writes a header produced by the embedded layout's property.
///
///
protected override void WriteHeader()
{
if (m_stream != null)
{
if (m_stream.AcquireLock())
{
try
{
base.WriteHeader();
}
finally
{
m_stream.ReleaseLock();
}
}
}
}
///
/// Closes the underlying .
///
///
///
/// Closes the underlying .
///
///
protected override void CloseWriter()
{
if (m_stream != null)
{
m_stream.AcquireLock();
try
{
base.CloseWriter();
}
finally
{
m_stream.ReleaseLock();
}
}
}
#endregion Override implementation of TextWriterAppender
#region Public Instance Methods
///
/// Closes the previously opened file.
///
///
///
/// Writes the to the file and then
/// closes the file.
///
///
protected void CloseFile()
{
WriteFooterAndCloseWriter();
}
#endregion Public Instance Methods
#region Protected Instance Methods
///
/// Sets and opens the file where the log output will go. The specified file must be writable.
///
/// The path to the log file. Must be a fully qualified path.
/// If true will append to fileName. Otherwise will truncate fileName
///
///
/// Calls but guarantees not to throw an exception.
/// Errors are passed to the .
///
///
protected virtual void SafeOpenFile(string fileName, bool append)
{
try
{
OpenFile(fileName, append);
}
catch (Exception e)
{
ErrorHandler.Error("OpenFile(" + fileName + "," + append + ") call failed.", e, ErrorCode.FileOpenFailure);
}
}
///
/// Sets and opens the file where the log output will go. The specified file must be writable.
///
/// The path to the log file. Must be a fully qualified path.
/// If true will append to fileName. Otherwise will truncate fileName
///
///
/// If there was already an opened file, then the previous file
/// is closed first.
///
///
/// This method will ensure that the directory structure
/// for the specified exists.
///
///
protected virtual void OpenFile(string fileName, bool append)
{
if (LogLog.IsErrorEnabled)
{
// Internal check that the fileName passed in is a rooted path
bool isPathRooted = false;
using (SecurityContext.Impersonate(this))
{
isPathRooted = Path.IsPathRooted(fileName);
}
if (!isPathRooted)
{
LogLog.Error(declaringType, "INTERNAL ERROR. OpenFile(" + fileName + "): File name is not fully qualified.");
}
}
lock (this)
{
Reset();
LogLog.Debug(declaringType, "Opening file for writing [" + fileName + "] append [" + append + "]");
// Save these for later, allowing retries if file open fails
m_fileName = fileName;
m_appendToFile = append;
LockingModel.CurrentAppender = this;
LockingModel.OpenFile(fileName, append, m_encoding);
m_stream = new LockingStream(LockingModel);
if (m_stream != null)
{
m_stream.AcquireLock();
try
{
SetQWForFiles(m_stream);
}
finally
{
m_stream.ReleaseLock();
}
}
WriteHeader();
}
}
///
/// Sets the quiet writer used for file output
///
/// the file stream that has been opened for writing
///
///
/// This implementation of creates a
/// over the and passes it to the
/// method.
///
///
/// This method can be overridden by sub classes that want to wrap the
/// in some way, for example to encrypt the output
/// data using a System.Security.Cryptography.CryptoStream.
///
///
protected virtual void SetQWForFiles(Stream fileStream)
{
#pragma warning disable CA2000 // Dispose objects before losing scope
StreamWriter writer = new StreamWriter(fileStream, m_encoding);
#pragma warning restore CA2000 // Dispose objects before losing scope
SetQWForFiles(writer);
}
///
/// Sets the quiet writer being used.
///
/// the writer over the file stream that has been opened for writing
///
///
/// This method can be overridden by sub classes that want to
/// wrap the in some way.
///
///
protected virtual void SetQWForFiles(TextWriter writer)
{
QuietWriter = new QuietTextWriter(writer, ErrorHandler);
}
#endregion Protected Instance Methods
#region Protected Static Methods
///
/// Convert a path into a fully qualified path.
///
/// The path to convert.
/// The fully qualified path.
///
///
/// Converts the path specified to a fully
/// qualified path. If the path is relative it is
/// taken as relative from the application base
/// directory.
///
///
protected static string ConvertToFullPath(string path)
{
return SystemInfo.ConvertToFullPath(path);
}
#endregion Protected Static Methods
#region Private Instance Fields
///
/// Flag to indicate if we should append to the file
/// or overwrite the file. The default is to append.
///
private bool m_appendToFile = true;
///
/// The name of the log file.
///
private string m_fileName = null;
///
/// The encoding to use for the file stream.
///
private Encoding m_encoding = Encoding.GetEncoding(0);
///
/// The security context to use for privileged calls
///
private SecurityContext m_securityContext;
///
/// The stream to log to. Has added locking semantics
///
private FileAppender.LockingStream m_stream = null;
///
/// The locking model to use
///
private FileAppender.LockingModelBase m_lockingModel = new FileAppender.ExclusiveLock();
#endregion Private Instance Fields
#region Private Static Fields
///
/// The fully qualified type of the FileAppender class.
///
///
/// Used by the internal logger to record the Type of the
/// log message.
///
private static readonly Type declaringType = typeof(FileAppender);
#endregion Private Static Fields
}
}