最近有个需求,要求把日志加密,因为系统一直用的是NLog,所以也只能在这个基础上加密了。本文的用法NLog很早就有了,记录下来,以作后用。
NLog加密相对好处理,只需要定义一个TargetWithLayout的子类,重写它的Write方法即可,至于加密算法,可以自己行决定,这样就可以把灵活放成最大化,有途径,不干涉。下面代码中的EncryptedFileTarget就是自定义的TargetWithLayout子类。而[Target("MyEncryptedFile")]特性,是为了在nlog.config中配置使用这个加密类。除此以外,不需要任务引用。
using NLog.Targets;
using NLog;
using NLog.Web;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Host.UseNLog();
var app = builder.Build();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
app.Logger.LogInformation("完成测试");
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
});
app.Run();
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
[ ]
public sealed class EncryptedFileTarget : TargetWithLayout
{
public string FileName { get; set; }
private string key = "bb45f81e2db4a7668d98638402e2461991785f98a89520051149c271652514ba";
protected override void Write(LogEventInfo logEvent)
{
var logMessage = this.Layout.Render(logEvent);
var encryptedMessage = Encrypt(logMessage, key);
FileName = FileName.Replace("${basedir}", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)).Replace("${shortdate}", DateTime.Now.ToString("yyyy-MM-dd"));
System.IO.File.AppendAllText(FileName, encryptedMessage + Environment.NewLine);
}
private string Encrypt(string clearText, string key)
{
var clearBytes = Encoding.Unicode.GetBytes(clearText);
using (var encryptor = Aes.Create())
{
var saltBytes = new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 };
var pdb = new Rfc2898DeriveBytes(key, saltBytes, 1000, HashAlgorithmName.SHA256);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
}
}
下面是使用MyEncryptedFile的NLog配置文件,需要引用EncryptedFileTarget所在的命名空间,放在extensions节点中即可。
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Info">
<extensions>
<add assembly="NLogDemo" />
</extensions>
<targets>
<target xsi:type="MyEncryptedFile" name="allfile" fileName="${basedir}\logs\nlog-AspNetCore-all-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />
<target xsi:type="File" name="ownFile-web" fileName="${basedir}\logs\nlog-AspNetCore-own-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
<target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" /> </targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="allfile" />
<logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<logger name="System.Net.Http.*" maxlevel="Info" final="true" />
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>