In this article, I’m going to explain logging in azure blob storage with nlog library.
Firstly, I need to create asp.net core empty web app then create an empty controller. I used swagger to help me btw.
Swagger Configuration (Startup.cs)
https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-2.2&tabs=visual-studio
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "Notification API",
Description = "AppCenter Notification API",
TermsOfService = "http://ozaksut.com/",
Contact = new Contact()
{
Name = "Yiğit Özaksüt",
Email = "yigit@ozaksut.com",
Url = "http://ozaksut.com/"
}
});
});
services.AddOptions();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.RoutePrefix = string.Empty;
c.SwaggerEndpoint("/swagger/v1/swagger.json", "AppCenter Notification API V1");
});
app.UseMvc();
}
Now I need to use Nlog
AzureStorage extension for Azure BlobStorage
https://github.com/JDetmar/NLog.Extensions.AzureStorage
Nlog.config
Don’t forget to set copy always!
<?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="info"-->
<!--internalLogFile="C:\Users\Yigit\Desktop\Log\internal-nlog.json"-->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
<!--AzureStorage extension-->
<add assembly="NLog.Extensions.AzureStorage" />
</extensions>
<!--"type" is your log type (AzureBlobStorage or File)-->
<!--"name" is your log rule name-->
<!--"blobName" is your file name-->
<!--"container" is your blob container name-->
<!--"connectionString" is your storage connection string-->
<targets async="true">
<target xsi:type="AzureBlobStorage"
name="error"
blobName="Error-${shortdate}.json"
container="logs"
connectionString="">
<layout type='CompoundLayout'>
<layout xsi:type="JsonLayout" includeAllProperties="true">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
<!--I found better option for additional data-->
<!--https://github.com/NLog/NLog/wiki/MDLC-Layout-Renderer-->
<!--<attribute name="request" layout="${gdc:request}" />-->
<attribute name="request" layout="${mdlc:item=request}" />
<!--<attribute name="response" layout="${gdc:response}" />-->
<attribute name="response" layout="${mdlc:item=response}" />
<attribute name="url" layout="${aspnet-request-url}" />
<attribute name="action" layout="${aspnet-mvc-action}" />
</layout>
<layout type='SimpleLayout' text="," />
</layout>
</target>
<target name="info"
xsi:type="AzureBlobStorage"
blobName="Info-${shortdate}.json"
container="logs"
connectionString="">
<layout type='CompoundLayout'>
<layout xsi:type="JsonLayout" includeAllProperties="true">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
<!--<attribute name="request" layout="${gdc:request}" />-->
<attribute name="request" layout="${mdlc:item=request}" />
<!--<attribute name="response" layout="${gdc:response}" />-->
<attribute name="response" layout="${mdlc:item=response}" />
<attribute name="url" layout="${aspnet-request-url}" />
<attribute name="action" layout="${aspnet-mvc-action}" />
</layout>
<layout type='SimpleLayout' text="," />
</layout>
</target>
<target name="trace"
xsi:type="AzureBlobStorage"
blobName="Trace-${shortdate}.json"
container="logs"
connectionString="">
<layout type='CompoundLayout'>
<layout xsi:type="JsonLayout" includeAllProperties="true">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
<!--<attribute name="request" layout="${gdc:request}" />-->
<attribute name="request" layout="${mdlc:item=request}" />
<!--<attribute name="response" layout="${gdc:response}" />-->
<attribute name="response" layout="${mdlc:item=response}" />
<attribute name="url" layout="${aspnet-request-url}" />
<attribute name="action" layout="${aspnet-mvc-action}" />
</layout>
<layout type='SimpleLayout' text="," />
</layout>
</target>
<target name="critical"
xsi:type="AzureBlobStorage"
blobName="Critical-${shortdate}.json"
container="logs"
connectionString="">
<layout type='CompoundLayout'>
<layout xsi:type="JsonLayout" includeAllProperties="true">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
<!--<attribute name="request" layout="${gdc:request}" />-->
<attribute name="request" layout="${mdlc:item=request}" />
<!--<attribute name="response" layout="${gdc:response}" />-->
<attribute name="response" layout="${mdlc:item=response}" />
<attribute name="url" layout="${aspnet-request-url}" />
<attribute name="action" layout="${aspnet-mvc-action}" />
</layout>
<layout type='SimpleLayout' text="," />
</layout>
</target>
</targets>
<!--Your log levels and rules-->
<rules>
<logger name="*" level="Error" writeTo="error" />
<logger name="*" level="Info" writeTo="info" />
<logger name="*" level="Trace" writeTo="trace" />
<logger name="*" level="Fatal" writeTo="critical" />
</rules>
</nlog>
If you want to inject additional context-data then you should consider using MDLC — Thanks Rolf Kristensen
https://github.com/NLog/NLog/wiki/MDLC-Layout-Renderer
I used CompoundLayout and JsonLayout. Because I wanted to take json list result for some reports. You need to remove last comma character, add a ‘]’ as the last character and add a ‘[‘ as the first character
SimpleLayout is seperator for each row
https://github.com/NLog/NLog/wiki/Tutorial
I need to add my request and response data in my logs that’s why I am adding gdc (GlobalDiagnosticsContext) on my log layout.
I found better option for additional data (MDLC). GDC (GlobalDiagnosticsContext) is for global data.
Program.cs
public class Program
{
public static void Main(string[] args)
{
var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
CreateWebHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
logger.Error(ex, "Stopped program because of exception");
throw;
}
finally
{
LogManager.Shutdown();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>().ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(LogLevel.Trace);
})
.UseNLog();
}
Now, you can create a Blob Storage
NotificationController
[Route("api/[controller]")]
[ApiController]
public class NotificationController : ControllerBase
{
private readonly IServiceManager _manager;
private readonly ILogger<NotificationController> _logger;
public NotificationController(ILogger<NotificationController> logger,
IServiceManager manager)
{
_logger = logger;
_manager = manager;
}
[HttpPost]
public async Task<ActionResult> Send([FromBody] SendNotificationRequest request)
{
MappedDiagnosticsLogicalContext.Set("request", JsonConvert.SerializeObject(request));
//GlobalDiagnosticsContext.Set("request", JsonConvert.SerializeObject(request));
await _manager.Post<SendNotificationRequest>(request);
return Ok();
}
}
ServiceManager.cs
public class ServiceManager : IServiceManager
{
private readonly ILogger<ServiceManager> _logger;
private static HttpClient _client;
public static HttpClient Client
{
get
{
if (_client == null)
{
_client = new HttpClient();
_client.DefaultRequestHeaders.Add("Accept", "application/json");
_client.DefaultRequestHeaders.Add("X-API-Token", "yout appcenter token");
}
return _client;
}
}
public string Url => "your appcenter notification post url";
public ServiceManager(ILogger<ServiceManager> logger)
{
_logger = logger;
}
public async Task<T> Get<T>()
{
var result = await Client.GetStringAsync(Url);
return JsonConvert.DeserializeObject<T>(result);
}
public async Task Post<K>(K request)
{
try
{
var req = JsonConvert.SerializeObject(request);
var encoding = Encoding.UTF8;
var mediaType = "application/json";
var content = new StringContent(req, encoding, mediaType);
var response = await Client.PostAsync(Url, content);
var result = await response.Content.ReadAsStringAsync();
MappedDiagnosticsLogicalContext.Set("response", JsonConvert.SerializeObject(result));
//GlobalDiagnosticsContext.Set("response", JsonConvert.SerializeObject(result));
//TODO
if (response.IsSuccessStatusCode && response.StatusCode == HttpStatusCode.Accepted)
{
var notificationResponse = JsonConvert.DeserializeObject<NotificationResponse>(result);
_logger.LogTrace("Accepted");
}
else
{
_logger.LogError("Error");
}
}
catch (Exception ex)
{
_logger.LogCritical(ex.Message);
}
}
}
When you try to post you can get logs on your blob storage.
[{ "time": "2019-02-24 16:44:25.8321", "level": "ERROR", "message": "Error",
"request": "{\"notification_target\":{\"type\":\"string\",\"devices\":[\"string\"]},\"notification_content\":{\"name\":\"string\",\"title\":\"string\",\"body\":\"string\",\"custom_data\":{\"additionalProp1\":\"string\",\"sound\":\"default\",\"badge\":0}}}",
"response": "\"{\\"error\\":{\\"code\\":400,\\"message\\":\\"unknown target.\\"}}\"",
"url": "https:\/\/localhost\/api\/Notification",
"action": "Send" }]
Post A Reply