Log4Net是我们经常使用的记log框架,但是最近发现了两个问题:
1. 按日期(rollingStyle)生成的文件没有自动删除(MaxSizeRollBackups);(按大小生成的会自动删除)
2. 如果按混合(Composite)生成的文件,在程序重启后,.1之后的文件会被重写,.0文件是appendToFile。(按日期和按大小生成的不存在该问题)
该文章主要就是解决第1个问题,通过写程序定期执行的方式,删除过期的log文件夹。可以使用定时器完成,但是我为了学习使用Quartz.Net,所以用了该框架来实现。
参考文档:
我们记录的日志是按天生成文件夹(yyyyMMdd),文件夹里按小时生成log(HH.log)。
所以删除过期日志时可以直接删除文件夹。
删除文件/文件夹使用语言自带的类库(System.IO)即可。
2. C# 中字符串转换成日期
我们可以根据文件夹的创建日期来决定是否删除,也可以根据文件夹的名称来判断。
DateTime.Pares()转换对string有格式要求。
可以使用该方法来转换:
DateTime.ParseExact(dateString, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture)
为了增强程序的适用性,我们判断过期文件夹的路径是从webconfig获取的log4net的设置路径。
如果xml.Load()的属性直接写Web.Config,程序会报错:C:Program Files (x86)IIS ExpressWeb.Config不存在。可以指定绝对路径。
因为我们是在MVC WEB程序,所以使用下述方法获取Web应用程序的根目录。
System.Web.HttpRuntime.AppDomainAppPath
4. C#的.Net作业调度(一) -Quartz.Net入门
6. 基于ASP.NET MVC(C#)和Quartz.Net组件实现的定时执行任务调度
参考文档4和5是Quartz.Net的入门案例。
参考文档6是在MVC中使用Quartz.Net。
但是最新的Quartz.Net使用方法和前3篇播客中的有变化,引入了异步机制,参考文档7是对此的案列。
参考上述文档,我的代码实现如下:
<log4net> <appender name="infoLog" type="log4net.Appender.RollingFileAppender,log4net"> <!--日志文件路径,按文件大小方式输出时在这里指定文件名,并且前面的日志按天在文件名后自动添加当天日期形成文件--> <file value="LogLogInfo" /> <!--是否是向文件中追加日志--> <appendToFile value="true" /> <!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])--> <rollingStyle value="Date" /> <!--每个文件的大小。只在混合方式与文件大小方式下使用,超出大小的在文件名后自动增加1重新命名--> <param name="maximumFileSize" value="10MB" /> <!--按日期产生文件,文件名[在日期方式与混合方式下使用]日志文件名格式为:20190809-info.log --> <datePattern value="yyyyMMdd\'LogInfo'HH'.log'" /> <!--日志文件名是否是固定不变的(是否只写到一个文件中)--> <staticLogFileName value="false" /> <!--扩展名--> <!--<preserveLogFileNameExtension value="true" />--> <!--设置文件的编号顺序--> <!--<countDirection value="1" />--> <!--log保留天数--> <param name="MaxSizeRollBackups" value="-1" /> <!--记录的格式。--> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="=========================================================
" /> <conversionPattern value="%d [%t] %-5p %c [%class]-[%M] - %m%n" /> <param name="Footer" value="=========================================================
" /> </layout> </appender> <root> <!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低) --> <level value="ALL" /> <appender-ref ref="infoLog"/> </root> </log4net>
namespace DeleteLog { public class DeleteLogJobScheduler { public static async Task Start() { //获取web.config中log的路径 XmlDocument xml = new XmlDocument(); string webConfig = HttpRuntime.AppDomainAppPath + "Web.config"; xml.Load(webConfig); XmlNode log4netNode = (xml.GetElementsByTagName("file"))[0]; string logDirectory = ((XmlElement)log4netNode).GetAttribute("value"); ISchedulerFactory factory = new StdSchedulerFactory(); IScheduler scheduler = await factory.GetScheduler(); await scheduler.Start(); IJobDetail job = JobBuilder.Create<DeleteLogDirectory>() .WithIdentity("myJob", "group1") .UsingJobData("directory", logDirectory)
//保存周期30天 .UsingJobData("expire", 30) .Build(); ITrigger trigger = TriggerBuilder.Create() .WithIdentity("myTrigger", "group1") .StartNow()
//一天执行一次 .WithSimpleSchedule(x => x .WithIntervalInHours(24) .RepeatForever()) .Build(); await scheduler.ScheduleJob(job, trigger); } } public class DeleteLogDirectory : IJob { Task IJob.Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; string logDirectory = HttpRuntime.AppDomainAppPath + dataMap.GetString("directory"); int logExpire = dataMap.GetInt("expire"); DateTime nowTime = DateTime.Now; DirectoryInfo root = new DirectoryInfo(logDirectory); DirectoryInfo[] dics = root.GetDirectories(); FileAttributes attr = File.GetAttributes(logDirectory); return Task.Run(() => { if (attr == FileAttributes.Directory) { foreach (DirectoryInfo dic in dics) { try { TimeSpan t = nowTime - DateTime.ParseExact(dic.Name, "yyyyMMdd", CultureInfo.CurrentCulture); int day = t.Days; if (day > logExpire) { Directory.Delete(dic.FullName, true); } } catch (Exception) { } } } }); } } }
//MVC中调用前文定义的任务调度 namespace DeleteLog { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { log4net.Config.XmlConfigurator.Configure(new FileInfo("Web.config")); DeleteLogJobScheduler.Start(); AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } } }