NLog自定义Target之MQTT
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通过Mqtt客户端订阅日志

源码
在这里NLog.Targets.MQTT:https://github.com/iioter/NLog.Targets.MQTT
相关链接
[1] NLog.Targets.MQTT:https://github.com/iioter/NLog.Targets.MQTT
[2] IoTGateway-Doc:http://iotgateway.net/blog/NLog
[3] NLog自定义Target:https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target
交流
| 公众号:工业物联网网关 | QQ群:712105424 |
|---|---|
![]() |
![]() |
NLog自定义Target之MQTT的更多相关文章
- NLog 自定义Target
http://nlog-project.org/2015/06/30/extending-nlog-is-easy.html 新建一个类库,命名规则为NLog.*.dll 定义一个类输出日志到Rabb ...
- 转:NLog 自定义日志内容,写日志到数据库;修改Nlog.config不起作用的原因
转:http://www.cnblogs.com/tider1999/p/4308440.html NLog的安装请百度,我安装的是3.2.NLog可以向文件,数据库,邮件等写日志,想了解请百度,这里 ...
- NLog自定义字段写入数据库表,示例
//自定义字段写入NLog日志 private void saveNLog(InvokeLogModel model) { LogEventInfo ei = new LogEventInfo(); ...
- 为NLog自定义LayoutRenderer
长话短说 前文<解剖HttpClientFactory,自由扩展HttpMessageHandler>主要想讲如何扩展HttpMessageHandler, 示例为在每个Http请求中的 ...
- NetCore2.2使用Nlog自定义日志写入路径配置方式
在一些特定场景的业务需求下,日志需要写入到不同的路径下提供日志分析.第一种:默认Nlog可以通过日志级别来区分路径,——优点是不需要额外配置,开箱即用——缺点是不够灵活,如果超过级别数量,则不满足需求 ...
- NLog 自定义字段 写入 oracle
1.通过Nuget安装NLog 下载,简单入门 请参照 我刚才转的几篇文章,下面我直接贴代码 2.建表语句 create table TBL_LOG ( id ) not null, appname ...
- [转]NLog 自定义字段 写入 oracle
本文转自:http://www.cnblogs.com/skyapplezhao/p/5690695.html 1.通过Nuget安装NLog 下载,简单入门 请参照 我刚才转的几篇文章,下面我直接贴 ...
- 关于NLog的target和Layout
这个没啥好说的,都是用别人的东西,看文档就行了,写的很详细. https://github.com/NLog/NLog/wiki/Configuration-file https://github.c ...
- NLog 安装使用
1:安装 Install-Package NLog.Config 或 通过Nuget 2:Log levels Trace 非常详细的信息,一般在开发时使用. Debug 比Trace稍微少一点一般不 ...
随机推荐
- pip:带你认识一个 Python 开发工作流程中的重要工具
摘要:许多Python项目使用pip包管理器来管理它们的依赖项.它包含在Python安装程序中,是Python中依赖项管理的重要工具. 本文分享自华为云社区<使用Python的pip管理项目的依 ...
- FFMPEG第一次学习
习惯用OneNote笔记,直接复制了,链接和一些命令放在最下面了 里面的库文件是我下的雷神的课件文件,我传到了自己github,链接也放最后了 转载自:https://blog.csdn.net/le ...
- Python BeautifulSoup4 爬虫基础、多线程学习
针对 崔庆才老师 的 https://ssr1.scrape.center 的爬虫基础练习.Threading多线程库.Time库.json库.BeautifulSoup4 爬虫库.py基本语法
- 状态机引擎在vivo营销自动化中的深度实践 | 引擎篇02
本文是<vivo营销自动化技术解密>的第3篇文章,分析了营销自动化业务背景和状态机引入原因.状态机的三种基本实现方式,同时介绍了几种业界流行的开源状态机框架实现和特点,以及在项目开发过程中 ...
- 挖矿病毒分析(centos7)
因为我在工作的时候被各种挖矿病毒搞过几次,所以在这里整理下我遇到的病毒以及大神们的解决方案. 服务器中挖矿病毒后,最基本的一个特征就是CPU使用率瞬间飙升,此时可以通过top命令进行查看,确认是否有异 ...
- vue3跳转路由3步曲
import { useRouter } from 'vue-router'; // 1. 引入路由export default { setup() { const $router ...
- android软件简约记账app开发day06-将记账条目添加到数据库并且绘制备注页面
android软件简约记账app开发day06-将记账条目添加到数据库并且绘制备注页面 首先写添加到数据库 在DBOpenHelper中添加创建记账表的语句 //创建记账表 sql = "c ...
- 百度飞桨数据处理 API 数据格式 HWC CHW 和 PIL 图像处理之间的关系
使用百度飞桨 API 例如:Resize Normalize,处理数据的时候. Resize:如果输入的图像是 PIL 读取的图像这个数据格式是 HWC ,Resize 就需要 HWC 格式的数据. ...
- screen使用小结
目录 安装 shell-screen-window关系 常用参数 快捷键 离开当前screen 打开一个新的窗口 查看窗口列表 窗口的快速切换 回到行首 关闭窗口 关闭所有窗口 关闭screen 删除 ...
- Golang 实现 Redis(10): 本地原子性事务
为了支持多个命令的原子性执行 Redis 提供了事务机制. Redis 官方文档中称事务带有以下两个重要的保证: 事务是一个单独的隔离操作:事务中的所有命令都会序列化.按顺序地执行.事务在执行的过程中 ...


