We have a self-hosted WebAPI application using OAuthBearerAuthenticationHandler and I see it logs exception using (ILogger)_logger when an exception is thrown. Since we are using our own logging framework, how can I supply my own ILogger to OAuthBearerAuthenticationHandler?
The solution for us was to set our won 'LoggerFactory'.
IAppBuilder appBuilder ............;
..........
appBuilder.SetLoggerFactory(new OwinLoggerFactory());
And for 'OwinLoggerFactory':
public class OwinLoggerFactory : ILoggerFactory
{
private readonly ILoggerFactory _baseLoggerFactory;
public OwinLoggerFactory()
{
_baseLoggerFactory=new DiagnosticsLoggerFactory();
}
public ILogger Create(string name)
{
//create your own OwinLogger class that implements 'ILogger'
// inside your own OwinLogger class, you may then hook up to any logging Fx you like.
}
}
Related
background
I hope to add additional middleware by registering the plugin without changing the original project code, but how to get the IApplicationBuilder required to register the middleware in the plugin is the biggest problem I currently face.
According to the Hosting Startup Document, the plugin can be registered by inheriting IHostingStartup and loaded automatically when the project starts, E.g:
// plugin
public class MyStartup: IHostingStartup
{
// Implement the IHostingStartup interface
public void Configure(IWebHostBuilder builder)
{
// TODO: I want to get an IApplicationBuilder object to register middleware
}
}
question
How to get IApplicationBuilder object by IWebHostBuilder?
In the official docs, Extend Startup with startup filters explains that IStartupFilter might be useful here:
Use IStartupFilter to configure middleware at the beginning or end of an app's Configure middleware pipeline without an explicit call to Use{Middleware}.
Here's a sample implementation:
public class MyStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseMiddleware<MyMiddleware>();
next(applicationBuilder);
};
}
}
In this example, we're adding MyMiddleware to the beginning of the pipeline, which means that it runs before the rest of the pipeline. To run MyMiddleware at the end of the pipeline, switch the order of app.UseMiddleware and next.
You must also register this implementation with the DI container, like this:
// plugin
public class MyStartup : IHostingStartup
{
// Implement the IHostingStartup interface
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.AddTransient<IStartupFilter, MyStartupFilter>();
});
}
}
Although this works, it's not as flexible as you might need it to be. For example, it doesn't allow you to inject middleware between middleware added by the app.
I'm working with a project which utilizes Simple Injector as dependency injector. On the other hand, this project uses Microsoft.Extensions.Logging in order to log the events that occurs in certain classes.
My technical issue is pretty simple to explain.
I want to register in my DI the ILogger independently of the class T which is being invoked, but I DO NEED to do it from my ILoggerFactory.CreateLogger<T>() method because this gets the logger configuration using Microsoft.Extensions.Configuration.
I need to use something like this in order to instance my logger:
private Microsoft.Extensions.Logging.ILogger CreateLogger<T>()
{
var factory = this.ResolveService<ILoggerFactory>();
var logger = factory.CreateLogger<T>();
return logger;
}
I could achieve the injection by doing:
Container.Register(typeof(ILogger<>), typeof(Logger<>));
And this allows us to resolve something like:
public class SomeApiController : ApiController
{
public SomeApiController(ILogger<SomeApiController> logger)
{
//logger is well instantiated, but doesn't got the configuration
logger.LogInformation("test log.");
}
}
But as I said, this does it without passing through the configuration obtained from the Microsoft.Extensions.Logging.ILoggerFactory class, so this isn't useful.
Is there a way to register ILogger<T> by using my CreateLogger<T>?
Use the following registrations:
container.RegisterInstance<ILoggerFactory>(loggerFactory);
container.RegisterSingleton(typeof(ILogger<>), typeof(Logger<>));
Or, in case you are integrating Simple Injector into a generic host or ASP.NET Core application, make use of the .AddLogging() extension method to even inject a non-generic ILogger into your application components, as demonstrates in this ASP.NET Core Startup class:
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(); // Adds logging to the framework
// AddSimpleInjector enables "cross wiring," which means you can let
// Simple Injector-resolved components to depend on the generic
// ILogger<T> abstraction.
services.AddSimpleInjector(container, options =>
{
options.AddAspNetCore();
// AddLogger allows Simple Injector-resolved components to depend on
// the non-generic Microsoft.Extensions.Logging.ILogger interface.
// Simple Injector will automatically inject the correct ILogger<T>
// for you.
options.AddLogging();
});
}
...
}
For a full example, see the ASP.NET Core and ASP.NET Core MVC Integration Guide.
Letting application components depend on ILogger instead of ILogger<T>, makes your code simpler, easier to test, and less error prone. If you're using Simple Injector without Service Collection integration (as the previous example showed, you can use the following registration to let Simple Injector ensure the correct Logger<T> is still injected whenever an ILogger is injected:
container.RegisterConditional(
typeof(ILogger),
c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
_ => true);
This ensures that every application component gets its own Logger<T> instance, where T is the type of the component the logger is injected into. Take the following class for example that depends on ILogger:
public class ComponentA : IService
{
public ComponentA(ILogger logger) { ... }
}
The above registration will ensure that ComponentA is injected with a Logger<ComponentA>, even though it simply depends on ILogger and not on ILogger<T>.
You can stop reading here if the above suits your needs... or continue reading if you're interested in a more SOLID solution.
A SOLID solution
Instead of letting application components depend on the framework-defined ILogger abstraction, you could also choose to define an application-specific logger abstraction, as prescribed by the Dependency Inversion Principle (DIP).
The DIP states that abstractions should be defined by the application itself—this means you define your own logger abstraction (also see this for an explanation of why you want to do this) and on top of that you build an adapter, much like described here. You can simply derive your generic adapter from the described MicrosoftLoggingAdapter as follows:
public sealed class MicrosoftLoggingAdapter<T> : MicrosoftLoggingAdapter
{
public MicrosoftLoggingAdapter(ILoggerFactory factory)
: base(factory.CreateLogger<T>()) { }
}
Using this generic adapter, you can configure Simple Injector as follows:
container.RegisterInstance<ILoggerFactory>(factory);
container.RegisterConditional(
typeof(MyApplication.Abstractions.ILogger),
c => typeof(MicrosoftLoggingAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
_ => true);
Based on Steven's solution, I post my answer to help anyone else:
private void RegisterServices()
{
Container.Register(ConfigureLogger, Lifestyle.Singleton);
Container.Register(typeof(ILogger<>), typeof(LoggingAdapter<>));
}
private ILoggerFactory ConfigureLogger()
{
LoggerFactory factory = new LoggerFactory();
var config = new ConfigurationBuilder()
.AddJsonFile("logging.json")
.Build();
//serilog provider configuration
var log = new LoggerConfiguration()
//.ReadFrom.Configuration(config)
.WriteTo
.RollingFile(ConfigSettings.LogsPath)
.CreateLogger();
factory.AddSerilog(log);
return factory;
}
public class LoggingAdapter<T> : ILogger<T>
{
private readonly Microsoft.Extensions.Logging.ILogger adaptee;
public LoggingAdapter(ILoggerFactory factory)
{
adaptee = factory.CreateLogger<T>();
}
public IDisposable BeginScope<TState>(TState state)
{
return adaptee.BeginScope(state);
}
public bool IsEnabled(LogLevel logLevel)
{
return adaptee.IsEnabled(logLevel);
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
adaptee.Log(logLevel, eventId, state, exception, formatter);
}
}
As you can see, my solution is using Serilog as a provider for logging in Microsoft.Extensions.Logging.
Hope it helps!
I wanted to know how to inject ILogger into a function in a ASP.NET Core app that's called by a Java client through Thrift.
So a high level code demo of what I want to do:
// ExecuteRequest is called by java client through Thrift
public class ThriftLayer
{
...
public string ExecuteRequest(...params)
{
...
var result = RequestFunc1(...params);
...do processing
return result;
}
...
}
// Contains request functions called by ExecuteRequest
public class ServerRequestHandler
{
...
public string RequestFunc1(...params)
{
return TaskFunc1(...params);
}
....
}
// Functions in this class are called by both the Thrift layer(called by ServerRequestHandler) as well as a Console application
// In Console applications, we can inject the ILogger at Startup - No issues there.
public class TaskFunctions
{
private readonly ILogger<TaskFunctions> _logger;
public TaskFunctions(ILogger<TaskFunctions> logger)
{
_logger = logger;
}
public string TaskFunc1(...params)
{
_logger.logInfo("<log message>");
...do processing
return stringResult;
}
}
So I wanted to know how can I inject ILogger into TaskFunctions while calling from Thrift?
The accepted answer from this question on StackOverflow will help you.
You need to build a ServiceCollection outside of the ASP.NET Core's Startup class.
the MVC part of your application will add those services in Startup.ConfigureServices method
the other part of your application will need to build and then use the service provider as such var taskFunctionsWithInjected = ActivatorUtilities.CreateInstance<TaskFunctions>(serviceProvider); in order to get the dependencies
Essentially, there are two different ways I get ILogger instances. One works perfectly fine, the other doesn't.
I have an Azure Function like this:
class AzureFunctionClass {
private readonly ISomeClass _someclass;
public AzureFunctionClass(ISomeClass someClass){
_someclass = someClass;
}
public Task<IActionResult> AzureFunction(ILogger log){
log.LogInformation("This works, I see this message when run");
_someclass.ExecuteMethod();
}
}
Another class, not containing Azure functions, like this:
class SomeClass : ISomeClass {
private readonly ILogger<SomeClass> _log;
public SomeClass(ILogger log){
_log = log;
}
public void ExecuteMethod(){
_log.LogInformation("This doesn't crash so _log isn't null, but it
doesn't write anything");
}
}
Startup.cs:
class Startup : IWebJobsStartup {
public void Configure(IWebJobsBuilder builder){
builder.Services.AddScoped<ISomeClass, SomeClass>();
builder.Services.AddTransient(typeof(ILogger<>), typeof(Logger<>));
builder.Services.AddScoped<ILogger<SomeClass>, Logger<SomeClass>>();
}
}
And no, I'm afraid that AzureFunctionClass cannot just pass its ILogger instance to ISomeClass as a parameter.
I've also looked everywhere for log files, such as in Azure Storage Explorer, to see if it's possibly just not writing to the Azure Portal console. Every log file I found had logs for the working case described above, and none of them had logs for the other case.
Current syntax shown has some issues with this injected dependencies.
class SomeClass : ISomeClass {
private readonly ILogger _log;
public SomeClass(ILogger<SomeClass> log) {
_log = log;
}
public void ExecuteMethod() {
_log.LogInformation("This doesn't crash so _log isn't null, but it doesn't write anything");
}
}
Second issue is that logging is added by default and your manually added settings are overriding the default setup.
class Startup : FunctionsStartup {
public override void Configure(IFunctionsHostBuilder builder) {
builder.Services.AddScoped<AzureFunctionClass>();
builder.Services.AddScoped<ISomeClass, SomeClass>();
//...
}
}
Technically all you needed to add was your function class and its dependencies.
Reference Use dependency injection in .NET Azure Functions
At present, the function runtime has a bug due to which it filters out any log that is created with a category that doesn't start with string Function..
See these GitHub issues:
#4425 - ILogger is not injected when using new DI functionality
#4345 - Remove filters for ILoggers created by customer DI
The logger injected in the function method is done by the function runtime which creates the logger with category set to Function.<FunctionName>.User. So this gets logged properly. But the logger that is injected into the constructor is done by the asp.net core DI framework, which sets the category name for the logger as Type.FullName (type in your example case is SomeClass). Because it's fullname doesn't start with Function, the lines logged with this category are filtered out.
There are two ways to workaround this.
Option 1: Change host.json to not filter logs from your namespace
{
"version": "2.0",
"logging": {
"logLevel": {
"<YourNameSpace>": "Information"
}
}
}
Option 2: Inject ILoggerFactory in your ctor, and create a logger with a category that won't get filtered
class SomeClass : ISomeClass {
private readonly ILogger _log;
public SomeClass(ILoggerFactory loggerFactory){ // Note that we inject ILoggerFactory
this._log = loggerFactory.CreateLogger(
LogCategories.CreateFunctionUserCategory(this.GetType().FullName)); // Must use CreateFunctionUserCategory to create the log category name otherwise the log gets filtered out.
}
public void ExecuteMethod(){
_log.LogInformation("This should get logged correctly.");
}
}
Note that, ILogger is already registered into DI framework by the function runtime (as mentioned in NKosi's answer), so those lines can be removed.
Given several types of class constructor injection, i.e.;
public class DataService :IDataService
{
public DataService(ILogger logger) { ... }
}
and,
public class Logger
{
public Logger(IDataService service) { ... }
}
or, should I do this instead;
public class DataService : IDataService, ILogger, IDisposable
{
public DataService() { ... }
}
However, I actually don't like to do this on every repository classes or other classes that needs data services and logging at the same time;
public class SomeRepository : IRepostiory
{
public SomeRepository (IDataService service, ILogger logger) { ... }
}
I'm fine with this model;
public interface IRepository : ILogger { ... }
or,
public interface IDataService : ILogger { ... }
Which one is preferred as best practice design? Also, how do we determine if we would like to log the data service process, and at the same time we also would like to log other components that injected ILogger services or using repository?
This is a tongue in cheek answer, but there is no "right" way to implement ILogger. You should probably use the existing framework to construct an ILogger, or hand it over to NLog, log4net, or Serilog. You shouldn't directly implement ILogger unless you plan to build your own logging framework. You should use the logging libary's extension methods to wireup the ILogger Here is an article about this.
Here is a minimal example of how to get console logging up and running:
var hostBuilder = Host.CreateDefaultBuilder().
ConfigureLogging((builderContext, loggingBuilder) =>
{
loggingBuilder.AddConsole((options) =>
{
//This displays arguments from the scope
options.IncludeScopes = true;
});
});
var host = hostBuilder.Build();
var logger = host.Services.GetRequiredService<ILogger<LogTest>>();
//This specifies that every time a log message is logged, the correlation id will be logged as part of it
using (logger.BeginScope("Correlation ID: {correlationID}", 123))
{
logger.LogInformation("Test");
logger.LogInformation("Test2");
}
I'll like to add that the BeginScope method could take any kind of object, not only string. You may pass a more complex object that could represent better the context/scope, like User Id, HostName, CorrelationId, Tennant Id ...
In my case where a created an external Api Logger service, and thus create my own ILogger where BeginScope with those values make more sense.
public IDisposable BeginScope<TState>(TState state)
{
_scopeContextManager = state as ScopeContextManager;
return default!;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
Exception exception, Func<TState, Exception, string> formatter)
{
// use _scopeContextManager
}
I struggled for a long time before figuring this out.