一个比较好用的日志记录系统

首先声明,没有按照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的方法。

原文地址:https://www.cnblogs.com/juqiang/p/10108.html