/* * Copyright 2018 faddenSoft * * Licensed 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. */ using System; using System.Collections.Generic; using System.IO; using System.Text; namespace CommonUtil { /// /// Debug log facility, with priority levels and time stamps. /// /// The logs are held in memory. The internal storage expands as logs are added /// until the maximum size is reached, then switches to circular buffering. This /// minimizes overhead for small logs while avoiding infinite expansion. /// public class DebugLog { /// /// Log priority levels, in ascending order. "Silent" is only used as an argument /// when setting the minimum priority level. /// public enum Priority { Verbose = 0, Debug, Info, Warning, Error, Silent } private static char[] sSingleLetter = { 'V', 'D', 'I', 'W', 'E', 'S' }; /// /// Holds a single log entry. /// private struct LogEntry { public DateTime mWhen; public Priority mPriority; public string mText; public LogEntry(Priority prio, string msg) { mWhen = DateTime.Now; mPriority = prio; mText = msg; } } /// /// Log collection. /// private List mEntries = new List(); private int mTopEntry = 0; /// /// Date/time when the log object was created. Used for relative time display mode. /// private DateTime mStartWhen; /// /// If set, display time stamps as relative time rather than absolute. /// private bool mShowRelTime = false; /// /// Minimum priority level. Anything below this is ignored. /// private Priority mMinPriority = Priority.Debug; /// /// Maximum number of lines we'll hold in memory. This is a simple measure /// to keep the process from expanding without bound. /// private int mMaxLines = 100000; /// /// Constructor. Configures min priority to Info. /// public DebugLog() : this(Priority.Info) { } /// /// Constructor. /// /// Minimum log priority level. public DebugLog(Priority prio) { mMinPriority = prio; mStartWhen = DateTime.Now; LogI("Log started at " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss zzz")); } /// /// Sets the message priority threshold. Messages below the specified priority /// will be ignored. /// /// Minimum priority value. public void SetMinPriority(Priority prio) { mMinPriority = prio; } /// /// Sets the "show relative time" flag. If set, the timestamp in the log is /// relative to when the log object was created, instead of wall-clock time. /// /// public void SetShowRelTime(bool showRelTime) { mShowRelTime = showRelTime; } /// /// Returns true if a message logged at the specified priority would be accepted. /// /// /// public bool IsLoggable(Priority prio) { return prio >= mMinPriority; } /// /// Clears all entries. /// public void Clear() { mEntries.Clear(); } /// /// Adds a message to the log buffer. /// /// Log priority. /// Message to log. public void Log(Priority prio, string message) { if (prio < mMinPriority) { return; } LogEntry ent = new LogEntry(prio, message); if (mEntries.Count < mMaxLines) { // Still growing. mEntries.Add(ent); } else { // Circular replacement. Adding to the end then removing [0] has // significant performance issues. mEntries[mTopEntry++] = ent; if (mTopEntry == mMaxLines) { mTopEntry = 0; } } } public void LogV(string message) { Log(Priority.Verbose, message); } public void LogD(string message) { Log(Priority.Debug, message); } public void LogI(string message) { Log(Priority.Info, message); } public void LogW(string message) { Log(Priority.Warning, message); } public void LogE(string message) { Log(Priority.Error, message); } /// /// Dumps the contents of the log to a file. /// /// Full or partial pathname. public void WriteToFile(string pathName) { StringBuilder sb = new StringBuilder(); using (StreamWriter sw = new StreamWriter(pathName, false, Encoding.UTF8)) { for (int i = mTopEntry; i < mEntries.Count; i++) { WriteEntry(sw, mEntries[i], sb); } for (int i = 0; i < mTopEntry; i++) { WriteEntry(sw, mEntries[i], sb); } } } /// /// Writes a single entry to a file. Pass in a StringBuilder so we don't have /// to create a new one every time. /// private void WriteEntry(StreamWriter sw, LogEntry ent, StringBuilder sb) { sb.Clear(); FormatEntry(ent, sb); sw.WriteLine(sb.ToString()); } /// /// Formats an entry, appending the text to the provided StringBuilder. /// private void FormatEntry(LogEntry ent, StringBuilder sb) { if (mShowRelTime) { sb.Append((ent.mWhen - mStartWhen).ToString(@"mm\:ss\.fff")); } else { sb.Append(ent.mWhen.ToString(@"hh\:mm\:ss\.fff")); } sb.Append(' '); sb.Append(sSingleLetter[(int)ent.mPriority]); sb.Append(' '); sb.Append(ent.mText); } /// /// Dumps the contents of the log to a string. This is intended for display in a /// text box, so lines are separated with CRLF. /// /// public string WriteToString() { StringBuilder sb = new StringBuilder(); for (int i = mTopEntry; i < mEntries.Count; i++) { FormatEntry(mEntries[i], sb); sb.Append("\r\n"); } for (int i = 0; i < mTopEntry; i++) { FormatEntry(mEntries[i], sb); sb.Append("\r\n"); } return sb.ToString(); } public override string ToString() { return "DebugLog has " + mEntries.Count + " entries"; } } }