Dotnet Core Day

Gökhan GÖKALP

11.11.2017

Devnot

Logging, Caching and Localization in Asp.NET Core 2.0

Agenda

  • Logging
  • Caching
  • Localization
  • References
  • Contacts

Logging

  • In DI system
  • Easy configuration in "Program.cs" instead of "Startup.cs"
  • ASP.NET Core Logging API provides us lots of logging providers

Logging

How to create log?

public class TodoController : Controller
{
    private readonly ILogger<TodoController> _logger;
    public TodoController(ILogger<TodoController> logger)
    {
        _logger = logger;
    }
}
public IActionResult Index()
{
    _logger.LogInformation(1000, "Index method invoked.");
    
    return View();
}
  • Just inject ILogger object
  • Use several logging methods

Logging

How to add providers?

  • No explicit logging configuration in "Startup" class.(like 1.x)
  • Lots of configuration arises by default with new WebHost configuration builder approach.
    • Kestrel
    • IIS configuration
    • Default configuration sources
    • Logging providers

"Console" and "Debug" logging provider default template.

ConfigureLogging((hostingContext, logging) =>
{
 logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
    logging.AddConsole();
    logging.AddDebug();
})

Logging

How to add providers?

Use several built-in logging providers with the "ConfigureLogging()" method on WebHost builder.

public static void Main(string[] args)
{
    BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureLogging((hostingContext, logging) =>
        {   logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
            logging.AddConsole();
        })
        .Build();
  • Console
  • Debug
  • EventSource
  • EventLog
  • TraceSource
  • Azure App Service

Logging

Log Category, Log Levels and Log Event Id

  • Log category specifies event's source.
  • Full qualified class name convention.
info: DotNETCoreDay.Controllers.TodoController[1000] 
Index method invoked. 
DotNETCoreDay.Controllers.TodoController:Information: Index method invoked

Two ways to create log category:

  1. Inject "ILogger<Type>".
  2. "CreateLogger()" method of the "ILoggerFactory".

Two ways to create log level:

  1. Use encapsulate methods - "ILogger.Log()":
    1. LogInformation()
    2. LogCritical()
    3. LogDebug()
    4. LogError()
    5. LogTrace()
    6. LogWarning()
  2. Use "ILogger.Log()" directly.

Logging

Log Category, Log Levels and Log Event Id

Use "log event id" to groupe related messages.

info: DotNETCoreDay.Controllers.TodoController[1002] //Get operations
      Getting item invalidid
warn: DotNETCoreDay.Controllers.TodoController[4000]
      GetById(invalidid) NOT FOUND

Logging

Log Exceptions

Use "LogError()" method.

catch(Exception ex)
{
    _logger.LogError(1001, ex, "Oops!");
}

Logging

Log Filtering Rules

Specify minimum log levels in "appsettings" easily.

{
  "Logging": {
    "Console": {
      "LogLevel":{
        "DotNETCoreDay.Controllers.HomeController" : "Information"
      }
    },
    "LogLevel": {
      "Default": "Debug"
    }
  }
}
  • Logging provider
  • Log category

or in "Program.cs":

WebHost.CreateDefaultBuilder(args)
        .ConfigureLogging((hostingContext, logging) =>
        {
            logging.AddFilter<ConsoleLoggerProvider>("DotNETCoreDay.Controllers.TodoController", 
            LogLevel.Information);
            logging.SetMinimumLevel(LogLevel.Debug); //Default value is "Information"
        })
        .Build();

Logging

Log Scopes

Useful for grouping some logical operations. E.g:

private void DoSomething()
{
    using(_logger.BeginScope("DoSomething scope!"))
    {
        _logger.LogInformation(1000, "DoSomething method invoked.");
        //..
        _logger.LogWarning(1002, "Something happened!");
    }
}
  • TransactionId
  • CorrelationId

To activating scopes:

WebHost.CreateDefaultBuilder(args)
    .ConfigureLogging((hostingContext, logging) =>
    {
        logging.AddConsole(options => options.IncludeScopes = true);
    })
    .Build();
{
  "Logging": {
    "IncludeScopes": true
  }
}

or

Logging

Third-party Logging Providers

Asp.NET Core 2.0 supports popular logging providers. E.g:

  • elmah.io
  • NLog
  • Serilog
  • etc..

Caching

  • Time to market & Performance
  • Caching is meant to reduce the latency and network overhead

Caching

Response Caching Middleware

public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCaching(options => {
        options.UseCaseSensitivePaths = false;
    });
}
  • More extensible
  • Renovated
  • Caching operations handles in Asp.NET Core middleware
  • Inject "Microsoft.AspNetCore.ResponseCaching" package

Configure in "Startup.cs"

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseResponseCaching();
}

Caching

Response Caching Middleware

[ResponseCache(Duration = 20)]  //second
public IActionResult Index()
{
    return View();
}
  • Decorate methods or classes
  • Default in-memory

ResponseCache attribute

  • public
  • max-age
  • private
  • no-store
  • etc...

Some HTTP directives (Cache-Control)

HTTP 1.1 Caching specification

Caching

Response Caching Middleware

ResponseCache attribute - VaryByQueryKeys

Request Result
http://example.com?key1=value1 Returned from server
http://example.com?key1=value1 Returned from middleware
http://example.com?key1=value2 Returned from server
  • Query key based response caching

