/*
* 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";
}
}
}