- Create new .NET Core Console App
- Add following code
var loggerFactory = new LoggerFactory()
.AddConsole()
.AddDebug();
- Add packages Microsoft.Extensions.Logging, Microsoft.Extensions.Logging.Console, Microsoft.Extensions.Logging.Debug
- Add code following code
ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Are you OK Computer");
logger.LogError("System failed");
logger.LogCritical("The world collapsed");
- Add new .NET Core Console App SpecificConsoleLoggerProvider
- Copy Program.cs class from previous example
- Add the following code instead the one from
LoggingConsoleDefault step 2
.
loggerFactory.AddProvider(new ConsoleLoggerProvider((text, logLevel) => logLevel >= LogLevel.Verbose , true));
- Show on github how LogLevel.Verbose has changed to LogLevel.Trace https://github.com/aspnet/Logging/blob/9506ccc3f3491488fe88010ef8b9eb64594abf95/src/Microsoft.Extensions.Logging.Abstractions/LogLevel.cs https://github.com/aspnet/Logging/commit/7a05d121d6c120a15d5a2178e3e45d7c400d22c0
- Change to LogLevel.Trace and add the following code
logger.LogTrace("Tracing...");
- Change code from
loggerFactory.AddProvider(
new ConsoleLoggerProvider((text, logLevel) => logLevel >= LogLevel.Trace, true));
loggerFactory.AddProvider(
new ConsoleLoggerProvider((text, logLevel) => logLevel >= LogLevel.Error, true));
- Create new .NET Core Console App ApplicationLogging
- Add file
LoggingHelper.cs
with following content
using Microsoft.Extensions.Logging;
namespace ApplicationLogging
{
public static class LoggingHelper
{
public static ILoggerFactory LoggerFactory { get; } = new LoggerFactory();
public static ILogger CreateLogger<T>() => LoggerFactory.CreateLogger<T>();
public static ILogger CreateLogger(string categoryName) => LoggerFactory.CreateLogger(categoryName);
}
}
Program.cs
file should look like
using Microsoft.Extensions.Logging;
using System;
namespace ApplicationLogging
{
class Program
{
static void Main(string[] args)
{
LoggingHelper.LoggerFactory.AddConsole();
LoggingHelper.CreateLogger<Program>().LogInformation("Before Hello World");
Console.WriteLine($"{Environment.NewLine} Hello World! {Environment.NewLine}");
LoggingHelper.CreateLogger("Program").LogInformation(new EventId(66), "After Hello World");
}
}
}
- Create new .NET Core Console App ScopeConsole
- Add new file
Service.cs
with the following code
using Microsoft.Extensions.Logging;
namespace Scope
{
class Service
{
private readonly ILogger<Service> _logger;
public Service(ILogger<Service> logger)
{
_logger = logger;
}
public void DoStuff()
{
// Some logic before calling DB
using (_logger.BeginScope($"{nameof(DoStuff)} has started DB execution"))
{
_logger.LogInformation("Init some data");
//...
_logger.LogInformation("Getting data from DB");
//...
_logger.LogInformation("Data from DB {}", 42);
}
}
}
}
- Change Main method in
Program.cs
.
Note: includeScopes must be true
static void Main(string[] args)
{
var loggerFactory = new LoggerFactory().AddConsole(includeScopes: true);
ILogger logger = loggerFactory.CreateLogger<Program>();
using (logger.BeginScope("Code block calling Service"))
{
logger.LogInformation("Getting item {ID}", 5);
var service = new Service(loggerFactory.CreateLogger<Service>());
service.DoStuff();
logger.LogWarning("End of block calling Service");
}
}
- Create ASP.NET Core Web Api project
- Set to run as ConsoleApp (Do not use IIS Express)
- Set breakpoint at Startup Configure method and show that LoggerFactory is instantiated.
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
- Where is the magic? What calls Configure() and where the ILoggerFactory instance actually came from and live at. Go to Program.cs -> Main() -> Build() -> F12 (Microsoft.AspNetCore.Hosting.Abstractions)
- Go to Github links https://github.com/aspnet/Hosting/blob/7ac6842d1864fd97c417abd016440893c3384b12/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs#L213 https://github.com/aspnet/Hosting/blob/7ac6842d1864fd97c417abd016440893c3384b12/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs#L237 https://github.com/aspnet/Hosting/blob/7ac6842d1864fd97c417abd016440893c3384b12/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs#L255 https://github.com/aspnet/Hosting/blob/7ac6842d1864fd97c417abd016440893c3384b12/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs#L316
public IWebHost Build()
...
var hostingServices = BuildCommonServices(out var hostingStartupErrors);
...
private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
...
// The configured ILoggerFactory is added as a singleton here. AddLogging below will not add an additional one.
var loggerFactory = _createLoggerFactoryDelegate?.Invoke(_context) ?? new LoggerFactory();
services.AddSingleton(loggerFactory);
_context.LoggerFactory = loggerFactory;
foreach (var configureLogging in _configureLoggingDelegates)
{
configureLogging(_context, loggerFactory);
}
//This is required to add ILogger of T.
services.AddLogging();
- Show AddLogging extension source code from https://github.com/aspnet/Logging/blob/dev/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs#L20
public static class LoggingServiceCollectionExtensions
{
/// <summary>
/// Adds logging services to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddLogging(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
return services;
}
}
- Add new ASP.NET Core Web Api project LoggerFactoryInStartupCtor
- Add private field _logger and change Startp ctor
private readonly ILogger _logger;
public Startup(IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
_logger = loggerFactory.CreateLogger<Startup>();
_logger.LogInformation($"Welcome from {nameof(Startup)}");
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
- Add new ASP.NET Core Web Api project LoggerFactoryInProgramMain
- Show IWebHostBuilder method
public IWebHostBuilder UseLoggerFactory(ILoggerFactory loggerFactory);
- Add the following code to Program.cs
public class Program
{
private static ILogger _logger;
public static void Main(string[] args)
{
var loggerFactory = new LoggerFactory().AddConsole();
_logger = loggerFactory.CreateLogger<Program>();
_logger.LogInformation($"Hello from {nameof(Main)} before building host");
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights()
.UseLoggerFactory(loggerFactory)
.Build();
host.Run();
}
}
- Remove
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
from Configure method in Startup class - Show the working demo
- Bonus: Latest interesting issue example on github aspnet/Announcements#241
- Create new new ASP.NET Core Web Api project LoggingInController
- Add
using Microsoft.Extensions.DependencyInjection;
inValuesController
- Add following fields
private readonly ILogger _loggerFromFactory;
private readonly ILogger _logger;
- Change constructor to
public ValuesController(ILoggerFactory loggerFactory, ILogger<ValuesController> logger)
{
_loggerFromFactory = loggerFactory.CreateLogger<ValuesController>();
_logger = logger;
}
- Change Get Action to
[HttpGet]
public IEnumerable<string> Get()
{
var _loggerFromServices = this.HttpContext.RequestServices.GetService<ILoggerFactory>()
.CreateLogger("Values");
_loggerFromFactory.LogWarning(new EventId(42), "Logger from factory");
_logger.LogWarning(new EventId(43), "Logger from DI");
_loggerFromServices.LogWarning(new EventId(44), "Logger from services");
return new string[] { "value1", "value2" };
}
- Create new empty sln RollingFile
- Create new .NET Core WebApi RollingFile
- Add package reference to
Serilog.Extensions.Logging.File
- Add
loggerFactory.AddFile("Logs/Nemke-{Date}.txt");
to Startup Configure method - Change code to
loggerFactory.AddFile("Logs/log-{Date}.txt", isJson:false, minimumLevel:LogLevel.Trace);
var logger = loggerFactory.CreateLogger<Startup>();
logger.LogTrace(new EventId(), new Exception("Demo"), "Trace message");
logger.LogDebug("Debug message");
logger.LogInformation("Information message");
logger.LogWarning("Warning message");
logger.LogError("Error message");
logger.LogCritical(new EventId(), new FormatException(),"Critical message");
- Show Log txt file (with regular and json entries)
- Do the lab by the link examples at
Before Serilog example, show slides about structure logging.
- Show github serilog sink links https://github.com/serilog/serilog/wiki/Provided-Sinks
- Create new .NET Core WebApi SerilogExample Project
- Add package reference to
Serilog.Extensions.Logging
,Serilog
,Serilog.Sinks.Literate
,Serilog.Sinks.Seq
- Add following code in Startup ctor
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.LiterateConsole()
.WriteTo.Seq("http://localhost:5341")
.CreateLogger();
- Add following code in Configure method
loggerFactory.AddSerilog();
- Change ValuesController