Caching

Response Caching Middleware

ResponseCache attribute - Vary

  • Specific header info based response caching
[ResponseCache(VaryByHeader = "Accept-Language")]
public IActionResult Index()
{
    return View();
}

Caching

Response Caching Middleware

ResponseCache attribute - Cache Profiles

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options => 
    {
        options.CacheProfiles.Add("Default",
        new CacheProfile()
        {
            Duration = 60 //Second
        })
    });
}
[ResponseCache(CacheProfileName = "Default"]
public IActionResult Index()
{
    return View();
}

Caching

Memory Cache

  • As a service
public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
}
readonly IMemoryCache _memoryCache;

TodoController(IMemoryCache memoryCache)
{
    _memoryCache = memoryCache;
}

CustomerModel GetCustomer(int id)
{
    CustomerModel customerModel;
    if(!_memoryCache.TryGetValue(id, out customerModel))
    {
        customerModel = //...

        var memoryCacheEntryOptions = new MemoryCacheEntryOptions()
        {
            SlidingExpiration = TimeSpan.FromMinutes(5)
        };
        _memoryCache.Set(id, customerModel, memoryCacheEntryOptions);
    }
    return customerModel;
}

Caching

Distributed Caching

  • Built-in distributed cache abstractions
  • Built-in distributed cache implementations
    • "Memory", "Redis" and "SqlServer".
  • Sync & Async methods with the "IDistributedCache" interface

How to use?

  1. Add caching package from NuGet
  2. Configure "IDistributedCache" implementation in "ConfigureServices" method
  3. Inject "IDistributedCache" implementation
services.AddDistributedRedisCache(options => 
{
    options.Configuration = "localhost:6379";
    options.InstanceName = "master";
});
readonly IDistributedCache _distributedCache;

public TodoController(IDistributedCache distributedCache)
{
    _distributedCache = distributedCache;
}

Localization

  • Asp.NET Core provides services and middleware

For localization:

  1. Make the app's content localizable
  2. Provide localized resources
  3. Choose implementation strategy

Localization

Make the App's Content Localizable

  • Like a tradionational Asp.NET resource(.resx) file
  • "IStringLocalizer" and "IStringLocalizer<T>" instead of "Resource.Text"
  • No need to create resource file for the main culture
  • Also “IHtmlLocalizer” and “IHtmlLocalizer<T>
public class TodoController : Controller
{
    private readonly IStringLocalizer<TodoController> _localizer;

    public TodoController(IStringLocalizer<TodoController> localizer)
    {
        _localizer = localizer;
    }

    public IActionResult Index()
    {
        CultureModel cultureModel = new CultureModel();

        cultureModel.Title = _localizer["Merhaba Dünya!"];

        return View(cultureModel);
    }
}

Localization

Make the App's Content Localizable

public class TodoController : Controller
{
    private readonly IStringLocalizer _localizerForShared;

    public CulturesController(IStringLocalizerFactory localizerFactory)
    {
        _localizerForShared = localizerFactory.Create(typeof(SharedResource));

    }
}

IStringLocalizerFactory (e.g shared resources)

Resource path: controller file path

  • Resources/Controllers/TodoController.en.resx

Localization

Make the App's Content Localizable

@using Microsoft.AspNetCore.Mvc.Localization

@inject IViewLocalizer Localizer

@{
    ViewData["Title"] = Localizer["Merhaba Dünya!"];
}

View Localization - IViewLocalizer

Resource path: view file path 

  • Resources/Views/ControllerName/Action.en.resx

Localization

Make the App's Content Localizable

public class CreateCultureModel
{
    [Required(ErrorMessage = "This field is required.")]
    public string Name { get; set; }
}

DataAnnotations Localization

  • Already localized with the "IStringLocalizer<T>

Localization

Configuring Localization

public void ConfigureServices(IServiceCollection services)
{
    services.AddLocalization(options => {
        options.ResourcesPath = "Resources";
    });

    services.AddMvc()
 	.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization(options =>
    {
        options.DataAnnotationLocalizerProvider = (type, factory) => {
            return factory.Create(typeof(SharedResource));
        }
    });
}


public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    var lockOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();

    app.UseRequestLocalization(lockOptions.Value);
}

How to add localization to service collection?

Localization

Configuring Localization

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<RequestLocalizationOptions>(options => {
        var supportedCultures = new List<CultureInfo>
        {
            new CultureInfo("tr-TR"),
            new CultureInfo("en-US")
        };

        options.DefaultRequestCulture = new RequestCulture("tr-TR");
        options.SupportedCultures = supportedCultures;
        options.SupportedUICultures = supportedCultures;
    });
}

SupportedCultures(date, currency etc.) and SupportedUICultures(translate)

  • Configure with the "RequestLocalizationOptions

Localization

Implement a Strategy to Select the Language

3 options with RequestLocalization

  • QueryStringRequestCultureProvider
    • First provider, e.g: http://localhost/?culture=en-US&ui-culture=en-US
  • CookieRequestCultureProvider
    • To track user selected culture
  • AcceptLanguageHeaderRequestCultureProvider
    • The predefined culture of user's browser

References

Contacts

Dotnet Core Day Seminar

By Gökhan Gökalp

Dotnet Core Day Seminar

Logging, Caching and Localization in Asp.NET Core 2.0

  • 1,136