/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.IO; using System.Text; using log4net; namespace OpenSim.Framework { /// /// Class for writing a high performance, high volume log file. /// Sometimes, to debug, one has a high volume logging to do and the regular /// log file output is not appropriate. /// Create a new instance with the parameters needed and /// call Write() to output a line. Call Close() when finished. /// If created with no parameters, it will not log anything. /// public class LogWriter : IDisposable { public bool Enabled { get; private set; } private readonly string m_logDirectory = "."; private readonly int m_logMaxFileTimeMin = 5; // 5 minutes public String LogFileHeader { get; set; } private StreamWriter m_logFile = null; private readonly TimeSpan m_logFileLife; private DateTime m_logFileEndTime; private readonly Object m_logFileWriteLock = new Object(); private readonly bool m_flushWrite; // set externally when debugging. If let 'null', this does not write any error messages. public ILog ErrorLogger = null; private readonly string LogHeader = "[LOG WRITER]"; /// /// Create a log writer that will not write anything. Good for when not enabled /// but the write statements are still in the code. /// public LogWriter() { Enabled = false; m_logFile = null; } /// /// Create a log writer instance. /// /// The directory to create the log file in. May be 'null' for default. /// The characters that begin the log file name. May be 'null' for default. /// Maximum age of a log file in minutes. If zero, will set default. /// Whether to do a flush after every log write. Best left off but /// if one is looking for a crash, this is a good thing to turn on. public LogWriter(string dir, string headr, int maxFileTime, bool flushWrite) { m_logDirectory = dir ?? "."; LogFileHeader = headr ?? "log-"; m_logMaxFileTimeMin = maxFileTime; if (m_logMaxFileTimeMin < 1) m_logMaxFileTimeMin = 5; m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0); m_logFileEndTime = DateTime.Now + m_logFileLife; m_flushWrite = flushWrite; Enabled = true; } // Constructor that assumes flushWrite is off. public LogWriter(string dir, string headr, int maxFileTime) : this(dir, headr, maxFileTime, false) { } public void Dispose() { Close(); GC.SuppressFinalize(this); } public void Close() { Enabled = false; if (m_logFile is not null) { m_logFile.Close(); m_logFile.Dispose(); m_logFile = null; } } public void Write(string line, params object[] args) { if (!Enabled) return; Write(String.Format(line, args)); } public void Flush() { if (!Enabled) return; m_logFile?.Flush(); } public void Write(string line) { if (!Enabled) return; try { lock (m_logFileWriteLock) { DateTime now = DateTime.UtcNow; if (m_logFile is null || now > m_logFileEndTime) { if (m_logFile is not null) { m_logFile.Close(); m_logFile.Dispose(); m_logFile = null; } // First log file or time has expired, start writing to a new log file m_logFileEndTime = now + m_logFileLife; string path = (m_logDirectory.Length > 0 ? m_logDirectory + System.IO.Path.DirectorySeparatorChar.ToString() : "") + String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss")); m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)); } if (m_logFile is not null) { StringBuilder buff = new StringBuilder(line.Length + 25); buff.Append(now.ToString("yyyyMMddHHmmssfff")); // buff.Append(now.ToString("yyyyMMddHHmmss")); buff.Append('"'); buff.Append(line); buff.Append("\r\n"); m_logFile.Write(buff.ToString()); if (m_flushWrite) m_logFile.Flush(); } } } catch (Exception e) { ErrorLogger?.ErrorFormat("{0}: FAILURE WRITING TO LOGFILE: {1}", LogHeader, e); Enabled = false; } return; } } }