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

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

posted @   鞠强  阅读(2225)  评论(6编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?

hello

world

点击右上角即可分享
微信分享提示