NLog ApplicationInsightsTarget unable to read application insights key from appsettings.json - azure-application-insights

I am trying to read the application insights instrumentation key from appsettings.json file in my ASP.NET Core 3.1 Web Application but all my tries went in vain so far as the target is still showing as unconfigured.
If I add the key directly in ApplicationInsightsTarget, then it is working fine.
Here is the content of appsettings.json file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"AppInsightsKey": "Instrumentation-Key-From-Azure-Application-Insights-Resource"
}
Here is the content of nlog.config file:
<?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"
throwConfigExceptions="true"
internalLogLevel="info"
internalLogFile="c:\temp\internal-nlog-AspNetCore3.txt">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="Microsoft.ApplicationInsights.NLogTarget" />
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
<target xsi:type="Console" name="lifetimeConsole" layout="${configsetting:item=AppInsightsKey} ${level:truncate=4:lowercase=true}: ${logger}[0]${newline} ${message}${exception:format=tostring}" />
<target name="aiTarget" xsi:type="ApplicationInsightsTarget"
layout="${date:format=yyyy-MM-dd HH\:mm\:ss}: [LOCAL] - ${level} - ${message}${exception:format=ToString}">
<instrumentationKey>${configsetting:item=AppInsightsKey}</instrumentationKey>
<contextproperty name="threadid" layout="${threadid}" />
</target>
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" minlevel="Trace" writeTo="lifetimeConsole" />
<logger name="*" minlevel="Trace" writeTo="aiTarget" />
</rules>
</nlog>
As you can see from above, I was trying to grab the application insights key from appsettings.json file by ${configsetting:item=AppInsightsKey}, but in all occasions it's coming as empty.
I've tried with ${configsetting:name=AppInsightsKey} , which didn't help either.
This is the program.cs file content
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Web;
using System;
namespace ASPNETCoreWebAppNLogAppInsightsDemo
{
public class Program
{
public static void Main(string[] args)
{
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
CreateHostBuilder(args).Build().Run();
}
catch (Exception exception)
{
//NLog: catch setup errors
logger.Error(exception, "Stopped program because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.UseNLog(); // NLog: Setup NLog for Dependency injection
}
}
Then I've tried to embed the key in the console target layout, just to ensure nlog is able to read it correctly, which to my surprise is working perfectly fine. So, looks like the issue is with ApplicationInsightsTarget only.
I know, I can pull it from Environment variables and hence in Azure App service it is not required to read from appsettings.json, but I would like to understand about this behavior since already spent too much time fixing myself :-)
Any help will be appreciated.

Try replacing the old code:
var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
With this new code:
var logger = LogManager.Setup()
.LoadConfigurationFromAppSettings()
.GetCurrentClassLogger();
The new code will load the appsettings.json, and sure that ${configsetting} are available during initialization of NLog Targets.

Related

Azure API - How do I setup CORS

I have REST API, that is hosted in Azure. If I make request in interactive console with GET method ('/api/pets'), request goes through just fine. But when I make POST request (POST '/api/pets'), CORS error appears.
Response in interacive console throws this error:
startup.cs
public void ConfigureServices(IServiceCollection services)
{
.
.
.
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
}));
.
.
.
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors("CorsPolicy");
.
.
.
}
All API's CORS policy
<policies>
<inbound>
<cors>
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>GET</method>
<method>POST</method>
<method>PUT</method>
<method>DELETE</method>
<method>HEAD</method>
<method>OPTIONS</method>
<method>PATCH</method>
<method>TRACE</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
</inbound>
<backend>
<forward-request />
</backend>
<outbound />
<on-error />
</policies>
I also tried setting CORS policy on azure portal only in specific API, but it doesn't work
Everything works fine locally.
Can you try configure this using the portal?
Open your API in the Azure API Management section of the Azure portal
Select All operations, or a single operation
On the right, choose Inbound processing > Add policy
You will get a list of prefab policy templates. Choose the "CORS" one and configure it at will:
UPDATE:
Can you modify the XML as below
<cors>
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods>
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
Few years ago I tried Outboud rules into API Managment Policies to add this headers to response:
<policies>
<inbound>
<base />
<cors allow-credentials="true">
...
</cors>
</inbound>
...
<outbound>
<base />
<set-header name="Access-Control-Allow-Origin" exists-action="override">
<value>#(context.Request.Headers.GetValueOrDefault("Origin",""))</value>
</set-header>
<set-header name="Access-Control-Allow-Credentials" exists-action="override">
<value>true</value>
</set-header>
</outbound>
...
</policies>
Also make sure that you are using app.UseCors() before response compression if exists and before app.UseEndpoints() or app.UseMvc()

${mdlc:item=RequestId} and ${aspnet-request-posted-body} not returning data

