使用Topshelf
開發Windows服務,比起直接寫Windows服務的優勢在于,可以直接運行調試。開發完成后,直接使用install
命令注冊為服務即可,簡單高效。
一、準備工作
-
創建一個控制臺應用
image.png -
在Nuget包管理器中搜索并安裝Topshelf、Quartz、log4net這幾個庫
image.png
使用Topshelf框架
- 在程序入口類
Program.cs
中修改Main方法
using Topshelf;
namespace DemoService
{
class Program
{
static void Main(string[] args)
{
string serviceName = "DemoService";
HostFactory.Run(config =>
{
config.Service<StartService>(s =>
{
s.ConstructUsing(name => new StartService());
s.WhenStarted(service => service.Start());
s.WhenStopped(service => service.Stop());
});
config.RunAsLocalSystem();
config.SetDescription(serviceName);
config.SetServiceName(serviceName);
config.SetDisplayName(serviceName);
});
}
}
}
- 新增類文件:
StartService.cs
,并編寫Start()、Stop()方法,這兩個方法在Main方法中被注冊為服務的啟動和停止方法。
namespace DemoService
{
public class StartService
{
public void Start()
{
}
public void Stop()
{
}
}
}
二、配置日志工具log4net
-
創建一個應用程序配置文件,改名為
log4net.config
image.png 在
log4net.config
的<configuration>
項中寫入如下配置項
<!-- Level的級別,由高到低 -->
<!-- None > Fatal > ERROR > WARN > DEBUG > INFO > ALL-->
<!-- 解釋:如果level是ERROR,則在cs文件里面調用log4net的info()方法,則不會寫入到日志文件中-->
<log4net>
<!--錯誤日志類-->
<logger name="logerror"><!--日志類的名字-->
<level value="ALL" /><!--定義記錄的日志級別-->
<appender-ref ref="ErrorAppender" /><!--記錄到哪個介質中去-->
</logger>
<!--信息日志類-->
<logger name="loginfo">
<level value="ALL" />
<appender-ref ref="InfoAppender" />
</logger>
<!--錯誤日志附加介質-->
<appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender"><!-- name屬性指定其名稱,type則是log4net.Appender命名空間的一個類的名稱,意思是,指定使用哪種介質-->
<param name="File" value="Log\\LogError\\" /><!--日志輸出到exe程序這個相對目錄下-->
<param name="AppendToFile" value="true" /><!--輸出的日志不會覆蓋以前的信息-->
<param name="MaxSizeRollBackups" value="100" /><!--備份文件的個數-->
<param name="MaxFileSize" value="10240" /><!--當個日志文件的最大大小-->
<param name="StaticLogFileName" value="false" /><!--是否使用靜態文件名-->
<param name="DatePattern" value=""Log_"yyyyMMdd".htm"" /><!--日志文件名-->
<param name="RollingStyle" value="Date" /><!--文件創建的方式,這里是以Date方式創建-->
<!--錯誤日志布局-->
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="<HR COLOR=red>%n異常時間:%d [%t] <BR>%n異常級別:%-5p <BR>%n異 常 類:%c [%x] <BR>%n%m <BR>%n <HR Size=1>" />
</layout>
</appender>
<!--信息日志附加介質-->
<appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="Log\\LogInfo\\" />
<param name="AppendToFile" value="true" />
<param name="MaxFileSize" value="10240" />
<param name="MaxSizeRollBackups" value="100" />
<param name="StaticLogFileName" value="false" />
<param name="DatePattern" value=""Log_"yyyyMMdd".htm"" />
<param name="RollingStyle" value="Date" />
<!--信息日志布局-->
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="<HR COLOR=blue>%n日志時間:%d [%t] <BR>%n日志級別:%-5p <BR>%n日 志 類:%c [%x] <BR>%n%m <BR>%n <HR Size=1>" />
</layout>
</appender>
</log4net>
-
以上配置將會將Info日志保存至項目文件夾下的
Log\\LogInfo\\
,將錯誤日志保存至Log\\LogError\\
,日志文件保存為.htm
格式。
轉義字符對照表
日志效果圖 在
Properties
下的AssemblyInfo.cs
中注冊一下log4net.config
配置文件
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", ConfigFileExtension = "config", Watch = true)]
- 編寫
LogHelper.cs
類
public class LogHelper
{
//這里的 loginfo 和 log4net.config 里的名字要一樣
private static readonly log4net.ILog loginfo = log4net.LogManager.GetLogger("loginfo");
//這里的 logerror 和 log4net.config 里的名字要一樣
private static readonly log4net.ILog logerror = log4net.LogManager.GetLogger("logerror");
public static void Info(string message)
{
if (loginfo.IsInfoEnabled)
{
loginfo.Info(message);
}
}
public static void Error(string message, Exception ex)
{
if (logerror.IsErrorEnabled)
{
logerror.Error(message, ex);
}
}
}
三、使用Quartz編寫定時任務
- 創建一個
DemoJob.cs
類
using Quartz;
using System;
namespace DemoService.Jobs
{
public class DemoJob : IJob
{
public void Execute(IJobExecutionContext context)
{
Console.WriteLine("Hello");
LogHelper.Info("Hello");
}
}
}
- 使用Quartz框架,打開
StartService.cs
文件,在Start()方法中添加如下內容
public class StartService
{
public void Start()
{
// 開啟調度
ISchedulerFactory sf = new StdSchedulerFactory();
IScheduler scheduler = sf.GetScheduler();
IJobDetail job = JobBuilder.Create<DemoJob>().Build();
// 服務啟動時執行一次
// ITrigger triggerNow = TriggerBuilder.Create().StartNow().Build();
// 每日22點執行一次
ITrigger trigger = TriggerBuilder.Create()
.StartNow()
.WithCronSchedule("0 0 22 1/1 * ? ")
.Build();
scheduler.ScheduleJob(job, trigger);
scheduler.Start();
LogHelper.Info("服務開始執行。");
}
public void Stop()
{
}
}
Cron
表達式很強大,但剛上手需要一定的學習成本,此處附上一個生成表達式的工具:
在線Cron表達式生成器傳送門想了解
Cron
表達式,可參考該篇博文:https://www.cnblogs.com/yanghj010/p/10875151.html
四、服務注冊、卸載
編寫如下兩個批處理文件,放在生成的目錄下,需要注冊、卸載時,直接執行即可。
install.bat
cd /d "%~dp0"
生成的可執行文件名.exe install
net start DemoService
cmd
uninstall.bat
cd /d "%~dp0"
生成的可執行文件名.exe uninstall
cmd