当前位置:网站首页>NLog自定义Target之MQTT
NLog自定义Target之MQTT
2022-06-22 15:36:00 【dotNET跨平台】
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 fields
Dictionary<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 =>
{
//MqttServerWebSocket
endpoints.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
感兴趣可以关注我
边栏推荐
猜你喜欢

2022年中国重卡智能化升级专题研究

Huawei cloud recruits partners in the field of industrial intelligence to provide strong support + commercial realization

每秒处理10万高并发订单的乐视集团支付系统架构分享

高可用性的ResourceManager

linux系统维护篇:mysql8.0.13源码下载及安装之“傻瓜式”操作步骤(linux-centos6.8)亲测可用系列

视频会议时听不到声音该如何处理?

In the era of video explosion, who is supporting the high-speed operation of video ecological network?

视频爆炸时代,谁在支撑视频生态网高速运行?

Partage de l'architecture du système de paiement du Groupe letv pour traiter 100 000 commandes simultanées élevées par seconde

Summary of safari compatibility issues
随机推荐
Spark Streaming checkpoint的问题与恢复
Hydra installation and use
团队管理|如何提高技术 Leader 的思考技巧?
【微信小程序封装底部弹出框】一
面试知识点
Analysis of the read data source code of spark shuffle
面试题之 <img>标签 的 title 和 alt 有什么区别
Vhedt business development framework
[wechat applet to obtain the height of custom tabbar] is absolutely available!!!
What is restful and what rules should be followed when designing rest APIs?
让代码优雅起来(学会调试+代码风格)
Huawei cloud recruits partners in the field of industrial intelligence to provide strong support + commercial realization
[pop up box at the bottom of wechat applet package] I
Safari兼容性问题总结
mysql-5.6.21-centos6.5源码安装配置
ERROR 1364 (HY000): Field ssl_cipher doesnt have a default value
STM32通过DMA进行ADC采集(HAL库)
[MYSQL]一台windows电脑安装多个mysql-不同版本
[wechat applet custom bottom tabbar]
mysql指令执行sql文件