This article shows how to Log to Elasticsearch using NLog in an ASP.NET Core application. NLog is a free open-source logging for .NET.
Code: https://github.com/damienbod/AspNetCoreNlog
NLog posts in this series:
- ASP.NET Core logging with NLog and Microsoft SQL Server
- ASP.NET Core logging with NLog and Elasticsearch
NLog.Extensions.Logging is required to use NLog in an ASP.NET Core application. This is added to the dependencies of the project. NLog.Targets.ElasticSearch is also added to the dependencies. This project is at present NOT the NuGet package from ReactiveMarkets, but the source code from ReactiveMarkets and updated to dotnetcore. Thanks to ReactiveMarkets for this library, hopefully the NuGet package will be updated and the NuGet package can be used directly.
The NLog configuration file also needs to be added to the publishOptions in the project.json file.
"dependencies": { "Microsoft.NETCore.App": { "version": "1.0.0", "type": "platform" }, "Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Diagnostics": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", "Microsoft.Extensions.Configuration.Json": "1.0.0", "Microsoft.Extensions.Logging": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", "NLog.Extensions.Logging": "1.0.0-rtm-alpha4", "NLog.Targets.ElasticSearch": "1.0.0-*" }, "publishOptions": { "include": [ "wwwroot", "Views", "Areas/**/Views", "appsettings.json", "web.config", "nlog.config" ] },
The NLog configuration is added to the Startup.cs class in the Configure method.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddNLog(); var configDir = "C:\\git\\damienbod\\AspNetCoreNlog\\Logs"; if (configDir != string.Empty) { var logEventInfo = NLog.LogEventInfo.CreateNullEvent(); foreach (FileTarget target in LogManager.Configuration.AllTargets.Where(t => t is FileTarget)) { var filename = target.FileName.Render(logEventInfo).Replace("'", ""); target.FileName = Path.Combine(configDir, filename); } LogManager.ReconfigExistingLoggers(); } //env.ConfigureNLog("nlog.config"); //loggerFactory.AddConsole(Configuration.GetSection("Logging")); //loggerFactory.AddDebug(); app.UseMvc(); }
The nlog.config target and rules can be configured to log to Elasticsearch. NLog.Targets.ElasticSearch is an extension and needs to be added using the extensions tag.
<?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" internalLogLevel="Warn" internalLogFile="C:\git\damienbod\AspNetCoreNlog\Logs\internal-nlog.txt"> <extensions> <add assembly="NLog.Targets.ElasticSearch"/> </extensions> <targets> <target name="ElasticSearch" xsi:type="BufferingWrapper" flushTimeout="5000"> <target xsi:type="ElasticSearch"/> </target> </targets> <rules> <logger name="*" minlevel="Trace" writeTo="ElasticSearch" /> </rules> </nlog>
The NLog.Targets.ElasticSearch package Elasticsearch URL can be configured using the ElasticsearchUrl property. This can be defined in the appsettings configuration file.
{ "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } }, "ElasticsearchUrl": "http://localhost:9200" }
NLog.Targets.ElasticSearch ( ReactiveMarkets )
The existing NLog.Targets.ElasticSearch project from ReactiveMarkets is updated to a NETStandard Library. This class library requires Elasticsearch.Net, NLog and Newtonsoft.Json. The dependencies are added to the project.json file. The library supports both netstandard1.6 and also net451.
{ "version": "1.0.0-*", "dependencies": { "NETStandard.Library": "1.6.0", "NLog": "4.4.0-betaV15", "Newtonsoft.Json": "9.0.1", "Elasticsearch.Net": "2.4.3", "Microsoft.Extensions.Configuration": "1.0.0", "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", "Microsoft.Extensions.Configuration.Json": "1.0.0" }, "frameworks": { "netstandard1.6": { "imports": "dnxcore50" }, "net451": { "frameworkAssemblies": { "System.Runtime.Serialization": "", "System.Runtime": "" } } } }
The StringExtensions class is extended to make it possible to define the Elasticsearch URL in a configuration file.
( original code from ReactiveMarkets )
using System; using System.IO; #if NET45 #else using Microsoft.Extensions.Configuration; #endif namespace NLog.Targets.ElasticSearch { internal static class StringExtensions { public static object ToSystemType(this string field, Type type) { switch (type.FullName) { case "System.Boolean": return Convert.ToBoolean(field); case "System.Double": return Convert.ToDouble(field); case "System.DateTime": return Convert.ToDateTime(field); case "System.Int32": return Convert.ToInt32(field); case "System.Int64": return Convert.ToInt64(field); default: return field; } } public static string GetConnectionString(this string name) { var value = GetEnvironmentVariable(name); if (!string.IsNullOrEmpty(value)) return value; #if NET45 var connectionString = ConfigurationManager.ConnectionStrings[name]; return connectionString?.ConnectionString; #else IConfigurationRoot configuration; var builder = new Microsoft.Extensions.Configuration.ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); configuration = builder.Build(); return configuration["ElasticsearchUrl"]; #endif } private static string GetEnvironmentVariable(this string name) { return string.IsNullOrEmpty(name) ? null : Environment.GetEnvironmentVariable(name); } } }
When the application is started the logs are written to Elasticsearch. These logs can be viewed in Elasticsearch
http://localhost:9200/logstash-‘date’/_search
{ "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 18, "max_score": 1.0, "hits": [{ "_index": "logstash-2016.08.19", "_type": "logevent", "_id": "AVaiJHPycDWw4BKmTWqP", "_score": 1.0, "_source": { "@timestamp": "2016-08-19T09:31:44.5790894Z", "level": "Debug", "message": "2016-08-19 11:31:44.5790|DEBUG|Microsoft.AspNetCore.Hosting.Internal.WebHost|Hosting starting" } }, { "_index": "logstash-2016.08.19", "_type": "logevent", "_id": "AVaiJHPycDWw4BKmTWqU", "_score": 1.0, "_source": { "@timestamp": "2016-08-19T09:31:45.4788003Z", "level": "Info", "message": "2016-08-19 11:31:45.4788|INFO|Microsoft.AspNetCore.Hosting.Internal.WebHost|Request starting HTTP/1.1 DEBUG http://localhost:55423/ 0" } }, { "_index": "logstash-2016.08.19", "_type": "logevent", "_id": "AVaiJHPycDWw4BKmTWqW", "_score": 1.0, "_source": { "@timestamp": "2016-08-19T09:31:45.6248512Z", "level": "Debug", "message": "2016-08-19 11:31:45.6248|DEBUG|Microsoft.AspNetCore.Server.Kestrel|Connection id \"0HKU82EHFC0S9\" completed keep alive response." } },
Links
https://github.com/NLog/NLog.Extensions.Logging
https://github.com/ReactiveMarkets/NLog.Targets.ElasticSearch
https://docs.asp.net/en/latest/fundamentals/logging.html
https://msdn.microsoft.com/en-us/magazine/mt694089.aspx
https://github.com/nlog/NLog/wiki/Database-target
https://www.elastic.co/products/elasticsearch
https://github.com/elastic/logstash
https://github.com/elastic/elasticsearch-net
https://www.nuget.org/packages/Elasticsearch.Net/
https://github.com/nlog/NLog/wiki/File-target#size-based-file-archival
http://www.danesparza.net/2014/06/things-your-dad-never-told-you-about-nlog/
