Logstash的Serilog HTTP Sink自定义格式

时间:2018-10-19 15:37:31

标签: json asp.net-core format logstash serilog

我正在使用Serilog HTTP sink登录到.Net Core项目中的Logstash。在startup.cs中,我具有以下代码来启用serilog。

 Log.Logger = new LoggerConfiguration()
        .Enrich.FromLogContext()
        .WriteTo.Http("http://mylogstashhost.com:5000").Enrich.WithProperty("user", "xxx").Enrich.WithProperty("serviceName", "yyy")
        .MinimumLevel.Warning()
        .CreateLogger();

此代码将日志发送到给定的http地址。我可以在提琴手上看到以下json被发布到了logstash,logstash返回了“确定”消息。

{"events":[{"Timestamp":"2018-10-19T18:16:27.6561159+01:00","Level":"Warning","MessageTemplate":"abc","RenderedMessage":"abc","user":"xxx","serviceName":"yyy","Properties":{"ActionId":"b313b8ed-0baf-4d75-a6e2-f0dbcb941f67","ActionName":"MyProject.Controllers.HomeController.Index","RequestId":"0HLHLQMV1EBCJ:00000003","RequestPath":"/"}}]}

但是当我检查Kibana时,看不到此日志。我试图弄清楚是什么原因导致的,并且我意识到,如果我以以下格式发送json,我可以看到日志。

{"Timestamp":"2018-10-19T18:16:27.6561159+01:00","Level":"Warning","MessageTemplate":"abc","RenderedMessage":"abc","user":"xxx","serviceName":"yyy","Properties":{"ActionId":"b313b8ed-0baf-4d75-a6e2-f0dbcb941f67","ActionName":"MyProject.Controllers.HomeController.Index" ,"RequestId":"0HLHLQMV1EBCJ:00000003","RequestPath":"/"}}

因此Logstash不希望该事件出现在Events {}中,并且它还希望“ Properties”中没有“ user”和“ ServiceName”标签。有没有办法像这样格式化我的Json?

2 个答案:

答案 0 :(得分:1)

我想用这种变化来扩展@nooaa答案。建议不要使用Newtonsoft.Json.Linq来操纵字符串以添加新对象。这样,您可以追加,添加或删除对象本身的现有属性。

此外,您可以结合事件的所有输出并在事件结束时执行一次output.write(有点性能),而不是在每个事件之后执行output.write

public override void Format(IEnumerable<string> logEvents, TextWriter output)
{
    if (logEvents == null) throw new ArgumentNullException(nameof(logEvents));
    if (output == null) throw new ArgumentNullException(nameof(output));

    // Abort if sequence of log events is empty
    if (!logEvents.Any())
    {
        return;
    }
    
    List<object> updatedEvents = new List<object>();
    foreach (string logEvent in logEvents)
    {
        if (string.IsNullOrWhiteSpace(logEvent))
        {
            continue;
        }

        // Parse the log event
        var obj = JObject.Parse(logEvent);

        // Add New entries 
        obj["@source_host"] = obj["fields"]["MachineName"].Value<string>().ToLower();

        // Remove any entries you are not interested in
        ((JObject)obj["fields"]).Remove("MachineName");
                
        // Default tags for any log that goes out of your app.
        obj["@tags"] = new JArray() { "appName", "api" };

        // Additional tags from end points (custom based on routes)
        if (obj["fields"]["tags"] != null) 
        {
            ((JArray)obj["@tags"]).Merge((JArray)obj["fields"]["tags"]);
            ((JObject)obj["fields"]).Remove("tags");
        }
       
        updatedEvents.Add(obj);
    }

    output.Write(JsonConvert.SerializeObject(updatedEvents));
}

答案 1 :(得分:0)

在经过一些研究和帮助之后,基本上可以实现自定义格式,应该实现ITextFormatter,BatchFormatter等接口。

我可以通过稍微修改ArrayBatchFormatter来获得所需的格式:

public class MyFormat : BatchFormatter
{
    /// <summary>
    /// Initializes a new instance of the <see cref="ArrayBatchFormatter"/> class.
    /// </summary>
    /// <param name="eventBodyLimitBytes">
    /// The maximum size, in bytes, that the JSON representation of an event may take before it
    /// is dropped rather than being sent to the server. Specify null for no limit. Default
    /// value is 256 KB.
    /// </param>
    public MyFormat(long? eventBodyLimitBytes = 256 * 1024): base(eventBodyLimitBytes)
    {

    }

    /// <summary>
    /// Format the log events into a payload.
    /// </summary>
    /// <param name="logEvents">
    /// The events to format.
    /// </param>
    /// <param name="output">
    /// The payload to send over the network.
    /// </param>
    public override void Format(IEnumerable<string> logEvents, TextWriter output)
    {
        if (logEvents == null) throw new ArgumentNullException(nameof(logEvents));
        if (output == null) throw new ArgumentNullException(nameof(output));

        // Abort if sequence of log events is empty
        if (!logEvents.Any())
        {
            return;
        }

        output.Write("[");

        var delimStart = string.Empty;

        foreach (var logEvent in logEvents)
        {
            if (string.IsNullOrWhiteSpace(logEvent))
            {
                continue;
            }
            int index = logEvent.IndexOf("{");

            string adjustedString = "{\"user\":\"xxx\",\"serviceName\" : \"yyy\"," + logEvent.Substring(1);
            if (CheckEventBodySize(adjustedString))
            {
                output.Write(delimStart);
                output.Write(adjustedString);
                delimStart = ",";
            }
        }

        output.Write("]");
    }
}