一个比较好用的日志记录系统
首先声明,没有按照AOP的方法来做,现在还是显示调用的。
using System;
using System.IO;
using System.Collections;
using System.Runtime.InteropServices;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.BZip2;
using ICSharpCode.SharpZipLib.Checksums;
namespace Genersoft.Focus
{
[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct LogFileHeader
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=19)]public string HeaderSignature;//FocusTools.LogFile,前面是0x12
public int EntryListNumber;
}
[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct LogFileEntryList
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=9)]public string CreateDate;//8位,20031201格式
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=10)]public string CreateTime;//9位,140324111格式
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=1024)]public string AdditionalInformation;//调用着传递来的日志信息
public int AdditionalInformationType;//
public int EntryNumber;//Entry的个数
}
[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct LogFileEntry
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=1024)]public string FileName;//calling的文件名
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=1024)]public string MethodName;//calling的方法名
public int FileLineNumber;//calling的行号位置
}
public enum LogType
{
Info,Warning,Error,Exception,Database
}
public class Log
{
private static LogFileHeader lfh;
private static LogFileEntryList lfel;
private static LogFileEntry[] lfe;
private static System.Text.StringBuilder sb = null;
private static Log instance = null;
private const string LogFileName = @"c:\focus.log";
private Log()
{
lfh = new LogFileHeader();
lfh.HeaderSignature = "FocusTools.LogFile";
sb = new System.Text.StringBuilder();
lfel = new LogFileEntryList();
}
public static Log Instance
{
get{if(null == instance)instance = new Log();return instance;}
}
public void Write(string s,LogType t)
{
System.DateTime dt = System.DateTime.Now;
lfel = new LogFileEntryList();
lfel.AdditionalInformation = s;
lfel.CreateDate = String.Format("{0:d4}{1:d2}{2:d2}",dt.Year,dt.Month,dt.Day);
lfel.CreateTime = String.Format("{0:d2}{1:d2}{2:d2}{3:d3}",dt.Hour,dt.Minute,dt.Second,dt.Millisecond);
//查找当前进程内所有线程的调用堆栈信息,怎么做?
System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(true);
lfel.EntryNumber = st.FrameCount-1;
lfel.AdditionalInformationType = Convert.ToInt32(t);
lfe = new LogFileEntry[st.FrameCount-1];
for(int i=st.FrameCount-1;i>0;i--)//最后一个是Write这个方法本身,所以就不写入了。
{
lfe[st.FrameCount-1-i] = new LogFileEntry();
lfe[st.FrameCount-1-i].FileName = st.GetFrame(i).GetFileName();
lfe[st.FrameCount-1-i].MethodName = st.GetFrame(i).GetMethod().Name;
lfe[st.FrameCount-1-i].FileLineNumber = st.GetFrame(i).GetFileLineNumber();
}
Flush();
}
private void Flush()
{
int entryListNum = 0;
FileStream fs = new FileStream(LogFileName,FileMode.OpenOrCreate,FileAccess.ReadWrite);
BinaryWriter bw = new BinaryWriter(fs);
BinaryReader br = new BinaryReader(fs);
int size,size2;
try
{
bw.Write(lfh.HeaderSignature);
br.BaseStream.Seek(19,SeekOrigin.Begin);
try
{
entryListNum = br.ReadInt32()+1;
}
catch
{
entryListNum = 1;
}
bw.BaseStream.Seek(19,SeekOrigin.Begin);
bw.Write(entryListNum);
bw.BaseStream.Seek(0,SeekOrigin.End);
size = Marshal.SizeOf(lfel);
size2 = Marshal.SizeOf(lfe[0]);
byte[] input = new byte[size+size2*lfel.EntryNumber];
IntPtr p = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(lfel,p,true);
byte[] bEntryList = new byte[size];
Marshal.Copy(p,bEntryList,0,size);
//bw.Write(bEntryList);
Marshal.FreeHGlobal(p);
bEntryList.CopyTo(input,0);
for(int i=0;i<lfel.EntryNumber;i++)
{
IntPtr p2 = Marshal.AllocHGlobal(size2);
Marshal.StructureToPtr(lfe[i],p2,true);
byte[] bEntry = new byte[size2];
Marshal.Copy(p2,bEntry,0,size2);
// bw.Write(bEntry);
Marshal.FreeHGlobal(p2);
bEntry.CopyTo(input,bEntryList.Length+i*size2);
}
// System.Diagnostics.Trace.WriteLine(System.Text.Encoding.Default.GetString(input));
Deflater def = new Deflater(Deflater.BEST_COMPRESSION);
if(def.IsNeedingInput)def.SetInput(input);
def.Finish();
// System.Diagnostics.Trace.WriteLine(System.Text.Encoding.Default.GetString(input));
byte[] dbuf = new byte[input.Length];
int dsize = 0;
dsize = def.Deflate(dbuf);
bw.Write(dsize);//压缩后的大小
bw.Write(input.Length);//压缩前的大小
bw.Write(dbuf,0,dsize);
}
catch(Exception ex)
{
throw ex;
}
finally
{
if(null != br)br.Close();
if(null != bw)bw.Close();
if(null != fs)fs.Close();
}
}
}
public class LogFileEventArgs
{
public LogFileEventArgs(int curvalue,int maxvalue)
{
this.curValue = curvalue;
this.maxValue = maxvalue;
}
private int curValue = 0;
private int maxValue = 0;
public int CurValue{get{return curValue;}set{curValue = value;}}
public int MaxValue{get{return maxValue;}set{maxValue = value;}}
}
public delegate void LogFileEventHandler(object sender, LogFileEventArgs e);
}
一共有两点需要注意:
1、堆栈信息是从StackTrace中读出来的。发布assembly的时候,要把同版本号的pdb文件也要复制上。否则,line和column信息无法读出来。
2、对于structure的处理,采用了一点IntPtr的方法。