ASP.NET Core 2 not reading endpoints from appsettings - kestrel-http-server

I'm trying to use ASP.NET Core 2.0's new way of configuring Kestrel endpoints via settings in appsettings.json. This was demonstrated at the Microsoft Build 2017 event (see it on YouTube timeframe 21:00-26:20).
The speaker said (and demonstrated) that the following would configuring the listening ports on Kestrel:
{
"Message": "TEST!!",
"Kestrel": {
"Endpoints": {
"Localhost": {
"Address": "127.0.0.1",
"Port": "5011"
}
}
}
}
But this hasn't worked for me. When I use 'dotnet run', the default port of 5000 is still being used. I know the appsettings.json file is being loaded, because I can use the Message value elsewhere.
From the code on GitHub, I can't see any place where Kestrel is being configured.
Has anyone else been able to get this method to work? I can't understand how it works in the demo but not in my own code.

I know this was asked a while ago, but I think it would make sense to post on this one as the ASP.NET Core framework has been enhanced to support Kestrel endpoint configuration from application settings since this question was initially asked.
Please find below more details and a little summary on how to achieve that in ASP.NET Core 2.1 and earlier versions.
It is possible to configure Kestrel endpoints in application settings starting from ASP.NET Core version 2.1, as also stated in the documentation.
For further details, it is possible to check in the actual WebHost.CreateDefaultBuilder() implementation:
from release 2.1, it loads the Kestrel configuration section, while on ASP.NET Core earlier versions it is necessary to explicitly call UseKestrel() when setting up your WebHost.
ASP.NET Core 2.1 setup
On ASP.NET Core 2.1, you can setup Kestrel to listen at http://127.0.0.1:5011 by simply providing the following appsettings.json:
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://127.0.0.1:5011"
}
}
}
}
Note you will need to use Url instead of Address and Port.
ASP.NET Core 2.0 setup
On ASP.NET Core 2.0 this very same behavior can be achieved by explicitly calling UseKestrel() in Program.cs. You may use an extension method to keep your Program.cs clean:
public class Program {
public static void Main(string[] args) {
// configuration
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.AddCommandLine(args)
.Build();
// service creation
var webHost = WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseKestrelEndpoints(config) // extension method to keep things clean
.UseConfiguration(config)
.UseApplicationInsights()
.Build();
webHost.Run();
}
}
The UseKestrelEndpoints() is an extension method that looks into the actual configuration settings and setup Kestrel options accordingly:
public static IWebHostBuilder UseKestrelEndpoints(this IWebHostBuilder webHostBuilder, IConfiguration configuration) {
var endpointSection = configuration.GetSection("kestrel:endpoints");
if (endpointSection.Exists()) {
webHostBuilder.UseKestrel(options => {
// retrieve url
var httpUrl = endpointSection.GetValue<string>("http:url", null);
/* setup options accordingly */
});
}
return webHostBuilder;
}
If needed, you may setup HTTPS in a similar way.

Unfortunately that doesn't work anymore. Check out How to use Kestrel in ASP.NET Core apps . So it doesn't really work with settings anymore. You can do something like the this in you program.cs. Here's I'm setting things up to use https locally but you get the general idea...
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
IHostingEnvironment env = null;
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((hostingContext, config) =>
{
env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
if (env.IsDevelopment())
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
.UseKestrel(options =>
{
if (env.IsDevelopment())
{
options.Listen(IPAddress.Loopback, 44321, listenOptions =>
{
listenOptions.UseHttps("testcert.pfx", "ordinary");
});
}
else
{
options.Listen(IPAddress.Loopback, 5000);
}
})
.Build();
}
}
}

Related

.NET Core 2: Force authentication handler to run before middleware?