I am using ${aspnet-request-posted-body} in my nlog.config file ,I have also added configuartions in startup file. but ${aspnet-request-posted-body} not logging request body and ${mdlc:item=RequestId} also not working
<target xsi:type="File" name="allfile" fileName="c:\Logs\Api_.log"
layout="==>[${event-properties:item=traceLevel}: User=${aspnet-user-identity}; ]
${when:when=('${event-properties:item=traceLevel}' == 'Verbose' and
'${aspnet-request-method}' != 'GET') > 0 and length('${mdlc:item=RequestId}') > 0:inner=${newline}Request-Body\: ${aspnet-request-posted-body}}
${newline}Logger name: ${logger}
${newline}" />
${aspnet-request-posted-body} and ${mdlc:item=RequestId} used in nlog file
To enable ${aspnet-request-posted-body} in startup I used this code.
app.Use(async (context, next) =>
{
context.Request.EnableBuffering();
await next?.Invoke();
});
Would probably start with something simple like this:
<target xsi:type="File" name="allfile" fileName="c:\Logs\Api_.log"
layout="RequestId=${mdlc:item=RequestId} RequestBody=${aspnet-request-posted-body}" />
And then enable the NLog InternalLogger at Debug-Level and look for clues:
https://github.com/NLog/NLog/wiki/Internal-Logging
https://github.com/NLog/NLog/wiki/Logging-troubleshooting
You also need to correctly register NLog as LoggingProvider with the method UseNLog(). See also https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3
If you are not using Microsoft ILogger for logging within the context for the active HttpRequest, then RequestId will not be available.
NLog.Web.AspNetCore v5.1 needs help from middleware for ${aspnet-request-posted-body} to work:
app.UseMiddleware<NLog.Web.NLogRequestPostedBodyMiddleware>();

Manage logging configuration with NLog in .NET Core 3

I'm using NLog in a .NET Core 3.1 worker service application.
Following the tutorial of NLog I inserted an nlog.config file to manage the configuration.
Now I'm confused because I have three points where I configure the logging:
In the code where I need to create a logger in a dependency injection context
// Other code...
services.AddScoped<IApplyJcdsCommandsJob, ApplyJcdsCommandsJob>(provider =>
{
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.ClearProviders()
.AddFilter("Microsoft", Microsoft.Extensions.Logging.LogLevel.Trace)
.AddFilter("System", Microsoft.Extensions.Logging.LogLevel.Trace)
.AddFilter("ApplyJcdsCommandsJob", Microsoft.Extensions.Logging.LogLevel.Trace)
//.AddConsole()
//.AddEventLog();
.AddNLog(configuration);
});
Microsoft.Extensions.Logging.ILogger logger = loggerFactory.CreateLogger<CommandsJob>();
return new CommandsJob(logger);
})
// Other code...
In appSettings.json
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace",
"System": "Trace",
"Microsoft": "Trace"
}
}
}
In NLog.config
The default config file produced by the nuget package installation:
<!-- a section of the config -->
<targets>
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="f" />
</rules>
<!-- ... -->
What I see is that if I remove the Nlog.config file, the log file will not be created.
Other changes seam to have no effect.
How are this configurations related?
What is the best way to switch on/off the logging and set the level?
People that decide to use NLog usually also want to disable all MEL-filtering to avoid the confusion with two filtering systems. So the NLog wiki-tutorial is targeted those users.
I guess people who are MEL-users first will probably just use new HostBuilder().CreateDefaultBuilder().Build() (Will setup everything with all guns enabled).
But if staying with the simple example, then you need to remove:
loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
And add:
loggingBuilder.AddConfiguration(config.GetSection("Logging"));
So it looks like this:
serviceCollection.AddLogging(loggingBuilder =>
{
loggingBuilder.ClearProviders();
loggingBuilder.AddConfiguration(config.GetSection("Logging"));
loggingBuilder.AddNLog(config);
})
ILoggingBuilder.AddConfiguration can be found at Nuget: Microsoft.Extensions.Logging.Configuration
AddNLog registers NLog like any other Microsoft Extension Logger (MEL) LoggingProvider (Similar to AddConsole).
This means NLog only gets log-output that has been "approved" by the MEL-ILogger. So any filtering configured in MEL will prevent logevents from reaching NLog.
NLog still has the ability to redirect based on Logger-names and LogLevel-severity to the wanted NLog-targets.
You can decide if you want to use MEL-Filtering or NLog-Filtering, or a combination of both. But if you just want to use "pure" NLog then just create an instance of NLog.Extensions.Logging.NLogLoggerFactory. It is a specialized ILoggerFactory that ignores MEL-Filtering-Configuration.
Btw. it is a little weird that you create an isolated LoggerFactory for each CommandsJob-instance. Would think that you would register the type in the dependency injection-framework, and let it inject constructor-parameters. See also this example:
https://github.com/NLog/NLog.Extensions.Logging/blob/master/examples/NetCore2/ConsoleExample/Program.cs
Where LoggerFactory is created with AddLogging(...) and where the Runner is registered in ServiceCollection for dependency-injection. When creating instance of Runner then dependency-injection will automatically provide ILogger as constructor-parameter.

Acces-Control-Allow-Origin works with Web.config (IIS7) but not with (WebApiConfig.cs) ASP.NET Cross Origin support

