/// <summary>
/// 日志操作,实现写日志、备份与回删日志
///
/// 使用实例:
/// LogOperator logOperator=new LogOperator();
/// logOperator.WriteOperationLog(category,msg);
///
/// 可以对日志文件大小、日志文件夹大小、日志文件名、日志保存路径进行设置
/// 自动备份、自动回删日志文件
/// 支持无阻塞的异步方式写操作日志(logOperator.WriteOperationLogAsync(int level,category,msg))
///
/// Charley
/// 2013/8/21
/// </summary>
public class LogOperator
{
private const int LOG_FILE_SIZE = 100; //log file size 100(KB)
private const int LOG_DIR_SIZE = 10; //log directory size 10(M)
private const string LOG_FILE_NAME = "log";
private const string LOG_PATH = "log";
private const string LOG_NAME = "XJLogOperator";
private delegate void WriteLogDelegate(LogMode level, string category, string msg); //利用委托异步写操作日志
private WriteLogDelegate mWriteLogDelegate;
#region Properties
private int mLogFileMaxSize;
/// <summary>
/// 单个日志文件的大小,单位KB
/// </summary>
public int LogFileMaxSize
{
set { mLogFileMaxSize = value; }
}
private int mLogDirMaxSize;
/// <summary>
/// 日志文件夹总大小,单位MB
/// </summary>
public int LogDirMaxSize
{
set { mLogDirMaxSize = value; }
}
private string mLogFileName;
/// <summary>
/// 日志文件名
/// </summary>
public string LogFileName
{
get { return mLogFileName; }
set { mLogFileName = value; }
}
private string mLogPath;
/// <summary>
/// 日志路径(相对路径或绝对路径)
/// </summary>
public string LogPath
{
get { return mLogPath; }
set
{
if (!Path.IsPathRooted(value))
{
mLogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, value);
}
else
{
mLogPath = value;
}
}
}
private string mErrorMsg;
/// <summary>
/// 操作错误消息
/// </summary>
public string ErrorMsg
{
get { return mErrorMsg; }
}
private string mLogName;
/// <summary>
/// 日志名称
/// </summary>
public string LogName
{
set { mLogName = value; }
}
private LogMode mLogMode;
/// <summary>
/// 日志级别
/// </summary>
public LogMode LogMode
{
get { return mLogMode; }
set { mLogMode = value; }
}
#endregion
/// <summary>
/// 使用默认值创建日志实例
/// </summary>
public LogOperator()
{
mWriteLogDelegate = new WriteLogDelegate(WriteOperationLogWithoutReturn);
mLogMode = LogMode.General;
mLogName = LOG_NAME;
mLogFileMaxSize = LOG_FILE_SIZE;
mLogDirMaxSize = LOG_DIR_SIZE;
mLogFileName = LOG_FILE_NAME;
mLogPath = LOG_PATH;
if (!Path.IsPathRooted(mLogPath))
{
mLogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, mLogPath);
}
}
/// <summary>
/// 指定日志名称
/// </summary>
/// <param name="log_name"></param>
public LogOperator(string log_name)
: this()
{
mLogName = log_name;
}
/// <summary>
/// 指定日志文件大小及日志文件夹总大小
/// </summary>
/// <param name="file_size"></param>
/// <param name="dir_size"></param>
public LogOperator(int file_size, int dir_size)
: this()
{
mLogFileMaxSize = file_size;
mLogDirMaxSize = dir_size;
}
/// <summary>
/// 指定日志文件名及路径
/// </summary>
/// <param name="file_name"></param>
/// <param name="file_path"></param>
public LogOperator(string file_name, string file_path)
: this()
{
mLogFileName = file_name;
mLogPath = file_path;
}
/// <summary>
/// 指定日志文件大小、日志文件夹总大小和日志文件名、日志路径
/// </summary>
/// <param name="file_size"></param>
/// <param name="dir_size"></param>
/// <param name="file_name"></param>
/// <param name="file_path"></param>
public LogOperator(int file_size, int dir_size, string file_name, string file_path)
: this(file_size, dir_size)
{
mLogFileName = file_name;
mLogPath = file_path;
}
/// <summary>
/// 指定日志名称、日志文件大小、日志文件夹总大小和日志文件名、日志路径
/// </summary>
/// <param name="log_name"></param>
/// <param name="file_size"></param>
/// <param name="dir_size"></param>
/// <param name="file_name"></param>
/// <param name="file_path"></param>
public LogOperator(string log_name, int file_size, int dir_size, string file_name, string file_path)
: this(log_name)
{
mLogFileMaxSize = file_size;
mLogDirMaxSize = dir_size;
mLogFileName = file_name;
mLogPath = file_path;
}
/// <summary>
/// 写操作日志
/// </summary>
/// <param name="mode">日志级别</param>
/// <param name="category">类别</param>
/// <param name="msg">消息</param>
/// <returns></returns>
public bool WriteOperationLog(LogMode mode, string category, string msg)
{
try
{
if ((mode & mLogMode) == 0) { return true; }
string dir = string.Empty;
string path = string.Empty;
if (string.IsNullOrEmpty(mLogPath) || string.IsNullOrEmpty(mLogFileName))
{
mErrorMsg = "Log file name or log file path is empty.";
return false;
}
if (Path.IsPathRooted(mLogPath))
{
dir = mLogPath;
}
else
{
dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, mLogPath);
}
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
addDirPower(dir, "Everyone", "FullControl");
}
DateTime now = DateTime.Now;
path = Path.Combine(dir, string.Format("{0}{1}.txt", mLogFileName, now.ToString("yyyMMdd")));
FileStream f;
f = new FileStream(path, FileMode.Append);
StreamWriter r = new StreamWriter(f);
if (category.Length <= 8)
{
category = category + "\t";
}
string formatstr = string.Format("{0}\t{1}\t{2}\t{3}", (LogMode)mode, DateTime.Now.ToString("HH:mm:ss"), category, msg);
r.WriteLine(formatstr);
r.Flush();
r.Close();
if (!BackupLog(path)
|| !DeleteLog())
{
return false;
}
return true;
}
catch (Exception ex)
{
mErrorMsg = ex.ToString();
return false;
}
}
/// <summary>
/// 记录Debug日志
/// </summary>
/// <param name="category">类别</param>
/// <param name="msg">消息</param>
/// <returns></returns>
public bool LogDebug(string category, string msg)
{
return WriteOperationLog(LogMode.Debug, category, msg);
}
/// <summary>
/// 记录Info日志
/// </summary>
/// <param name="category">类别</param>
/// <param name="msg">消息</param>
/// <returns></returns>
public bool LogInfo(string category, string msg)
{
return WriteOperationLog(LogMode.Info, category, msg);
}
/// <summary>
/// 记录Warn日志
/// </summary>
/// <param name="category">类别</param>
/// <param name="msg">消息</param>
/// <returns></returns>
public bool LogWarn(string category, string msg)
{
return WriteOperationLog(LogMode.Warn, category, msg);
}
/// <summary>
/// 记录Error日志
/// </summary>
/// <param name="category">类别</param>
/// <param name="msg">消息</param>
/// <returns></returns>
public bool LogError(string category, string msg)
{
return WriteOperationLog(LogMode.Error, category, msg);
}
/// <summary>
/// 记录Fatal日志
/// </summary>
/// <param name="category">类别</param>
/// <param name="msg">消息</param>
/// <returns></returns>
public bool LogFatal(string category, string msg)
{
return WriteOperationLog(LogMode.Fatal, category, msg);
}
/// <summary>
/// 以无阻塞的异步方式写操作日志
/// </summary>
/// <param name="mode">日志级别</param>
/// <param name="category"></param>
/// <param name="msg"></param>
public void WriteOperationAsync(LogMode mode, string category, string msg)
{
mWriteLogDelegate.BeginInvoke(mode, category, msg, null, null);
}
private void WriteOperationLogWithoutReturn(LogMode mode, string category, string msg)
{
WriteOperationLog(mode, category, msg);
}
/// <summary>
/// 备份日志文件
/// </summary>
/// <returns></returns>
private bool BackupLog(string filePath)
{
try
{
if (!System.IO.File.Exists(filePath))
{
mErrorMsg = string.Format("File not exist.\t{0}", filePath);
return false;
}
System.IO.FileInfo file = new FileInfo(filePath);
long size = file.Length;
string path = string.Empty;
if (size > mLogFileMaxSize * 1024)
{
int i = 1;
do
{
path = filePath.Substring(0, filePath.Length - 4) + i.ToString("000") + ".txt";
i++;
}
while (System.IO.File.Exists(path));
file.CopyTo(path);
file.Delete();
WriteOperationLog(LogMode.Warn, "BackupLog", string.Format("Backup log {0} successful.", path));
}
return true;
}
catch (Exception ex)
{
mErrorMsg = string.Format("Backup log fail.\t{0}", ex.Message);
WriteOperationLog(LogMode.Warn, "BackupLog", string.Format("Backup log fail.\t{0}", ex.Message));
return false;
}
}
/// <summary>
/// 回删日志文件
/// </summary>
/// <returns></returns>
private bool DeleteLog()
{
try
{
string dir = string.Empty;
if (Path.IsPathRooted(mLogPath))
{
dir = mLogPath;
}
else
{
dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, mLogPath);
}
System.IO.DirectoryInfo logdir = new DirectoryInfo(dir);
if (getDirSize(logdir.FullName) <= mLogDirMaxSize * 1024 * 1024)
{
return true;
}
while (getDirSize(logdir.FullName) > mLogDirMaxSize * 1024 * 1024 / 2)
{
List<FileInfo> files = logdir.GetFiles().OrderBy(f => f.LastWriteTime).ToList();
if (files.Count > 0)
{
if (!deleteFile(files[0]))
{
WriteOperationLog(LogMode.Warn, "DeleteLogFile", string.Format("Delete log file {0} fail.", files[0].FullName));
}
}
}
}
catch (Exception ex)
{
mErrorMsg = string.Format("Delete log fail.\t{0}", ex.Message);
WriteOperationLog(LogMode.Warn, "DeleteLogFile", string.Format("Delete log file fail!\t{0}", ex.Message));
return false;
}
return true;
}
private long getDirSize(string path)
{
long size = 0;
try
{
DirectoryInfo dir = new DirectoryInfo(path);
for (var i = 0; i < dir.GetFiles().Count(); i++)
{
size = size + dir.GetFiles()[i].Length;
}
}
catch (Exception ex)
{
WriteOperationLog(LogMode.Warn, "GetDirSize", string.Format("Get directory size fail.\t{0}", ex.Message));
size = -1;
}
return size;
}
private bool deleteFile(FileInfo file)
{
try
{
File.Delete(file.FullName);
return true;
}
catch (Exception ex)
{
WriteOperationLog(LogMode.Warn, "DeleteFile", string.Format("Delete file fail.\t{0}", ex.Message));
return false;
}
}
private void addDirPower(string dirName, string username, string power)
{
DirectoryInfo dirInfo = new DirectoryInfo(dirName);
if ((dirInfo.Attributes & FileAttributes.ReadOnly) != 0)
{
dirInfo.Attributes = FileAttributes.Normal;
}
//取得访问控制列表
DirectorySecurity dirSecurity = dirInfo.GetAccessControl();
switch (power)
{
case "FullControl":
dirSecurity.AddAccessRule(new FileSystemAccessRule(username, FileSystemRights.FullControl, InheritanceFlags.ContainerInherit, PropagationFlags.InheritOnly, AccessControlType.Allow));
break;
case "ReadOnly":
dirSecurity.AddAccessRule(new FileSystemAccessRule(username, FileSystemRights.Read, AccessControlType.Allow));
break;
case "Write":
dirSecurity.AddAccessRule(new FileSystemAccessRule(username, FileSystemRights.Write, AccessControlType.Allow));
break;
case "Modify":
dirSecurity.AddAccessRule(new FileSystemAccessRule(username, FileSystemRights.Modify, AccessControlType.Allow));
break;
}
dirInfo.SetAccessControl(dirSecurity);
}
}
/// <summary>
/// 日志级别,可任意组合
/// </summary>
[Flags]
public enum LogMode
{
/// <summary>
/// 调试
/// </summary>
Debug = 0x01,
/// <summary>
/// 信息
/// </summary>
Info = 0x02,
/// <summary>
/// 警告
/// </summary>
Warn = 0x04,
/// <summary>
/// 错误
/// </summary>
Error = 0x08,
/// <summary>
/// 致命
/// </summary>
Fatal = 0x16,
/// <summary>
/// 所有消息
/// </summary>
All = Debug | Info | Warn | Error | Fatal,
/// <summary>
/// 一般(默认)
/// </summary>
General = Info | Warn | Error | Fatal
}