Ripping api endpoints out of a .NET Core 1 web project to a .NET Core 2 api-only project. My experience with auth (both -orization and -entication) is minimal at best, mainly because most projects I've worked on have already been setup for auth and/or it's been an AD environment.
The API portion of the site uses a pre-shared token to be included in the header of every request. This token is the key to all auth, user identification, permissions, etc etc. The user info (ie. who are you and what can you do) is contained in a custom CurrentContext class.
The Core 1 project uses middleware (ContextMiddleware) to init the CurrentContext instance that is registered in DI as scoped. By the time the ContextMiddleware class is called, the custom auth handler has already been called, the necessary header token has already been examined, authentication checks have passed, and a principal has been created. Thus, the ContextMiddleware class, which heavily depends on the principal existing, can build the CurrentContext and the truckload of information needed to know who's calling.
The Core 2 project ends up running ContextMiddleware before the authentication handler, and I can't figure out how to force the order of those two to swap.
Relevant code snippets:
public class Startup {
public void ConfigureServices(IServiceCollection services) {
// ...
// https://geeklearning.io/how-to-migrate-your-authentication-middleware-to-asp-net-core-2-0/
services.AddAuthentication( options =>
{
options.DefaultScheme = UserTokenAuthenticationDefaults.AuthenticationScheme;
} ).AddUserTokenAuthentication(UserTokenAuthenticationDefaults.AuthenticationScheme,
UserTokenAuthenticationDefaults.AuthenticationScheme,
o => { } );
// ...
}
public void Configure(IApplicationBuilder app /*...*/) {
if (env.IsDevelopment()){
app.UseDeveloperExceptionPage();
} else {
app.UseExceptionHandler("/error/500");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMiddleware<ContextMiddleware>();
app.UseMvc();
}
}
If more code snippets are needed for further information, please let me know. How do I force my auth handler's HandleAuthenticateAsync() to run before ContextMiddleware is invoked?
I was dealing with this too and we found it best to support a "dynamic" authentication scheme we made up that then allows a selector to be called to make the authentication type decision.
Add a DynamicAuthenticationDefaults.AuthenticationScheme = "Dynamic"; constant somewhere in your codebase per ASP.NET Core standards, then in your startup class' ConfigureServices add the dynamic scheme:
.AddAuthentication( options =>
{
options.DefaultScheme = DynamicAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = DynamicAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = DynamicAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = DynamicAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = DynamicAuthenticationDefaults.AuthenticationScheme;
} )
.AddYourCustomAuthentication( YourCustomAuthenticationDefaults.AuthenticationScheme, YourCustomAuthenticationDefaults.AuthenticationScheme, options => { } )
.AddPolicyScheme( DynamicAuthenticationDefaults.AuthenticationScheme, DynamicAuthenticationDefaults.AuthenticationScheme, options =>
{
options.ForwardDefaultSelector = DynamicAuthenticationSchemaSelector.Evaluate;
} );
then in a custom DynamicAuthenticationSchemaSelector class implement that evaluation method:
public static class DynamicAuthenticationSchemaSelector
{
public static string Evaluate( HttpContext context )
{
string result;
var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
if( !string.IsNullOrEmpty( authHeader ) && authHeader.StartsWith( YourCustomAuthenticationDefaults.AuthenticationScheme ) )
{
result = YourCustomAuthenticationDefaults.AuthenticationScheme;
}
else
{
result = IdentityConstants.ApplicationScheme;
}
return result;
}
}
You'll get the proper authentication middleware handling.
You don't need to call this "dynamic" either and it's just for following best practices / patterns -- any string will suffice.

AWS Elastic Beanstalk environment variables in ASP.NET Core 1.0

How do I get environment variables from elastic beanstalk into an asp.net core mvc application? I have added a .ebextensions folder with app.config file in it with the following:
option_settings:
- option_name: HelloWorld
value: placeholder
- option_name: ASPNETCORE_ENVIRONMENT
value: placeholder
The .ebextensions folder is included in the publish package.
On deployment, both the variables are visible in the aws elasticbeanstalk console at Configuration > Software Configuration > Environment Variables
However, when I try to read the variables in the application, none of the below options are working:
Environment.GetEnvironmentVariable("HelloWorld") // In controller
Configuration["HelloWorld"] // In startup.cs
Any ideas on what I could be missing? Thanks.
I just implemented a slightly other solution which injects the beanstalk environment variables to the program so that you may access them by Environment.GetEnvironmentVariable():
private static void SetEbConfig()
{
var tempConfigBuilder = new ConfigurationBuilder();
tempConfigBuilder.AddJsonFile(
#"C:\Program Files\Amazon\ElasticBeanstalk\config\containerconfiguration",
optional: true,
reloadOnChange: true
);
var configuration = tempConfigBuilder.Build();
var ebEnv =
configuration.GetSection("iis:env")
.GetChildren()
.Select(pair => pair.Value.Split(new[] { '=' }, 2))
.ToDictionary(keypair => keypair[0], keypair => keypair[1]);
foreach (var keyVal in ebEnv)
{
Environment.SetEnvironmentVariable(keyVal.Key, keyVal.Value);
}
}
Simply call SetEbConfig(); before building your webhost. With this solution, also AWS SDK does read it's settings like AWS_ACCESS_KEY_ID correctly.
Had the same problem, and just received a reply from AWS support about this issue. Apparently environment variables are not properly injected into ASP.NET Core applications in elastic beanstalk.
As far as I know, they're working to fix the problem.
The workaround is to parse C:\Program Files\Amazon\ElasticBeanstalk\config\containerconfiguration into the configuration builder. This file is part of your elastic beanstalk environment and should be accessible upon deploying your project.
First add the file:
var builder = new ConfigurationBuilder()
.SetBasePath("C:\\Program Files\\Amazon\\ElasticBeanstalk\\config")
.AddJsonFile("containerconfiguration", optional: true, reloadOnChange: true);
Then access the values:
var env = Configuration.GetSection("iis:env").GetChildren();
foreach (var envKeyValue in env)
{
var splitKeyValue = envKeyValue.Value.Split('=');
var envKey = splitKeyValue[0];
var envValue = splitKeyValue[1];
if (envKey == "HelloWorld")
{
// use envValue here
}
}
Courtesy of
G.P. from
Amazon Web Services
I implemented the other answer to create a convenient workaround to load the environment properties from Elastic Beanstalk directly into your ASP.NET Core app configuration.
For ASP.NET Core 2.0 - edit your Program.cs
Note that this WebHost build was taken from the source code of WebHostBuilder.CreateDefaultBuilder()
https://github.com/aspnet/MetaPackages/blob/dev/src/Microsoft.AspNetCore/WebHost.cs
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace NightSpotAdm
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
// TEMP CONFIG BUILDER TO GET THE VALUES IN THE ELASTIC BEANSTALK CONFIG
IConfigurationBuilder tempConfigBuilder = new ConfigurationBuilder();
tempConfigBuilder.AddJsonFile(
#"C:\Program Files\Amazon\ElasticBeanstalk\config\containerconfiguration",
optional: true,
reloadOnChange: true
);
IConfigurationRoot tempConfig = tempConfigBuilder.Build();
Dictionary<string, string> ebConfig = ElasticBeanstalk.GetConfig(tempConfig);
// START WEB HOST BUILDER
IWebHostBuilder builder = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory());
// CHECK IF EBCONFIG HAS ENVIRONMENT KEY IN IT
// IF SO THEN CHANGE THE BUILDERS ENVIRONMENT
const string envKey = "ASPNETCORE_ENVIRONMENT";
if (ebConfig.ContainsKey(envKey))
{
string ebEnvironment = ebConfig[envKey];
builder.UseEnvironment(ebEnvironment);
}
// CONTINUE WITH WEB HOST BUILDER AS NORMAL
builder.ConfigureAppConfiguration((hostingContext, config) =>
{
IHostingEnvironment env = hostingContext.HostingEnvironment;
// ADD THE ELASTIC BEANSTALK CONFIG DICTIONARY
config.AddJsonFile(
"appsettings.json",
optional: true,
reloadOnChange: true
)
.AddJsonFile(
$"appsettings.{env.EnvironmentName}.json",
optional: true,
reloadOnChange: true
)
.AddInMemoryCollection(ebConfig);
if (env.IsDevelopment())
{
Assembly appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
.UseIISIntegration()
.UseDefaultServiceProvider(
(context, options) => { options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); })
.ConfigureServices(
services =>
{
services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
});
return builder.UseStartup<Startup>().Build();
}
}
public static class ElasticBeanstalk
{
public static Dictionary<string, string> GetConfig(IConfiguration configuration)
{
return
configuration.GetSection("iis:env")
.GetChildren()
.Select(pair => pair.Value.Split(new[] { '=' }, 2))
.ToDictionary(keypair => keypair[0], keypair => keypair[1]);
}
}
}
For ASP.NET Core 1.0
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile(#"C:\Program Files\Amazon\ElasticBeanstalk\config\containerconfiguration", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
var config = builder.Build();
builder.AddInMemoryCollection(GetEbConfig(config));
Configuration = builder.Build();
}
private static Dictionary<string, string> GetEbConfig(IConfiguration configuration)
{
Dictionary<string, string> dict = new Dictionary<string, string>();
foreach (IConfigurationSection pair in configuration.GetSection("iis:env").GetChildren())
{
string[] keypair = pair.Value.Split(new [] {'='}, 2);
dict.Add(keypair[0], keypair[1]);
}
return dict;
}
Instead of having to parse the containerconfiguration you can leverage the ebextensions options to set the variable as part of your deploy process:
commands:
set_environment:
command: setx ASPNETCORE_ENVIRONMENT "Development" /M
This will set a global environment variable as part of your application deployment. This variable use-case is officially supported and documented by Microsoft.
After deploying your app you can verify the setting is set correctly in the EC2 instance:
AWS addressed this issue in the Elastic Beanstalk Windows Server platform update on June 29, 2020:
Previously, Elastic Beanstalk didn't support passing environment variables to .NET Core applications and multiple-application IIS deployments that use a deployment manifest [1]. The Elastic Beanstalk Windows Server platform update on June 29, 2020 [2] now fixes this gap. For details, see Configuring your .NET environment in the Elastic Beanstalk console [3].
[1] https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/dotnet-manifest.html
[2] https://docs.aws.amazon.com/elasticbeanstalk/latest/relnotes/release-2020-06-29-windows.html
[3] https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_NET.container.console.html#dotnet-console
Above solution doesnt helped me to load config file based on enviroment settings. So here is my solution AWS ElasticBeansTalk "hack"
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{GetEnvVariableAWSBeansTalkHack(env)}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
private static string GetEnvVariableAWSBeansTalkHack(IHostingEnvironment env)
{
var config = new ConfigurationBuilder()
.AddJsonFile(#"C:\Program Files\Amazon\ElasticBeanstalk\config\containerconfiguration", optional: true, reloadOnChange: true).Build();
Dictionary<string, string> dict = new Dictionary<string, string>();
foreach (IConfigurationSection pair in config.GetSection("iis:env").GetChildren())
{
string[] keypair = pair.Value.Split(new[] { '=' }, 2);
dict.Add(keypair[0], keypair[1]);
}
return dict.ContainsKey("ASPNETCORE_ENVIRONMENT")
? dict["ASPNETCORE_ENVIRONMENT"]
: env.EnvironmentName;
}
You can create an implementation of Microsoft.Extensions.Configuration.
Also available at https://gist.github.com/skarllot/11e94ed8901a9ddabdf05c0e5c08dbc5.
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using System.IO;
using System.Linq;
namespace Microsoft.Extensions.Configuration.AWS
{
public class AmazonEBConfigurationProvider : ConfigurationProvider
{
private const string ConfigurationFilename = #"C:\Program Files\Amazon\ElasticBeanstalk\config\containerconfiguration";
public override void Load()
{
if (!File.Exists(ConfigurationFilename))
return;
string configJson;
try
{
configJson = File.ReadAllText(ConfigurationFilename);
}
catch
{
return;
}
var config = JObject.Parse(configJson);
var env = (JArray)config["iis"]["env"];
if (env.Count == 0)
return;
foreach (var item in env.Select(i => (string)i))
{
int eqIndex = item.IndexOf('=');
Data[item.Substring(0, eqIndex)] = item.Substring(eqIndex + 1);
}
}
}
public class AmazonEBConfigurationSource : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new AmazonEBConfigurationProvider();
}
}
public static class AmazonEBExtensions
{
public static IConfigurationBuilder AddAmazonElasticBeanstalk(this IConfigurationBuilder configurationBuilder)
{
configurationBuilder.Add(new AmazonEBConfigurationSource());
return configurationBuilder;
}
}
}
Then use with your ConfigurationBuilder:
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true)
.AddAmazonElasticBeanstalk() // <-- Merge with other sources
.AddEnvironmentVariables();
.NET Core 2 + posrgresql RDS
Further to #sebastian's great answer above, I found that the settings were in a different part of the file, viz. plugins:rds:env.
Also there was no need to split on =, so the parsing code I have is:
private static void SetEbConfig()
{
var tempConfigBuilder = new ConfigurationBuilder();
tempConfigBuilder.AddJsonFile(
#"C:\Program Files\Amazon\ElasticBeanstalk\config\containerconfiguration",
optional: true,
reloadOnChange: true
);
var configuration = tempConfigBuilder.Build();
var ebEnv = configuration.GetSection("plugins:rds:env")
.GetChildren()
.ToDictionary(child => child.Key, child => child.Value);
foreach (var keyVal in ebEnv)
{
Environment.SetEnvironmentVariable(keyVal.Key, keyVal.Value);
}
}
The relevant (and redacted ;-)) JSON is as follows:
{
"plugins": {
"rds": {
"Description": "RDS Environment variables",
"env": {
"RDS_PORT": "....",
"RDS_HOSTNAME": "....",
"RDS_USERNAME": "....",
"RDS_DB_NAME": "....",
"RDS_PASSWORD": "...."
}
}
}
}
(This reply is separate since I don't have rep to comment...)
This can definitely be done in an .ebextensions folder. Simply create a new file in your .ebextensions folder (I used a name of "options.config"), mark it as "copy if newer" or "copy always" and make sure you use the option_settings header with a aws:elasticbeanstalk:application:environment namespace:
option_settings:
aws:elasticbeanstalk:application:environment:
MyEnvVar: SomeValue
EDIT: I forgot to include a link to the docs!
https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-softwaresettings.html
Update for ASP.net Core 3
Set ASPNETCORE_ENVIRONMENT environment variable in Elastic Beanstalk to Staging, Production, Development...
In Core project create appsettings.Staging.json and this configuration will be used when this the project is deployed.

ASP.Net MVC: How to handle session start and end by owin middle ware

this is sample code for session start and end.
public void Session_OnStart()
{
Application.Lock();
Application["UsersOnline"] = (int)Application["UsersOnline"] + 1;
Application.UnLock();
}
public void Session_OnEnd()
{
Application.Lock();
Application["UsersOnline"] = (int)Application["UsersOnline"] - 1;
Application.UnLock();
}
from MVC6 there will be no global.asax file. so how to handle session start and end by owin middle ware. if possible discuss with code example. thanks
It will be very hard to replicate what you want to do in .NET Core 1.0. The SessionStart and SessionEnd events do not exists (and there is no plan to introduce them) and you can only try to "replicate" them with some tricks.
As clearly pointed out here in this extremely clear article,
Since one of the driving forces behind ASP.NET Core 1.0 is "cloud-readiness", the focus on session management design has been to make it work in a distributed scenario. Session_End only ever fired when sessions used inproc mode (local server memory) and the team have stated that they won't add features that only work locally.
BTW, short answer on session usage:
Setup
Add the following entries to the dependencies node of your project.json file
"Microsoft.AspNet.Session": "1.0.0-rc1-final"
Modify you ConfigureServices to activate the Session:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(30);
options.CookieName = ".MyApplication";
});
}
Then you can activate it like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseSession();
//removed for brevity
}
Usage
HttpContext.Session.SetString("Name, "Foo");
HttpContext.Session.SetInt32("Age", 35);
or
var name = HttpContext.Session.GetString("Name");
var age = HttpContext.Session.GetInt32("Age");

How to add logs in asp.net vNext

I need to set up logs in my asp.net application. It's easy to add output to the console, but I need to configure it in Azure. I don't know how to do it. I need to log all information that occurs with my app into some file and read it.
The ILoggerFactory allows an app to use any implementation of ILogger and ILoggerProvider.
For details on how to implement the interfaces properly, look at the framework's ConsoleLogger and ConsoleLoggerProvider. See also the ASP.NET Core documentation on logging.
Here is a minimal example of a custom ILogger to get started. This is not production code, rather, it demos enough technical depth either to write your own ILogger or to use one from the community.
project.json
"dependencies": {
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final"
}
MyLoggingProvider.cs
namespace LoggingExample
{
using Microsoft.Extensions.Logging;
public class MyLoggingProvider : ILoggerProvider
{
public ILogger CreateLogger(string categoryName)
{
return new MyLogger();
}
public void Dispose()
{
// TODO Cleanup
}
}
}
MyLogger.cs
In Azure you will want to write to somewhere other than C:/temp/some-guid.txt. This is enough to get you started, though, with writing your own simple logger.
namespace LoggingExample
{
using System;
using Microsoft.Extensions.Logging;
public class MyLogger : ILogger
{
public void Log(LogLevel logLevel, int eventId, object state,
Exception exception, Func<object, Exception, string> formatter)
{
var builder = new StringBuilder();
if (formatter != null) {
builder.AppendLine(formatter(state, exception));
}
var values = state as ILogValues;
if (values != null) {
foreach (var v in values.GetValues()) {
builder.AppendLine(v.Key + ":" + v.Value);
}
}
var logPath = string.Format("C:/temp/{0}.txt", Guid.NewGuid());
File.WriteAllText(logPath, builder.ToString());
}
public bool IsEnabled(LogLevel logLevel) {
return true;
}
public IDisposable BeginScopeImpl(object state) {
return null;
}
}
}
Startup.cs
Now in startup you can use add your logger via loggerFactory.AddProvider(new MyLoggingProvider()). Every call to the ILogger will now log with your provider.
namespace LoggingExample
{
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Extensions.Logging;
public class Startup
{
public void Configure(
IApplicationBuilder app,
ILoggerFactory loggerFactory)
{
loggerFactory
.AddConsole(minLevel: LogLevel.Verbose)
.AddProvider(new MyLoggingProvider());
app.Run(async (context) =>
{
var logger = loggerFactory.CreateLogger("CatchAll");
logger.LogInformation("Hello logger!");
await context.Response.WriteAsync("Hello world!");
});
}
}
}
MyController.cs
Anywhere that supports dependency injection can now receive an ILogger that will log to all of the providers that we registered in the Startup.Configure method.
namespace LoggingExample
{
using Microsoft.AspNet.Mvc;
using Microsoft.Extensions.Logging;
public class MyController : Controller
{
public MyController(ILogger logger)
{
logger.LogInformation("Logging from my controller");
}
}
}
Log4Net
Use Log4Net. Its a common framework for logging that everyone who follows up on your code will understand, and it lets you do things like attach a new log "destination" on the fly just by editing your config file. It already covers most of the things you'll want to do (like create a separate file for each "day"), and most of the log mining tools out there will be able to read the files l4n creates.
Setting it Up
There are tutorials online for how to get started, but they basically require a few simple steps:
Download the Log4Net nuget package.
Adjust the log settings in your web.config file
Create a static instance of the logger object
Log Stuff wherever you need to. If you decide you want your logger to write to a file, it will. If you add a database writer, it will write to the db too. Want your log entries to show up in console, just add that logger in your default (debug) config.
Once you get it setup, logging is as simple as this code:
...
} catch(SystemException ex) {
logger.Error("This error was thrown by the XXX routine", ex);
}
Hope that's helpful.
Edit: Config File + Core
As #auga points out in his oh-so-helpful comment, config for ASP.Net 5 may require you to read carefully the link I added under step #2 above (configuring your logger). Instead of re-writing someone else's blog post, I'll just link to the article I used to set this up in our ASP.NET 5 environment. Works really well.
If you're reading this post to learn (instead of skimming it to critique), I'd suggest following the links...

