123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace VCommon.Logging
- {
- public class FileLogger : ILogger
- {
- // {logTo}\{level}\{level}_yyyyMMdd_{num:000}.log
- // [*]按时间创建
- // [*]续写
- // [*]按大小分卷
- // [*]自动删除旧文件
- public bool LogToConsole { get; set; }
- public int ByteSplit { get; }
- public int? PreserveDays { get; }
- public string LogTo { get; }
- protected enum Level
- {
- All,
- Debug,
- Trace,
- Info,
- Warn,
- Error,
- Fatal
- }
- private readonly Dictionary<Level, FileRoller> _rollers = new Dictionary<Level, FileRoller>();
- private readonly FileRoller _allRoller;
- private Task _cleanOldFileTask;
- private readonly Dictionary<string, DateTime> _files = new Dictionary<string, DateTime>();
- protected FileLogger(int mbSplit = 10, int? preserveDays = null, string logTo = null, bool enableAll = false)
- {
- //init env
- ByteSplit = mbSplit * 1024 * 1024;
- PreserveDays = preserveDays;
- LogTo = logTo ?? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
- if (enableAll)
- _allRoller = new FileRoller(this, Level.All);
- if (PreserveDays.HasValue)
- {
- if (Directory.Exists(logTo))
- {
- var files = Directory.GetFiles(LogTo, "*.log", SearchOption.AllDirectories);
- foreach (var file in files)
- {
- var fn = Path.GetFileNameWithoutExtension(file)?.Split('_');
- if (fn?.Length == 3)
- {
- var dt = DateTime.ParseExact(fn[1], "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None);
- _files[file] = dt;
- }
- }
- CleanOldFiles();
- }
- }
- }
- private void NewFileCreated(string fullPath, DateTime dt)
- {
- if (!PreserveDays.HasValue) return;
- lock (_files)
- {
- _files[fullPath] = dt;
- }
- }
- private void CleanOldFiles()
- {
- if (false == PreserveDays.HasValue) return;
- if (false == _cleanOldFileTask?.IsCompleted) return;
- var timeToDelete = DateTime.Now.Date.AddDays(-PreserveDays.Value);
- _cleanOldFileTask = Task.Factory.StartNew(() =>
- {
- KeyValuePair<string, DateTime>[] files;
- lock (_files)
- {
- files = _files.ToArray();
- }
- var filesToDelete = files.Where(p => p.Value < timeToDelete).Select(p => p.Key);
- foreach (var file in filesToDelete)
- {
- File.Delete(file);
- lock (_files)
- {
- _files.Remove(file);
- }
- }
- });
- }
- private void WriteLogInternal(Level level, string summary, object moreInfo)
- {
- lock (this)
- {
- try
- {
- CleanOldFiles();
- if (!_rollers.TryGetValue(level, out var roller)) roller = _rollers[level] = new FileRoller(this, level);
- var today = DateTime.Now;
- var formatted = FormatMessage(today, level, summary, moreInfo);
- roller.WriteLine(today, formatted);
- _allRoller?.WriteLine(today, formatted);
- if (LogToConsole)
- {
- Console.WriteLine(formatted);
- }
- }
- catch (Exception e)
- {
- System.Diagnostics.Debug.Print(e.ToString());
- System.Diagnostics.Debug.Print(summary);
- }
- }
- }
- protected virtual string FormatMessage(DateTime time, Level level, string summary, object moreInfo)
- {
- return $"{time:yyyy-MM-dd HH:mm:ss.fff} {level} {summary} {moreInfo}";
- }
- public void Debug(string summary, object moreInfo = null)
- {
- WriteLogInternal(Level.Debug, summary, moreInfo);
- }
- public void Trace(string summary, object moreInfo = null)
- {
- WriteLogInternal(Level.Trace, summary, moreInfo);
- }
- public void Info(string summary, object moreInfo = null)
- {
- WriteLogInternal(Level.Info, summary, moreInfo);
- }
- public void Warn(string summary, object moreInfo = null)
- {
- WriteLogInternal(Level.Warn, summary, moreInfo);
- }
- public void Error(string summary, object moreInfo = null)
- {
- WriteLogInternal(Level.Error, summary, moreInfo);
- }
- public void Fatal(string summary, object moreInfo = null)
- {
- WriteLogInternal(Level.Fatal, summary, moreInfo);
- }
- private class FileRoller
- {
- private readonly FileLogger _ctx;
- private readonly Level _level;
- private DateTime _fsInstanceDate;
- private int _rollNum;
- private FileStream _fsInstance;
- public FileRoller(FileLogger ctx, Level level)
- {
- _level = level;
- _ctx = ctx;
- }
- private void SwitchFile()
- {
- _fsInstance?.Close();
- var fullPath = Path.Combine(_ctx.LogTo, _level.ToString(), $"{_level}_{_fsInstanceDate:yyyyMMdd}_{_rollNum:000}.log");
- _fsInstance = File.Open(fullPath, FileMode.Append, FileAccess.Write, FileShare.Read);
- _ctx.NewFileCreated(fullPath, _fsInstanceDate);
- }
- private void EnsureFsInstance(DateTime dateTime)
- {
- var date = dateTime.Date;
- if (null != _fsInstance && _fsInstanceDate == date) return;
- _fsInstanceDate = date;
- //ensure dir
- var path = Path.Combine(_ctx.LogTo, _level.ToString());
- if (false == Directory.Exists(path)) Directory.CreateDirectory(path);
- //find last file or start from 0
- var pattern = $"{_level}_{_fsInstanceDate:yyyyMMdd}_???.log";
- var lastFile = Directory.GetFiles(path, pattern).OrderByDescending(p => p).FirstOrDefault();
- _rollNum = null != lastFile
- ? int.Parse(Path.GetFileNameWithoutExtension(lastFile).Split('_').Last())
- : 0;
- SwitchFile();
- }
- private void RollNext()
- {
- _rollNum++;
- SwitchFile();
- }
- public void WriteLine(DateTime date, string content)
- {
- EnsureFsInstance(date);
- var buf = Encoding.UTF8.GetBytes(content + Environment.NewLine);
- if (_fsInstance.Length + buf.Length > _ctx.ByteSplit) RollNext();
- _fsInstance.Write(buf, 0, buf.Length);
- _fsInstance.Flush();
- }
- }
- }
- }
|