NLog自定义Target之MQTT

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NLog自定义Target之MQTT相关的知识,希望对你有一定的参考价值。

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

8. 也可以通过Mqtt客户端订阅日志 

源码及相关链接

[1] Githubhttps://github.com/iioter/NLog.Targets.MQTT

[2] Giteehttps://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

感兴趣可以关注我

以上是关于NLog自定义Target之MQTT的主要内容,如果未能解决你的问题,请参考以下文章

NLog使用说明

Nlog日志之File

为啥 NLog 不将范围数据记录到 Application Insights 自定义维度

创建用于使用NLog记录的自定义扩展功能

NLog日志框架使用探究-2

NLog日志框架使用探究-2