最近在做一个asp.net的项目要求实现一个记录日志的功能,本来极力推荐使用Log4net的,开源的配置又方便实现又多样,但领导考虑到将来这个项目要做成产品,担心它的开放政策,找了一大堆理由不同意使用,无奈自己来一个吧
要求:1,服务器不重启的前提下随时更改记录日志的级别
2.考虑并发的情况
直接上代码吧
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Configuration;
using System.Web;
using System.Text.RegularExpressions;
using System.Data;
using System.Threading;
namespace WebApplication1
{
//日志信息级别类
public enum Level {
/// <summary>
/// 调试信息
/// </summary>
Debug = 3,
/// <summary>
/// 警告信息
/// </summary>
Warn = 2,
/// <summary>
/// 错误信息
/// </summary>
Error = 1
};
//日志信息类
public class Logs
{
#region 字段
private Level level; //错误级别
private string datetime; //发生时间
private string source; //发生地址
private string message; //错误内容
#endregion
#region 构造函数
/// <summary>
/// 对象有参构造函数
/// </summary>
/// <param name="lvl">错误级别(枚举值)</param>
/// <param name="datetime">发生时间</param>
/// <param name="source">发生地址</param>
/// <param name="message">错误内容</param>
public Logs(Level level, string datetime, string source, string message)
{
this.level = level;
this.datetime = datetime;
this.source = source;
this.message = message;
}
#endregion
#region 属性
public Level Level
{
get { return level; }
set { level = value; }
}
public string Datetime
{
get { return datetime; }
set { datetime = value; }
}
public string Source
{
get { return source; }
set { source = value; }
}
public string Message
{
get { return message; }
set { message = value; }
}
#endregion
}
//日志相关配置类
public static class ConstDeclare
{
/// <summary>
/// 写日志间隔时间
/// </summary>
private static int witeTime = 10000;
/// <summary>
/// 日志错误级别
/// </summary>
private static string logLevel = Level.Debug.ToString();
/// <summary>
/// 保留日志文件数
/// </summary>
private static int logsCount = 10;
/// <summary>
///日志文件大小
/// </summary>
private static int logSize = 1024*1024;
/// <summary>
/// 日志文件主目录
/// </summary>
public static string filePath = "SystemLog";
/// <summary>
/// 错误数
/// </summary>
private static int excptionCount = 0;
public static int WiteTime
{
get { return witeTime; }
set { if (value > 0) witeTime = value * 60000; }
}
public static string LogLevel
{
get { return logLevel; }
set
{
if (value.Equals(Level.Debug.ToString()) || value.Equals(Level.Error.ToString()) || value.Equals(Level.Warn.ToString()))
{
logLevel = value;
}
}
}
public static int LogsCount
{
get { return logsCount; }
set { if (value > 0) logsCount = value; }
}
public static int LogSize
{
get { return logSize; }
set { if (value > 0) logSize = value*1024; }
}
public static string FilePath
{
get { return filePath; }
set { filePath = value; }
}
public static int ExcptionCount
{
get { return excptionCount; }
set { excptionCount = value; }
}
}
//文档处理类
public class ExceptionManager
{
//定义队列集合
private static readonly Queue<Logs> docQueue = new Queue<Logs>();
//添加文档
/// <summary>
/// 添加异常信息
/// </summary>
/// <param name="level">错误级别(枚举值)</param>
/// <param name="dataTime">出错时间</param>
/// <param name="source">发生地址</param>
/// <param name="message">错误内容</param>
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
public static void AddException(Level level, string dataTime, string source, string message)
{
Logs logs = new Logs(level, dataTime, source, message);
if ((int)level <= (int)(Level)Enum.Parse(typeof(Level), ConstDeclare.LogLevel))
{
//从队列一端插入内容
docQueue.Enqueue(logs);
ConstDeclare.ExcptionCount++; //错误数
if (IsDocumentAvailable)
{
GetException();
}
}
}
//只读属性,确定队列中是不是还有元素
public static bool IsDocumentAvailable
{
get
{
return docQueue.Count > 0;
}
}
//读取文档 并 写入
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
public static void GetException()
{
Logs ex = null;
if (docQueue.Count > 0)
{
ex = docQueue.Dequeue();
SaveException(ex);
}
}
//间隔时间执行某动作 (写文件)
public static void SaveException(Logs e)
{
//指定日志文件的目录
string fileLogPath = AppDomain.CurrentDomain.BaseDirectory + ConstDeclare.FilePath;
//错误日志的记录地址(每天产生一个日志文件)
string fileLogName = DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".log";
try
{
if (!Directory.Exists(fileLogPath))
{
Directory.CreateDirectory(fileLogPath);
}
DirectoryInfo info = new DirectoryInfo(fileLogPath);
//FileInfo[] existingFiles = info.GetFiles(string.Format("{0}*{1}", fileNameWithoutExtension, extension));
FileInfo[] existingFiles = info.GetFiles();
List<FileInfo> deleteFiles = new List<FileInfo>();
FileInfo finfo;
//Regex regex = new Regex(string.Format(@"{0}\.(.+).{1}", fileNameWithoutExtension, extension));
if (existingFiles.Length > 0)
{
for (int index = 0; index < existingFiles.Length; index++)
{
//Match sequenceMatch = regex.Match(existingFiles[index].FullName);
//if (sequenceMatch.Success)
{
deleteFiles.Add(existingFiles[index]);
}
}
deleteFiles.Sort((x, y) => x.CreationTime < y.CreationTime ? 1 : x.CreationTime > y.CreationTime ? -1 : 0);
//最近创建的日志文件
finfo = deleteFiles[0];
if (finfo.Length >= ConstDeclare.LogSize)
{
for (int index = ConstDeclare.LogsCount; index < deleteFiles.Count; index++)
{
try
{
deleteFiles[index].Delete();
}
catch
{
}
}
finfo = new FileInfo(string.Format("{0}\\{1}", fileLogPath, fileLogName));
}
}
else
{
finfo = new FileInfo(string.Format("{0}\\{1}", fileLogPath, fileLogName));
}
//string directory = Path.GetDirectoryName(finfo.FullName);
//string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(finfo.FullName);
//string extension = Path.GetExtension(finfo.FullName);
//创建只写文件流
using (FileStream fs = finfo.OpenWrite())
{
//根据上面创建的文件流创建写数据流
StreamWriter strwriter = new StreamWriter(fs);
//设置写数据流的起始位置为文件流的末尾
strwriter.BaseStream.Seek(0, SeekOrigin.End);
strwriter.WriteLine("[发生时间]:" + e.Datetime.ToString());
strwriter.WriteLine("[错误级别]:" + e.Level.ToString());
strwriter.WriteLine("[发生地址]:" + e.Source);
//strwriter.WriteLine("[目标网站]:" + e.TargetSite);
strwriter.WriteLine("[错误内容]:" + e.Message);
//strwriter.WriteLine("[帮助链接]:" + e.HelpLink);
//strwriter.WriteLine("[堆栈跟踪]:" + e.StackTrace);
//strwriter.WriteLine("[内部异常]:" + e.InnerException);
strwriter.WriteLine("[记录时间]:" + DateTime.Now.ToString());
//写入间隔符
strwriter.WriteLine("************************************************************************");
strwriter.WriteLine("\n");
//清空缓冲区内容,并把缓冲区内容写入基础流
strwriter.Flush();
//关闭写数据流
strwriter.Close();
fs.Close();
}
}
catch (Exception ex)
{
AddException(Level.Debug, DateTime.Now.ToString(), ex.TargetSite.ToString()+ex.Source+ex.StackTrace, ex.Message);
}
}
#region 写日志的线程操作
public static void Start()
{
// 参数 : public delegate void ThreadStart();
//Thread thread = new Thread(DoThreadFunc);
//thread.Start();
System.Timers.Timer tm = new System.Timers.Timer();
tm.Elapsed += new System.Timers.ElapsedEventHandler(tm_Elapsed);
tm.Interval = 10000;
tm.Enabled = true;
}
static void tm_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
//无限循环读取,只要队列中有内容,这条线程就读出来
//while (true)
{
while(IsDocumentAvailable)
{
GetException();
}
//Thread.CurrentThread.Join(1000);
//Thread.Sleep(ConstDeclare.WiteTime);
}
}
public static void DoThreadFunc()
{
//无限循环读取,只要队列中有内容,这条线程就读出来
while (true)
{
while (IsDocumentAvailable)
{
GetException();
Thread.Sleep(10000);
}
//Thread.CurrentThread.Join(1000);
//Thread.Sleep(ConstDeclare.WiteTime);
}
}
#endregion
#region 获取日志信息
public static DataSet GetLogs()
{
DataSet ds = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add("id", typeof(System.Int32));
dt.Columns.Add("logdate", typeof(System.DateTime));
dt.Columns.Add("level", typeof(System.String));
dt.Columns.Add("address", typeof(System.String));
dt.Columns.Add("message", typeof(System.String));
string filepath = AppDomain.CurrentDomain.BaseDirectory + ConstDeclare.FilePath;
try
{
if (Directory.Exists(filepath))
{
DirectoryInfo dinfo = new DirectoryInfo(filepath);
FileInfo[] finfo = dinfo.GetFiles();
List<FileInfo> linfo = new List<FileInfo>();
int rowid = 0;
for (int index = 0; index < finfo.Length; index++)
{
linfo.Add(finfo[index]);
}
linfo.Sort((x, y) => x.CreationTime < y.CreationTime ? 1 : x.CreationTime > y.CreationTime ? -1 : 0);
for (int i = 0; i < linfo.Count; i++)
{
StreamReader sr = new StreamReader(string.Format("{0}\\{1}", filepath, Path.GetFileName(linfo[i].FullName)), Encoding.UTF8);
DataRow dr = null;
while (!sr.EndOfStream)
{
string logline = sr.ReadLine();
string[] str = logline.Split(new string[] { "]:" }, StringSplitOptions.RemoveEmptyEntries);
if (!string.IsNullOrEmpty(logline) && str.Length > 0)
{
if (logline.Substring(1, 4).Equals("发生时间"))
{
//创建一个空行
rowid++;
dr = dt.NewRow();
dr["id"] = rowid;
dr["logdate"] = str.Length > 1 ? str[1] : "";
}
if (logline != null && logline.Substring(1, 4).Equals("错误级别") && dr != null)
{
dr["level"] = str.Length > 1 ? ConvertLevel(str[1]) : "";
}
if (logline != null && logline.Substring(1, 4).Equals("发生地址") && dr != null)
{
dr["address"] = str.Length > 1 ? str[1] : "";
}
if (logline != null && logline.Substring(1, 4).Equals("错误内容") && dr != null)
{
dr["message"] = str.Length > 1 ? str[1] : "";
//添加一行数据
dt.Rows.Add(dr);
}
}
}
sr.Close();
}
ds.Tables.Add(dt);
}
}
catch (Exception ex)
{
ExceptionManager.AddException(Level.Debug, DateTime.Now.ToString(), ex.TargetSite.ToString()+ex.Source+ex.StackTrace, ex.Message);
}
return ds;
}
public static string ConvertLevel(string level)
{
string leveldesc;
if (level.Equals(Level.Debug.ToString()))
{
leveldesc = "调试";
}
else if (level.Equals(Level.Error.ToString()))
{
leveldesc = "错误";
}
else
{
leveldesc = "警告";
}
return leveldesc;
}
#endregion
}
}
在Global.asax.cs页面调用
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
ExceptionManager.AddException(Level.Debug, DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss fff"), "Global.asax void A pplication_Start(object sender, EventArgs e)", "当应用程序开始时,启动一个定时器,用来定时执行任务AddException方 法记录错误");
ExceptionManager.Start();
}
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
Exception ex = Server.GetLastError().GetBaseException();
ExceptionManager.AddException(Level.Debug, DateTime.Now.ToString(), ex.Source, ex.Message);
Server.ClearError();
}
}