十全十美的文本日志类
2012-02-27 10:09 白面青铜 阅读(595) 评论(2) 编辑 收藏 举报十全十美的文本日志类包含几个特点:
1. 高性能,不卡不拖主进程.
2. 多线程支持.
3. 多进程支持.
4. 灵活分类.
5. 按不同大小,时间性要求存储.
6. 备份清理机制.
1. 高性能,不卡不拖主进程.
绝对不能因写日志影响到主程序性能.
2. 多线程支持.
多线程不冲突,不漏记录.
3. 多进程支持.
多线程不冲突,不漏记录.
4. 灵活分类.
Error,Warn,Info......
5. 按不同大小,时间性要求存储.
可以按每5M写一个文件,按照每小时写一个文件,每天写一个文件.
6. 备份清理机制.
不能经常有人找到你说服务器D盘被日志写破了,不能经常有人找到你说怎么昨天的日志没有拉?
下面的200多行的代码经过测试,仅是我写的一个完美分布式日志系统的一个独立写日志的类.请各大家采用吧,完全开源,敬请保留我的签名.
1. 10个进程,每个进程最大线程数异步写500万条,一共5000万条 没有sleep的写完一条不漏.
2. 日PV过亿的高压网上长期运行,不漏不卡不拖不带.
附代码: Cofnig配置读取类自己写.
using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Threading;
using System.Collections;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.Specialized;
/// <summary>
/// 作者: cl.carl wopani@gmail.com
/// 日期:2010-06-02
/// 完全开源,敬请保留我的签名.
/// </summary>
public class Log
{
static Encoding encode = Encoding.GetEncoding("GB2312");
static string logPath = Config.LogPath;
static string bakPath = Config.LogBakPath;
static string logName = Config.LogName;
static double bakLength = Config.LogBakLength;
static int timedLog = Config.TimedLog;
static int logAction = Config.LogAction;// (int)(LogAction.Error|LogAction.Info|LogAction.Warn);
static object o = new object();
static int refreshTime = Config.LogRefreshTime;
static double clearHour = Config.LogClearHour;
static DateTime bakTime = DateTime.Now;
static DateTime clearTime = DateTime.Now;
static Dictionary<LogAction, StringBuilder> logs = null;
static Queue<KeyValuePair<LogAction, string>> logStack = null;
static readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
static readonly ReaderWriterLockSlim frwLock = new ReaderWriterLockSlim();
static Mutex mut = null;
static Thread proThread = null;
static Log()
{
lock (o)
{
Init();
}
}
static void Init()
{
try
{
if (Config.IsMutex)
mut = new Mutex(false, "LogMutex");
if (logAction < 0) return;
FileInfo f = new FileInfo(logPath);
if (!f.Directory.Exists) f.Directory.Create();
Array acs = Enum.GetValues(typeof(LogAction));
logs = new Dictionary<LogAction, StringBuilder>();
foreach (LogAction ac in acs)
{
if (!logs.ContainsKey(ac)) logs.Add(ac, new StringBuilder());
if ((logAction & (int)ac) != 0 && ac != LogAction.Write)
Directory.CreateDirectory(logPath + ac.ToString());
}
logStack = new Queue<KeyValuePair<LogAction, string>>();
if (logAction > -1 && (proThread == null || !proThread.IsAlive))
{
proThread = new Thread(WriteLog);
proThread.IsBackground = true;
proThread.Start();
}
}
catch (Exception e) { e = null; }
}
public static void Write(string msg)
{
if (logAction >= 0)
PushLog(LogAction.Write, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss::fff") + ":" + msg);
}
public static void Write(LogAction action, string msg)
{
if (logAction >= 0 && (logAction & (int)action) != 0)
PushLog(action, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss::fff") + ":" + msg);
}
public static void Error(string msg)
{
if (logAction > -1 && (logAction & (int)LogAction.Error) != 0)
PushLog(LogAction.Error, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss::fff") + ":" + msg);
}
public static void Info(string msg)
{
if (logAction > -1 && (logAction & (int)LogAction.Info) != 0)
PushLog(LogAction.Info, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss::fff") + ":" + msg);
}
public static void Warn(string msg)
{
if (logAction > -1 && (logAction & (int)LogAction.Warn) != 0)
PushLog(LogAction.Warn, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss::fff") + ":" + msg);
}
private static void PushLog(LogAction action, string msg)
{
try
{
if (logStack.Count > Config.SecurityMemNumber) return;
KeyValuePair<LogAction, string> log = new KeyValuePair<LogAction, string>(action, msg);
rwLock.EnterWriteLock();
logStack.Enqueue(log);
}
catch (Exception e) { e = null; }
finally { rwLock.ExitWriteLock(); }
}
static void WriteLog()
{
while (true)
{
Thread.Sleep(refreshTime);
int count = 0;
try
{
rwLock.EnterWriteLock();
count = logStack.Count;
int i = count;
while (i > 0)
{
KeyValuePair<LogAction, string> log = logStack.Dequeue();
i--;
if (null != log.Value)
{
logs[log.Key].AppendLine(log.Value);
}
}
}
catch (Exception e) { e = null; }
finally
{
rwLock.ExitWriteLock();
}
if (count > 0)
{
try
{
DateTime now = DateTime.Now;
foreach (LogAction ac in logs.Keys)
{
if ((logAction & (int)ac) != 0)
{
string path = logPath;
string bakDir = bakPath;
string act = (ac != LogAction.Write ? (ac.ToString() + "\\") : "");
bakDir += act;
path += act;
if (timedLog == 1)
path += now.ToString("yyyy-MM-dd HH-00-00") + ".txt";
else if (timedLog == 2)
path += now.ToString("yyyy-MM-dd 00-00-00") + ".txt";
else path += logName;
StringBuilder sb = logs[ac];
if (sb.Length > 0)
{
WriteMsg(sb.ToString(), path, Config.IsMutex);
sb.Length = 0;
BakLog(path, bakDir);
}
}
}
}
catch (Exception e) { e = null; }
}
}
}
public static void WriteMsg(object msg, string logPath, bool isMutex)
{
if (!isMutex)
{
try
{
frwLock.EnterWriteLock();
WriteMsg(msg, logPath);
}
catch (Exception e) { e = null; Init(); }
finally
{
frwLock.ExitWriteLock();
}
return;
}
try
{
mut.WaitOne();
WriteMsg(msg, logPath);
}
catch (Exception e) { e = null; Init(); }
finally
{
mut.ReleaseMutex();
}
}
public static void WriteMsg(object msg, string logPath)
{
try
{
using (StreamWriter sw = new StreamWriter(logPath, true, encode))
{
sw.Write(msg);
}
}
catch (Exception e) { e = null; Init(); }
}
public static void DeleteExpirateFiles(string dir, DateTime expDate)
{
List<string> files = Directory.GetFileSystemEntries(dir, "*.txt").ToList().Where(x => ((File.GetLastWriteTime(x) < expDate))).ToList();
foreach (string f in files)
{
File.Delete(f);
}
}
static void BakLog(string logPath, string backDir)
{
try
{
DateTime now = DateTime.Now;
TimeSpan span = now - bakTime;
if (span.Minutes > 5) //每5分钟备份一次
{
FileInfo f = new FileInfo(logPath);
if (f.Length > bakLength)
{
string path = backDir + now.ToString("yyyy-MM-dd HH-mm-ss") + ".txt";
f.MoveTo(path);
bakTime = DateTime.Now;
}
}
span = now - clearTime;
if (span.Minutes > 10) //每10分钟清理一次.
{
DirectoryInfo dirInfo = new DirectoryInfo(backDir);
DateTime expDate = now.AddHours(-clearHour);
DeleteExpirateFiles(backDir, expDate);
clearTime = DateTime.Now;
}
}
catch (Exception e) { e = null; Init(); }
}
}
/// <summary>
/// 根据需要扩展日志类型
/// </summary>
[Flags]
public enum LogAction
{
Write = 1,
Info = 2,
Error = 4,
Warn = 8,
Get = 16,
Set = 32,
Seria = 64
}