NServiceBus Version 5 Asp.NET MVC

We have an ASP.NET MVC 4 website using NServiceBus to send messages to various services. Because our dev environments don't have these services installed, we stub the ServiceBus instance for local development. We use an Autofac Module registered in our Application_Start to set this all up and configure our Bus instance for injection into controllers.
My NSB 3.3 configuration:
public class ServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register<IBus>((c, p) =>
{
var useServiceBus = bool.Parse(ConfigurationManager.AppSettings["UseServiceBus"]);
if (useServiceBus)
{
return NServiceBus.Configure.With()
.Log4Net()
.DefaultBuilder()
.XmlSerializer()
.MsmqTransport().IsTransactional(false).PurgeOnStartup(false)
.MsmqSubscriptionStorage()
.UnicastBus().ImpersonateSender(false)
.CreateBus()
.Start(() => NServiceBus.Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>().Install());
}
else
return c.Resolve<TestServiceBus>();
}).SingleInstance();
}
}
My attempted NSB 5.0.3 configuration:
public class ServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register<IBus>((c, p) =>
{
var useServiceBus = bool.Parse(ConfigurationManager.AppSettings["UseServiceBus"]);
if (useServiceBus)
{
var configuration = new BusConfiguration();
configuration.UsePersistence<RavenDBPersistence>();
configuration.RegisterComponents(r =>
{
r.ConfigureComponent<EnvironmentMessageMutator>(() => new EnvironmentMessageMutator(new DetectsEnvironment().GetEnvironment()), DependencyLifecycle.InstancePerCall);
});
return Bus.Create(configuration);
}
else
return c.Resolve<TestServiceBus>();
}).SingleInstance();
}
}
Bus.Create is failing with an ObjectDisposedException, "Cannot access a disposed object" on CommonObjectBuilder. I can't find any documentation on the correct way to set this up in NSB 5, or how to configure the regular NServiceBus injection to allow us to do the stubbing described above.
(This should be tagged NServiceBus 5, but I don't have the reputation)
Okay, it turns out this is NSB 5's way of telling you you have the ServiceControl monitoring plugins installed in your endpoint, but don't have the appropriate queues on the machine.
Solution is to install ServiceControl.

Resources