NLog是.Net中最流行的日志记录开源项目(之一),它灵活免费开源

官方支持文件网络(Tcp、Udp)、数据库控制台等输出

社区支持ElasticSeq等日志平台输出

实时日志需求

在工业物联网等特定场景下需要实时获取日志信息

工业物联网领域常用的是mqtt协议

那我们就使用NLog 自定义一个Target,将日志输出到MqttServer

Web通过Mqtt(websocket)实时获取日志,而不是传统的通过WebApi轮询日志

NLog自定义Target

  1. 官方文档介绍了如何自定义Target,可以获取到一串日志消息,无法获取结构化消息
  2. 需要时用使用自定义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}";
}
}
  1. 重写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实时查看应用程序的日志

  1. 创建WebApi项目
  2. 引用NLog.Target.MQTT

  1. 配置文件
<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>
  1. 配置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;
});
});
// ...
  1. 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()));
}
});
// ...
  1. 输出日志
// ...
_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();
// ...
  1. 实时查看日志

    访问index.html

  2. 通过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的更多相关文章

  1. NLog 自定义Target

    http://nlog-project.org/2015/06/30/extending-nlog-is-easy.html 新建一个类库,命名规则为NLog.*.dll 定义一个类输出日志到Rabb ...

  2. 转:NLog 自定义日志内容,写日志到数据库;修改Nlog.config不起作用的原因

    转:http://www.cnblogs.com/tider1999/p/4308440.html NLog的安装请百度,我安装的是3.2.NLog可以向文件,数据库,邮件等写日志,想了解请百度,这里 ...

  3. NLog自定义字段写入数据库表,示例

    //自定义字段写入NLog日志 private void saveNLog(InvokeLogModel model) { LogEventInfo ei = new LogEventInfo(); ...

  4. 为NLog自定义LayoutRenderer

    长话短说 前文<解剖HttpClientFactory,自由扩展HttpMessageHandler>主要想讲如何扩展HttpMessageHandler,  示例为在每个Http请求中的 ...

  5. NetCore2.2使用Nlog自定义日志写入路径配置方式

    在一些特定场景的业务需求下,日志需要写入到不同的路径下提供日志分析.第一种:默认Nlog可以通过日志级别来区分路径,——优点是不需要额外配置,开箱即用——缺点是不够灵活,如果超过级别数量,则不满足需求 ...

  6. NLog 自定义字段 写入 oracle

    1.通过Nuget安装NLog 下载,简单入门 请参照 我刚才转的几篇文章,下面我直接贴代码 2.建表语句 create table TBL_LOG ( id ) not null, appname ...

  7. [转]NLog 自定义字段 写入 oracle

    本文转自:http://www.cnblogs.com/skyapplezhao/p/5690695.html 1.通过Nuget安装NLog 下载,简单入门 请参照 我刚才转的几篇文章,下面我直接贴 ...

  8. 关于NLog的target和Layout

    这个没啥好说的,都是用别人的东西,看文档就行了,写的很详细. https://github.com/NLog/NLog/wiki/Configuration-file https://github.c ...

  9. NLog 安装使用

    1:安装 Install-Package NLog.Config 或 通过Nuget 2:Log levels Trace 非常详细的信息,一般在开发时使用. Debug 比Trace稍微少一点一般不 ...

随机推荐

  1. Struts2-day1总结

    1.Struts2的基本执行流程(详见我的博客) 2.Struts2的分模块开发 如果有多个项目的Struts.xml写在一起,容易造成数据混乱,所以可以使用分模块的方法,在项目下新建*.xml配置文 ...

  2. springboot中bean的重定义

    需求描述: 项目中应用其他项目的jar包,然后有些controller中的方法有缺陷需要修改. 1.配置添加 spring.main.allow-bean-definition-overriding= ...

  3. IDEA小技巧:Markdown里的命令行可以直接运行了

    作为一名开发者,相信大部分人都喜欢用Markdown来写文章和写文档. 如果你经常用开源项目或者自己维护开源项目,肯定对于项目下的README文件也相当熟悉了吧,通常我们会在这里介绍项目的功能.如何使 ...

  4. Spring 源码 (1)Spring IOC Bean 创建的整体流程

    Spring IOC 中涉及的重要接口 BeanDefinition Bean的描述信息,实现类包括 RootBeanDefinition 和 GenericBeanDefinition,Bean的描 ...

  5. SpringBoot--Easycode、mybatisX插件生成entity,controller,service,dao,mapper IDEA版 项目提效神器

    一.介绍 Easycode是idea的一个插件,可以直接对数据的表生成entity,controller,service,dao,mapper,无需任何编码,简单而强大. MybatisX 是一款基于 ...

  6. 数据结构篇(2) ts实现单链表

    interface NodeItem { prev: NodeItem | null next: NodeItem | null data: any } class NodeItem { prev: ...

  7. python数据处理matplotlib入门(2)-利用随机函数生成变化图形

    综合前述的类.函数.matplotlib等,完成一个随机移动的过程(注意要确定移动的次数,比如10万次),每次行走都完全是随机的,没有明确的方向,结果是由一系列随机决策确定的,最后显示出每次移动的位置 ...

  8. python学习-Day37

    目录 今日内容详细 GIL全局解释器锁 GIL与普通互斥锁区别 GIL对程序的影响 验证多线程作用 两个大前提 关于CPU的个数 关于任务的类型 死锁现象 避免死锁的解决: 添加超时释放锁 信号量 自 ...

  9. python学习-Day20

    目录 今日内容详细 作业讲解 re模块补充说明 findall的优先级查询 通过索引的方式单独获取分组内匹配到的数据 分组之后还可以给组起别名 split的优先级查询 collections模块 具名 ...

  10. Halo 开源项目学习(五):评论与点赞

    基本介绍 博客系统中,用户浏览文章时可以在文章下方发表自己的观点,与博主或其他用户进行互动,也可以为喜欢的文章点赞.下面我们一起分析一下 Halo 项目中评论和点赞功能的实现过程. 发表评论 评论可以 ...