C#日志记录工具 MicroLog
Posted Thomas会写字
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#日志记录工具 MicroLog相关的知识,希望对你有一定的参考价值。
一个简单的日志记录工具,性能高效,常用功能都有,结构简单。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
using System.IO;
namespace MicroLog
public class MLog
public MFilter filter = new MFilter();
public MConvert convert = new MConvert();
public MThr thr = new MThr();
public List<MLog> transTo = new List<MLog>();
public LinkItem<LogItem> linkLog = new LinkItem<LogItem>();
public int logSize = 0;
public object objLog = new object();
public void Write(String msg, MType type = MType.mType1, Exception e = null)
LogItem log = new LogItem(type, msg, e);
if (transTo != null)
for (int i = 0; i < transTo.Count; i++)
transTo[i].Write(msg, type, e);
// Thr
log.thrName = thr.Name;
// filter
if (filter.Filt(log) == false) return;
// convert
convert.FormatLog(log);
// addLink
lock (objLog)
linkLog.AddEnd(log);
logSize += log.logSize;
public class LogItem
public DateTime time;
public Exception e;
public String thrName;
public String mark;
public byte[] logHead;
public String msg;
public MType type;
public String fileName;
public String filePath;
public String fileExten;
public int thrID = -1;
public int logSize = 0;
public byte[] logBuf;
public LogItem(MType type, String msg, Exception e = null)
time = DateTime.Now;
this.type = type;
this.msg = msg;
this.e = e;
public class MConvert
public bool IsEnableLogHead = true;
public bool IsStableName = false;
byte[] logHead = null;
String logName = "";
String logNameExt = "";
String logMark = "";
String logLayout = "";
String pathLayout = "";
List<String> listLogLayout = new List<String>();
List<String> listPathLayout = new List<String>();
Dictionary<String, int> dictLog;
Dictionary<int, String> dictPath;
public String LogHead
get
if (logHead == null || logHead.Length == 0) return "";
return Encoding.UTF8.GetString(logHead, 0, logHead.Length - 1);
set
logHead = Encoding.UTF8.GetBytes(value + "\\n");
public String LogName
get
return logName + "." + logNameExt;
set
int n = value.LastIndexOf('.');
if (n > 0)
logName = value.Substring(0, n);
logNameExt = value.Substring(n + 1, value.Length - n - 1);
else
logName = value;
logNameExt = "log";
public String LogMark
get
return logMark;
set
logMark = value;
public String LogLayout
get => logLayout;
set
logLayout = value;
String format = value;
int n1 = format.IndexOf("");
int n2 = format.IndexOf("");
dictLog = new Dictionary<string, int>();
while (n1 >= 0 && n2 >= 0)
if (n1 > 0)
listLogLayout.Add(format.Substring(0, n1));
String sKey = format.Substring(n1 + 1, n2 - n1 - 1);
dictLog.Add(sKey, listLogLayout.Count);
listLogLayout.Add("");
format = format.Substring(n2 + 1, format.Length - n2 - 1);
n1 = format.IndexOf("");
n2 = format.IndexOf("");
if (format.Length > 0)
listLogLayout.Add(format);
listLogLayout.Add("\\n");
public String PathLayout
get
return pathLayout;
set
pathLayout = value;
String format = value;
int n1 = format.IndexOf("");
int n2 = format.IndexOf("");
dictPath = new Dictionary<int, String>();
while (n1 >= 0 && n2 >= 0)
if (n1 > 0)
listPathLayout.Add(format.Substring(0, n1));
String sKey = format.Substring(n1 + 1, n2 - n1 - 1);
dictPath.Add(listPathLayout.Count, sKey);
listPathLayout.Add("");
format = format.Substring(n2 + 1, format.Length - n2 - 1);
n1 = format.IndexOf("");
n2 = format.IndexOf("");
if (format.Length > 0)
listPathLayout.Add(format);
if (listPathLayout.Count == 0 || listPathLayout[listPathLayout.Count - 1] != "\\\\")
listPathLayout.Add("\\\\");
public void FormatLog(LogItem log)
if (IsEnableLogHead)
log.logHead = logHead;
log.fileName = logName;
log.fileExten = logNameExt;
log.mark = LogMark;
foreach (String sK in dictLog.Keys)
int nIdx = 0;
if (dictLog.TryGetValue(sK, out nIdx))
if (sK.ToLower() == "time")
listLogLayout[nIdx] = log.time.ToString("HH:mm:ss,fff");
else if (sK.ToLower() == "exp" && log.e != null)
listLogLayout[nIdx] = log.e.StackTrace;
else if (sK.ToLower() == "thrname")
listLogLayout[nIdx] = log.thrName;
else if (sK.ToLower() == "thrid")
listLogLayout[nIdx] = log.thrID.ToString("D3");
else if (sK.ToLower() == "type")
listLogLayout[nIdx] = Enum.GetName(log.type.GetType(), log.type);
else if (sK.ToLower() == "msg")
listLogLayout[nIdx] = log.msg;
else if (sK.ToLower() == "mark")
listLogLayout[nIdx] = log.mark;
int nS = 0;
for (int i = 0; i < listLogLayout.Count; i++)
nS += listLogLayout[i].Length * 2;// 中文字符占两个长度
log.logBuf = new byte[nS];
log.logSize = 0;
for (int i = 0; i < listLogLayout.Count; i++)
byte[] tmp = Encoding.UTF8.GetBytes(listLogLayout[i]);
Array.Copy(tmp, 0, log.logBuf, log.logSize, tmp.Length);
log.logSize += tmp.Length;
foreach (int nIdx in dictPath.Keys)
String s = "";
if (dictPath.TryGetValue(nIdx, out s))
listPathLayout[nIdx] = log.time.ToString(s);
log.filePath = "";
for (int i = 0; i < listPathLayout.Count; i++)
log.filePath += listPathLayout[i];
public String GetLogName()
String s = GetFullPath();
int n = s.LastIndexOf("\\\\");
return s.Substring(n + 1, s.Length - n - 1);
public String GetLogPath()
foreach (int nIdx in dictPath.Keys)
String s = "";
DateTime time = DateTime.Now;
if (dictPath.TryGetValue(nIdx, out s))
listPathLayout[nIdx] = time.ToString(s);
String filePath = "";
for (int i = 0; i < listPathLayout.Count; i++)
filePath += pathLayout[i];
return filePath;
public String GetFullPath()
String basePath = GetLogPath();
if (IsStableName == true)
return basePath + logName + "." + logNameExt;
String[] files = Directory.GetFiles(basePath, logName + "#*", SearchOption.AllDirectories);
if (files.Length > 0)
List<String> listFiles = new List<String>(1000);
for (int i = 0; i < files.Length; i++)
listFiles.Add(files[i]);
listFiles.Sort(new CompareRisedTime());
return listFiles[listFiles.Count - 1];
return basePath + logName + "#" + (0).ToString("D3") + "." + logNameExt;
public class MFilter
String[] excludeMsg;
String[] includeMsg;
String[] excludeThr;
String[] includeThr;
String exMsg;
String inMsg;
String exThr;
String inThr;
bool[] isEnableLog = null;
public bool IsEnable = false;
public String ExMsg
get
return exMsg;
set
exMsg = value;
excludeMsg = exMsg.Split(',');
public String InMsg
get
return inMsg;
set
inMsg = value;
includeMsg = inMsg.Split(',');
public String ExThr
get
return exThr;
set
exThr = value;
excludeThr = exThr.Split(',');
public String InThr
get
return inThr;
set
inThr = value;
includeThr = inThr.Split(',');
public MFilter()
int len = System.Enum.GetNames((new MType()).GetType()).Length;
isEnableLog = new bool[len];
for (int i = 0; i < len; i++)
isEnableLog[i] = true;
public void EnableLog(MType type, bool bEnable)
isEnableLog[(int)type] = bEnable;
public void EnableAllLog(bool bEnable)
for (int i = 0; i < isEnableLog.Length; i++)
isEnableLog[i] = bEnable;
public bool IsEnableLog(MType type)
return isEnableLog[(int)type];
public bool Filt(LogItem log)
if (IsEnable == false) return true;
if (isEnableLog[(int)log.type] == false) return false;
if (excludeMsg != null && excludeMsg.Length > 0)
foreach (String str in excludeMsg)
if (log.msg.IndexOf(str) >= 0) return false;
if (excludeThr != null && excludeThr.Length > 0)
foreach (String str in excludeThr)
if (log.thrName.IndexOf(str) >= 0) return false;
if (includeMsg != null && includeMsg.Length > 0)
foreach (String str in includeMsg)
if (log.msg.IndexOf(str) >= 0) goto next1;
return false;
next1:
if (includeThr != null && includeThr.Length > 0)
foreach (String str in includeThr)
if (log.thrName.IndexOf(str) >= 0) goto next2;
return false;
next2:
return true;
public class MThr
public bool IsEnable = false;
public int ID
get
return Manager.GetCurThrID();
public string Name
get
return Manager.GetCurThrName();
set
Manager.SetCurThrName(value);
public static class Manager
static Dictionary<String, MLog> dictLog = new Dictionary<String, MLog>();
static Dictionary<String, MWriter> dictWrite = new Dictionary<String, MWriter>();
static Dictionary<int, String> dictThr = new Dictionary<int, String>();
static Object objLockLog = new object();
static Object objLockThr = new object();
static int maxLogSize = 1024 * 1024 * 10;
static int maxLogCount = 20;
static int logWriteTime = 100;
public static int MaxLogSize
get
return maxLogSize;
set
maxLogSize = value;
if (maxLogSize < 1024 * 1024) maxLogSize = 1024 * 1024;
public static int MaxLogCount
get
return maxLogCount;
set
maxLogCount = value;
if (maxLogCount < 1) maxLogCount = 1;
public static int LogWriteTime
get
return logWriteTime;
set
logWriteTime = value;
if (logWriteTime < 10) logWriteTime = 10;
if (logWriteTime > 1000) logWriteTime = 1000;
public static int SetCurThrName(String name)
int id = GetCurThrID();
lock (objLockThr)
if (dictThr.ContainsKey(id))
dictThr.Remove(id);
dictThr.Add(id, name);
return id;
public static int GetCurThrID()
return AppDomain.GetCurrentThreadId();
public static String GetCurThrName()
String name = "";
lock (objLockThr)
int id = GetCurThrID();
if (!dictThr.TryGetValue(id, out name))
name = "";
return name;
public static void AddLog(String Name, MLog log)
lock (objLockLog)
if (dictLog.ContainsKey(Name))
dictLog.Remove(Name);
if (dictWrite.ContainsKey(Name))
dictWrite.Remove(Name);
MWriter w = new MWriter(log);
dictLog.Add(Name, log);
dictWrite.Add(Name, w);
public static void RemoveLog(String Name)
lock (objLockLog)
MLog log = null;
MWriter w = null;
if (dictLog.TryGetValue(Name, out log) && dictWrite.TryGetValue(Name, out w))
dictLog.Remove(Name);
log.filter.IsEnable = true;
log.filter.EnableAllLog(false);
w.bExit = true;
public static MLog GetLog(String Name)
MLog log = null;
lock (objLockLog)
if (dictLog.ContainsKey(Name))
dictLog.TryGetValue(Name, out log);
return log;
public static MLog[] GetLogs()
lock (objLockLog)
return dictLog.Values.ToArray();
public static String[] GetNames()
lock (objLockLog)
return dictLog.Keys.ToArray();
public static MWriter GetWriter(String name)
lock (objLockLog)
MWriter w = null;
dictWrite.TryGetValue(name, out w);
return w;
public class MWriter
MLog microLog;
Thread thr;
List<String> listFiles;
int nameIdx = -1;
public bool bFlush = false;
public bool bExit = false;
byte[] buf = null;
int nExtraSize = 1024 * 1024;
object objCF = new object();
public MWriter(MLog microLog)
buf = new byte[Manager.MaxLogSize + nExtraSize];
this.microLog = microLog;
listFiles = new List<String>();
thr = new Thread(FuncWriteLog);
thr.IsBackground = true;
thr.Start();
void FuncWriteLog()
Stopwatch sw = new Stopwatch();
sw.Restart();
while (bExit == false)
Thread.Sleep(10);
if (sw.ElapsedMilliseconds < Manager.LogWriteTime && bFlush == false)
continue;
if (buf.Length != Manager.MaxLogSize + nExtraSize)
buf = new byte[Manager.MaxLogSize + nExtraSize];
LinkItem<LogItem> linkLog = null;
lock (microLog.objLog)
if (microLog.logSize > 0)
linkLog = microLog.linkLog;
microLog.linkLog = new LinkItem<LogItem>();
microLog.logSize = 0;
if (linkLog == null || linkLog.Count() == 0)
continue;
SubItem<LogItem> itm = linkLog.FromFirst();
String basePath = itm.t.filePath;
String baseName = itm.t.fileName;
String baseNameExt = itm.t.fileExten;
if (Directory.Exists(basePath) == false)
Directory.CreateDirectory(basePath);
listFiles.Clear();
nameIdx = -1;
int remainSize = 0;
String name = CheckFile(basePath, baseName, baseNameExt, microLog.convert.IsStableName, ref remainSize);
int writedSize = 0;
while (itm != null)
if (itm.t.filePath == basePath)
if (remainSize == Manager.MaxLogSize && writedSize == 0 &&
microLog.convert.IsEnableLogHead == true && itm.t.logHead != null
&& itm.t.logHead.Length > 0)
Array.Copy(itm.t.logHead, 0, buf, writedSize, itm.t.logHead.Length);
writedSize += itm.t.logHead.Length;
if (writedSize < remainSize
&& baseName == itm.t.fileName
&& baseNameExt == itm.t.fileExten)
Array.Copy(itm.t.logBuf, 0, buf, writedSize, itm.t.logSize);
writedSize += itm.t.logSize;
else
WriteLog(buf, writedSize, name);
basePath = itm.t.filePath;
baseName = itm.t.fileName;
baseNameExt = itm.t.fileExten;
name = CheckFile(basePath, baseName, baseNameExt, microLog.convert.IsStableName, ref remainSize);
writedSize = 0;
itm = itm.pPrev;
else
basePath = itm.t.filePath;
baseName = itm.t.fileName;
baseNameExt = itm.t.fileExten;
if (Directory.Exists(basePath) == false)
Directory.CreateDirectory(basePath);
listFiles.Clear();
nameIdx = -1;
WriteLog(buf, writedSize, name);
name = CheckFile(basePath, baseName, baseNameExt, microLog.convert.IsStableName, ref remainSize);
writedSize = 0;
itm = itm.pPrev;
if (itm != null)
itm = itm.pNext;
if (writedSize > 0)
WriteLog(buf, writedSize, name);
bFlush = false;
GC.Collect();
void WriteLog(byte[] buf, int size, String name)
using (FileStream fs = new FileStream(name, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
fs.Write(buf, 0, size);
fs.Close();
void Flush()
bFlush = true;
String CheckFile(String basePath, String baseName, String baseNameExt, bool bStableName, ref int remainSize)
lock (objCF)
String s = "";
int nFileSize = 0;
if (bStableName) // 如果是固定名字
s = basePath + baseName + "." + baseNameExt;
if (File.Exists(s))
nFileSize = (int)(new FileInfo(s)).Length;
remainSize = Manager.MaxLogSize - nFileSize;
if (remainSize < 0)
File.Delete(s);
remainSize = Manager.MaxLogSize;
return s;
String[] files = Directory.GetFiles(basePath, baseName + "#*", SearchOption.AllDirectories);
if (listFiles.Count == 0 && files.Length > 0)
for (int i = 0; i < files.Length; i++)
listFiles.Add(files[i]);
listFiles.Sort(new CompareRisedTime());
while (listFiles.Count > Manager.MaxLogCount)
File.Delete(listFiles[0]);
listFiles.RemoveAt(0);
if (listFiles.Count > 0)
s = listFiles[listFiles.Count - 1];
if (nameIdx < 0)
int n = s.LastIndexOf("#");
String sub = s.Substring(n + 1, 3);
nameIdx = int.Parse(sub);
nFileSize = 0;
if (File.Exists(s))
int n1 = s.LastIndexOf("\\\\");
int n2 = s.LastIndexOf("#");
String sub = s.Substring(n1 + 1, n2 - n1 - 1);
if (sub != baseName)
nameIdx = 0;
s = basePath + baseName + "#" + nameIdx.ToString("D3") + "." + baseNameExt;
listFiles.Clear();
listFiles.Add(s);
nFileSize = 0;
else
nFileSize = (int)(new FileInfo(s)).Length;
else
nameIdx++;
s = basePath + baseName + "#" + nameIdx.ToString("D3") + "." + baseNameExt;
remainSize = Manager.MaxLogSize - nFileSize;
if (remainSize <= 0)
nameIdx++;
s = basePath + baseName + "#" + nameIdx.ToString("D3") + "." + baseNameExt;
listFiles.Add(s);
remainSize = Manager.MaxLogSize;
return s;
public class SubItem<T>
public T t;
public SubItem<T> pPrev;
public SubItem<T> pNext;
public SubItem(T tVal, SubItem<T> pPrev_, SubItem<T> pNext_)
t = tVal;
pPrev = pPrev_;
pNext = pNext_;
public class LinkItem<T>
SubItem<T> pZero = null;
SubItem<T> pEnd = null;
int nCount = 0;
object obj = new object();
public SubItem<T> FromFirst()
return pZero;
public void AddAt(T t_, int nPos)
if (nPos < 0 || nPos > nCount)
throw new ArgumentException("插入的位置不合适 nPos=" + nPos + " ,COunt=" + nCount);
lock (obj)
if (nPos == 0)
pZero.pPrev = new SubItem<T>(t_, null, pZero);
pZero = pZero.pPrev;
else if (nPos == nCount)
pEnd.pNext = new SubItem<T>(t_, pEnd, null); ;
pEnd = pEnd.pNext;
else
SubItem<T> pSel = pZero;
for (int i = 0; i < nCount; i++)
if (i == nPos)
pSel.pPrev.pNext = new SubItem<T>(t_, pSel.pPrev, pSel);
pSel.pPrev.pNext.pNext.pPrev = pSel.pPrev.pNext;
break;
pSel = pSel.pNext;
nCount++;
public void AddEnd(T t_)
lock (obj)
if (pEnd == null)
pEnd = new SubItem<T>(t_, null, null);
pZero = pEnd;
else if (nCount == 1)
pEnd = new SubItem<T>(t_, pZero, null);
pZero.pNext = pEnd;
else
pEnd.pNext = new SubItem<T>(t_, pEnd, null); ;
pEnd = pEnd.pNext;
//pNow.pNext = null;
nCount++;
public void AddFirst(T t_)
lock (obj)
if (pZero == null)
pZero = new SubItem<T>(t_, null, null);
pEnd = pZero;
else
pZero.pPrev = new SubItem<T>(t_, null, pZero);
pZero = pZero.pPrev;
//pZero.pPrev = null;
nCount++;
public T RemoveAt(int nPos)
if (nPos < 0 || nPos >= nCount)
throw new ArgumentException("输入参数超限 idx=" + nPos + " ,Count=" + nCount);
lock (obj)
SubItem<T> pSel = pZero;
for (int i = 0; i < nCount; i++)
if (i == nPos)
if (pSel.pPrev != null)
if (i == nCount - 1)
pEnd = pEnd.pPrev;
pSel.pPrev.pNext = pSel.pNext;
if (pSel.pNext != null)
if (i == 0)
pZero = pZero.pNext;
pSel.pNext.pPrev = pSel.pPrev;
nCount--;
if (nCount == 0) Clear();
break;
pSel = pSel.pNext;
return pSel.t;
public T RemoveEnd()
if (nCount == 0)
throw new ArgumentException("List容量为0,无法移除");
lock (obj)
T t_ = pEnd.t;
if (pEnd.pPrev != null)
pEnd = pEnd.pPrev;
pEnd.pNext = null;
nCount--;
else
Clear();
return t_;
public T RemoveFirst()
if (nCount == 0)
throw new ArgumentException("List容量为0,无法移除");
lock (obj)
T t_ = pZero.t;
if (pZero.pNext != null)
pZero = pZero.pNext;
pZero.pPrev = null;
nCount--;
else
Clear();
return t_;
public T ViewFirst()
if (nCount < 1)
throw new ArgumentException("数量为0 nCount=" + nCount);
return pZero.t;
public T ViewAt(int nPos)
if (nPos < 0 || nPos >= nCount)
throw new ArgumentException("输入参数超限 idx=" + nPos + " ,Count=" + nCount);
lock (obj)
SubItem<T> pSel = pZero;
for (int i = 0; i < nCount; i++)
if (i == nPos)
break;
pSel = pSel.pNext;
return pSel.t;
public T ViewEnd()
if (nCount < 1)
throw new ArgumentException("数量为0 nCount=" + nCount);
return pEnd.t;
// 十分耗费资源
public T this[int index]
get
lock (obj)
if (index < 0 || index >= nCount)
throw new ArgumentException("输入参数超限 idx=" + index + " ,Count=" + nCount);
SubItem<T> pSel = pZero;
T tRtn = pSel.t;
for (int i = 0; i < nCount; i++)
if (i == index)
tRtn = pSel.t;
break;
pSel = pSel.pNext;
return tRtn;
set
lock (obj)
if (index < 0 || index >= nCount)
throw new ArgumentException("输入参数超限 idx=" + index + " ,Count=" + nCount);
SubItem<T> pSel = pZero;
for (int i = 0; i < nCount; i++)
if (i == index)
pSel.t = value;
break;
pSel = pSel.pNext;
public void Clear()
lock (obj)
pZero = null;
pEnd = null;
nCount = 0;
public int Count()
return nCount;
public T[] GetElements()
if (Count() == 0)
return null;
T[] tt = null;
lock (obj)
tt = new T[Count()];
for (int i = 0; i < Count(); i++)
tt[i] = this[i];
return tt;
public enum MType
mType1 = 0,
mType2,
mType3,
mType4
class CompareRisedTime : IComparer<String>
public int Compare(String a, String b)
FileInfo f1 = new FileInfo(a);
FileInfo f2 = new FileInfo(b);
if (f1.CreationTime > f2.CreationTime) return 1;
if (f1.CreationTime < f2.CreationTime) return -1;
return 0;
调用
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MicroLog
public partial class Form1 : Form
public Form1()
InitializeComponent();
private void Form1_Load(object sender, EventArgs e)
MLog log1 = new MLog();
log1.convert.LogHead = "[Time] [Mark] -ThrID:ThrName- [Type] Msg"; // 每个日志文件第一行都会输出
log1.convert.LogName = "firstTest.log";
log1.convert.PathLayout = "D:\\\\MLog\\\\Log1_yyy.MM.dd";
log1.convert.LogLayout = "[time] [mark] -thrid:thrname- [type] msg";
log1.convert.LogMark = "mark1";
log1.convert.IsEnableLogHead = true;
log1.convert.IsStableName = false; // 是否固定文件名,固定:只有一个文件,达到最大文件大小后悔删除源文件
log1.thr.IsEnable = true;
log1.thr.Name = "main";
MLog log2 = new MLog();
log2.convert.LogName = "secondTest.log";
log2.convert.PathLayout = "D:\\\\MLog\\\\Log2_yyy.MM.dd";
log2.convert.LogLayout = "[time] [mark] -thrid:thrname- [type] msg";
log2.convert.LogMark = "mark2";
log2.thr.IsEnable = false;
log2.thr.Name = "222";
log2.filter.IsEnable = true;
//log2.filter.ExThr = "2";
log2.filter.ExMsg = "10";
//log2.filter.EnableLog(MType.mType1, false);
Manager.AddLog("log1", log1);
Manager.AddLog("log2", log2);
Manager.MaxLogCount = 100;
Manager.MaxLogSize = 1024 * 1024 * 10;
private void button1_Click(object sender, EventArgs e)
Stopwatch sw = new Stopwatch();
MLog log = Manager.GetLog("log1");
sw.Restart();
for (int i = 0; i < 1000000; i++)
log.Write("firstLog 打两块就到啦世纪东方啦收到啦就是到了看静安寺多放辣椒撒冷的发几款 " + i.ToString("D10"));
long l = sw.ElapsedMilliseconds;
double n1 = l / 1000.0;
double n2 = l / 100.0;
MessageBox.Show("TotlaTime=" + n1.ToString() + " s\\n Ave 10000 Item = " + n2.ToString("F2") + " ms");
private void button2_Click(object sender, EventArgs e)
MLog log1 = Manager.GetLog("log1");
MLog log2 = Manager.GetLog("log2");
log1.transTo.Add(log2); // log1 的日志会流转给 log2
log1.Write("firstLog 打两块就到啦世纪东方啦收到啦就是到了看静安寺多放辣椒撒冷的发几款 10");
log1.Write("firstLog 打两块就到啦世纪东方啦收到啦就是到了看静安寺多放辣椒撒冷的发几款 11");
log1.transTo.Remove(log2);
以上是关于C#日志记录工具 MicroLog的主要内容,如果未能解决你的问题,请参考以下文章