NLog是.Net中最流行的日志記錄開源項目(之一),它靈活、免費、開源
官方支持文件、網絡(TCP、UDP)、數據庫、控制臺等輸出
社區支持Elastic、Seq等日志平臺輸出
實時日志需求
在工業物聯網等特定場景下需要實時獲取日志信息
工業物聯網領域常用的是mqtt協議
那我們就使用NLog?自定義一個Target,將日志輸出到MqttServer
Web通過Mqtt(websocket)實時獲取日志,而不是傳統的通過WebApi輪詢日志
NLog自定義Target
官方文檔介紹了如何自定義Target,可以獲取到一串日志消息,無法獲取結構化消息
需要使用自定義Field來完成這部分工作
///?<summary>
///?Additional?field?details
///?</summary>
[NLogConfigurationItem]
public?class?Field
{///?<summary>///?Name?of?additional?field///?</summary>[RequiredParameter]public?string?Name?{?get;?set;?}///?<summary>///?Value?with?NLog?<see?cref="NLog.Layouts.Layout"/>?rendering?support///?</summary>[RequiredParameter]public?Layout?Layout?{?get;?set;?}///?<summary>///?Custom?type?conversion?from?default?string?to?other?type///?</summary>///?<remarks>///?<see?cref="System.Object"/>?can?be?used?if?the?<see?cref="Layout"/>?renders?JSON///?</remarks>public?Type?LayoutType?{?get;?set;?}?=?typeof(string);///?<inheritdoc?/>public?override?string?ToString(){return?$"Name:?{Name},?LayoutType:?{LayoutType},?Layout:?{Layout}";}
}
重寫Write方法
protected?override?void?Write(LogEventInfo?logEvent)
{//default?fieldsDictionary<string,?object>?logDictionary?=?new(){{?"timestamp",?logEvent.TimeStamp?},{?"level",?logEvent.Level.Name?},{?"message",?RenderLogEvent(Layout,?logEvent)?}};//customer?fields//這里都處理為字符串了,有優化空間foreach?(var?field?in?Fields){var?renderedField?=?RenderLogEvent(field.Layout,?logEvent);if?(string.IsNullOrWhiteSpace(renderedField))continue;logDictionary[field.Name]?=?renderedField;}SendTheMessage2MqttBroker(JsonConvert.SerializeObject(logDictionary));
}
使用
下面將使用Nlog.Target.MQTT,演示通過web實時查看應用程序的日志。
創建WebApi項目
引用NLog.Target.MQTT
配置文件
<extensions><add?assembly="NLog.Web.AspNetCore"/><!--<add?assembly="NLog.Targets.MQTT"/>--><add?assembly="NLog.Targets.MQTT"/>
</extensions>
<!--?the?targets?to?write?to?-->
<targets><!--?MQTT?Target??--><target?xsi:type="MQTT"?name="mqtt"?host="localhost"?port="1883"?username="UserName"??password="Password"?topic="log"layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message}?${exception:format=tostring}|url:?${aspnet-request-url}|action:?${aspnet-mvc-action}|${callsite}"?><field?name="machine"?layout="${machinename}"?/><field?name="processid"?layout="${processid}"?/><field?name="threadname"?layout="${threadname}"?/><field?name="logger"?layout="${logger}"?/><field?name="callsite"?layout="${callsite-linenumber}"?/><field?name="url"?layout="${aspnet-request-url}"?/><field?name="action"?layout="${aspnet-mvc-action}"?/><field?name="level"?layout="${level:uppercase=true}"?/><field?name="message"?layout="${message}"?/><field?name="exception"?layout="${exception:format=toString}"?/></target>
</targets>
<!--?rules?to?map?from?logger?name?to?target?-->
<rules><logger?name="*"?minlevel="Trace"?writeTo="mqtt"?/>
</rules>
配置MQTTServer和NLog
//?...
//?NLog:?Setup?NLog?for?Dependency?injection
builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
builder.Host.UseNLog();//AddHostedMqttServer
builder.Services.AddHostedMqttServer(mqttServer?=>{mqttServer.WithoutDefaultEndpoint();}).AddMqttConnectionHandler().AddConnections();//Config?Port
builder.WebHost.UseKestrel(option?=>
{option.ListenAnyIP(1883,?l?=>?l.UseMqtt());option.ListenAnyIP(80);
});
var?app?=?builder.Build();//?...
//UseStaticFiles?html?js?etc.
app.UseStaticFiles();
app.UseRouting();//Websocket?Mqtt
app.UseEndpoints(endpoints?=>
{//MqttServerWebSocketendpoints.MapConnectionHandler<MqttConnectionHandler>("/mqtt",?options?=>{options.WebSockets.SubProtocolSelector?=?MqttSubProtocolSelector.SelectSubProtocol;});
});
//?...
Web連接MqttServer
//?...????
<script?src="./jquery.min.js"></script>
<script?src="./mqtt.min.js"></script>
<script?src="./vue.js"></script>
//?...var?client?=?mqtt.connect('ws://'?+?window.location.host?+?'/mqtt',?options);
client.on('connect',function()?{client.subscribe('log',function(err)?{if?(!err)?{console.log("subed!");}?else?{alert("subed?error!");}});});
client.on('message',function(topic,?message)?{if?(topic?===?'log')?{if?(app.logs.length?>?50)app.logs.length?=?0;app.logs.unshift($.parseJSON(message.toString()));}});
//?...
輸出日志
//?...??
_logger.LogDebug("LogDebug!");
_logger.LogError(new?Exception("Exception?Message!"),?"LogError!");//new?thread?output?log?after?500ms
Thread?thread?=?new?Thread(ThreadProc);
thread.Name?=?"My?Thread";
thread.Start();
//?...
實時查看日志 訪問/index.html
8. 也可以通過Mqtt客戶端訂閱日志?
源碼及相關鏈接
[1]?Github:https://github.com/iioter/NLog.Targets.MQTT
[2]?Gitee:https://gitee.com/iioter/NLog.Targets.MQTT
[3]??IoTGateway-Doc:http://iotgateway.net/blog/NLog
[4]?NLog自定義Target:https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target
感興趣可以關注我