For a project i want to load and view a pdf file with angular-pdfjs. The team uses ASP.net Cross Origin, to Allow-Acces-Control, Headers, Credentials etc.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Replace the default implementation of the ITraceWriter with our custom logger.
config.Services.Replace(typeof (ITraceWriter), new GlobalTraceLogger());
// Replace the default exception logger to be able to log exceptions with NLog
config.Services.Replace(typeof (IExceptionLogger), new GlobalExceptionLogger());
// Replace the default exceptionhandler to be able to handle exceptions globally
config.Services.Replace(typeof (IExceptionHandler), new GlobalExceptionHandler());
// We must enable cors, because otherwise we are not able to commuincate with a java script client
// TODO: We need to restirct the requested resource. Do not allow every origin!
// Do not run this in prodocutive environment
var cors = new EnableCorsAttribute("*", "*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
config.MapHttpAttributeRoutes();
// Make the default return type JSON
var appXmlType =
config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
This works good so far, but if i want to load my pdf file with angular-pdfjs, i get a Cross Origin Error, because Allow-Acces-Control-Origin "*" didn't works for my pdf-url.
(https://img3.picload.org/image/roirrgcw/corsworksnot.png)
But if i using instead of ASP.net Cross Origin Support the Allow-Access-Control of IIS7 in Web.config:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested- With, Content-Type, Accept" />
<add name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" />
<add name="Access-Control-Allow-Credentials" value="true" />
</customHeaders>
</httpProtocol>
it works and the pdf will be loaded correctly.
(https://picload.org/image/roirrgci/corsworks.jpg)
But the problem is, at the moment the page is loaded via "file://" and so i get
an error because there is no Access-Control-Allow-Origin for 'null'. That means, my pdf is loading correctly this way, but the login, pictures... won't be loaded anymore. So my question is, if someone knows how i can change the WebApiConfig-Implementation that my pdf-file get an Access-Controll-Allow as well. Or maybe can someone tell where the error could be.
For information:
Thats the way i'm loading the pdf with angular-pdfjs:
<!---------------------------THE PDF VIEWER DIRECTIVE------------------------->
<div pdf-viewer="options" pdf-url="pdfUrl" id="my-viewer" class="col col-lg-10"></div>
<!---------------------------THE PDF VIEWER DIRECTIVE------------------------->
and thats the url, i'm using:
function PdfviewController(ebmGuideLineService, mediaService, $scope, $window) {
var vm = this;
$scope.pdfUrl = 'http://localhost:3787/NCCN_Evidence_Blocks_Melanoma.pdf';
$scope.options = { mouseZoom: false, mousePan: false };
Please tell me, if you need more informations and thank you for your help.

Hangfire.io Dashboard mapped to IIS Virtual Directory

I'm having trouble getting the Hangfire (1.5.8) dashboard to work inside of an IIS Virtual Directoy. Everything works beautifully in my dev environment where my application is simply mapped to the root of localhost. Our beta server, on the other hand, uses Virtual Directories to separate apps and app pools.
It's an ASP.Net MVC site using Hangfire with an OWIN Startup class. It gets deployed to http://beta-server/app-name/. When I attempt to access either http://beta-server/app-name/hangfire or http//beta-server/hangfire I get a 404 from IIS.
For the purposes of troubleshooting this, my IAuthenticationFilter simply returns true.
Here is my Startup.cs, pretty basic:
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
GlobalConfiguration.Configuration
.UseSqlServerStorage(new DetectsEnvironment().GetEnvironment());
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
AuthorizationFilters = new[] {new AuthenticationFilter()}
});
app.UseHangfireServer();
}
}
Does anyone have a working implementation that gets deployed to a Virtual Directory? Are there any OWIN middleware admin/management tools I can use to dig into what URL is getting registered within IIS?
I ended up fixing this simply by adding the HTTPHandler to the section in web.config.
<system.webServer>
<handlers>
<add name="hangfireDashboard" path="hangfire" type="System.Web.DefaultHttpHandler" verb="*" />
</handlers>
</system.webServer>
I had a similar issue in ASP.NET Core 2.0 and it required proper authorization setup (I use a middleware to protect the route, so I did not rely on authorization in my example):
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new [] {new HangfireDashboardAuthorizationFilter()}
});
/// <summary>
/// authorization required when deployed
/// </summary>
public class HangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter
{
///<inheritdoc/>
public bool Authorize(DashboardContext context)
{
// var httpContext = context.GetHttpContext();
// Allow all authenticated users to see the Dashboard (potentially dangerous).
// handled through middleware
return true; // httpContext.User.Identity.IsAuthenticated;
}
}
There is not need to change anything in web.config.
For more information check Hangfire documentation about this topic.
I had the exact same problem. In my case, this was because of bad configuration - the Startup class was not called. So try to add the following to your config file:
<add key="owin:appStartup" value="YourProject.YourNamespace.Startup, YourProject" />
<add key="owin:AutomaticAppStartup" value="true" />
Hope this helps.
Martin

Resources