I need a way to send build events/progress during the build process.
All I have is roughly:
var proj = Microsoft.Build.Evaluation.Project(csprojDir);
var consoleLogger = new Microsoft.Build.Logging.ConsoleLogger();
proj.Build(consoleLogger);
But I can't find a way to handle events (e.g. some Action or delegate of any kind that receives events).
Any idea how catch those?
Write your own custom logger and pass an instance of your logger to the Build method. There are overloads of Build that accept an IEnumerable<ILogger> so you can pass both the consoleLogger and your custom logger.
ILogger defines an Initialize(IEventSource) method. IEventSource has the events you are probably looking for.
There is example code (the same example) in both the IEventSource Interface and ILogger Interface documentation.
Related
I'm working with a codebase (Minimal APIs : .NET 6) which exposes a custom middleware (UseCustomMiddleware) that is added to IApplicationBuilder via extension methods.
The second parameter of UseCustomMiddleware is a Func<HttpRequest, Identity, Message, ... Task<(bool Pass, Error Error)> that act as a predicate for providing authentication mechanism.
Here's the layout in Program.cs:
builder.Services.AddScoped<AuthenticationService>();
var app = builder.Build();
app.UseCustomMiddleware<IContract,Methods>("/", async (httpRequest, accessibility, message, ...) =>
{
//resolving dependencies here is not a problem.
var authenticationService = app.Services.CreateScope().ServiceProvider.GetRequiredService<AuthenticationService>();
//the rest of logic continues...
});
Everything works fine but the logic inside lambda is getting lengthier and lengthier and I need to move that to a separate class file.
I could create a static class and define the same static method with the signature of Func<...> and reference it in place of lambda but then I don't know how to resolve dependencies in there.
What is the proper way to achieve this?
Not sure what UseCustomMiddleware is but you don't need app.Services.CreateScope().ServiceProvider... (also you don't dispose the scope which is bad). Middleware should have access to HttpContext, which has RequestServices property which you should use to resolve services. In theory you can try to get it from HttpRequest:
app.UseCustomMiddleware<IContract,Methods>("/", async (httpRequest, accessibility, message, ...) =>
{
var authenticationService = httpRequest.HttpContext.RequestServices.GetRequiredService<AuthenticationService>();
});
Also see samples in the docs, especially for middlewares extracted into classes, I would argue they are more suitable for complex logic then ones with Func handlers.
Weird and very specific question. Right now we have a logging interface that uses the CallerMemberNameAttribute like so:
interface ILogger
{
void Info(string message, [CallerMemberName] string memberName = "");
}
Which is all great and perfect and works fine in our implementation. However, a requirement has come up that we need to write some functions that can be invoked in our process and also elsewhere, and that these functions need to use a defined ITracer class that looks like the following:
interface ITracer
{
void TraceInformation(string message);
}
So when run in our environment, these functions should log to our current logging infra, but when run in the other environment, it has it's own ITracer set that does its own thing. So I wrote a shim that just passes the messages through when called in our environment:
class Logger : ILogger
{
public void Info(string message, [CallerMemberName] string memberName = "") => // log some stuff
public ITracer GetTraceWriter() => return new TraceWriter(this);
}
class TraceWriter : ITracer
{
public TraceWriter(ILogger logger) => this.logger = logger;
public void TraceInformation(string message) => this.logger.Info($"{message}");
}
This works fine, but the memberName is part of the log message that is output, and with this implementation, when the TraceWriter starts logging, it always has the memberName equal to TraceInformation. Is there a way I can pass this parameter attribute through the function call somehow? The main issue here is that I cannot change the ITracer interface.
Solutions thought of but can't get to work:
Change the TraceInformation call in ITracer to return a function call to ILogger.Info that could be invoked directly from the method (Cannot do this because I cannot change the ITracer interface)
Something you could do, which may or may not be ideal, would be to use the StackFrame class to look up the stack to the calling function.
You can use this to iterate through the stack to either find (or exclude) specific types, or perhaps types that implement a specific interface - or more simply to just 'up' the stack a specific number of frames.
If you included this in your implementation of void Info(..) you could access method names as
# get the name of the current method (i.e. Info)
new StackFrame(0, false).GetMethod().Name
# get the name of the calling method (i.e. TraceInformation)
new StackFrame(1, false).GetMethod().Name
# get the name of the parent calling method - what you are looking for
new StackFrame(2, false).GetMethod().Name
Of course, you then need to reconcile when the method is called from the ITracer object and when it is called directly. You could also get calling objects, inspect what interfaces they implement, and record appropriate method name.
This all uses reflection so you will need to consider performance impact, however I expect that CallerMemberName also uses reflection / may have similar impact.
I am right now working with Logging using ASP.NET Core LoggerFactory (Serilog extension). I want to put logs in controller and business service methods. I have done that via constructor injection of ILogger like this
In controller:
ILogger<HomeController> _logger
In Service:
ILogger<ServiceName> _logger
I believe this will be instantiated at each request, so for each HTTP request, it will create multiple instances of Logger. Say for each controller and each service class but it is bit different than previous methods of logging where we used to create only one logger instance and use that for logging stuff everywhere.
Any downside for this?
This is totally fine. Typically, it's cheap to instantiate a logger, so it's totally OK to do it like that performance-wise.
Still, consider either (1) using the global log instance which Serilog has or (2) using static field initialized in a field declaration. Again, not for the performance reasons, but rather to avoid polluting your constructor with not-so-relevant stuff.
UPD Update on implementing (1)
Basically, it's just a matter of deciding where would you put the logger init code into. In a ASP.NET Core it would be the first line of Main method (that Log is a static class from Serilog namespace):
Log.Logger = new LoggerConfiguration().WriteTo.LiterateConsole(LogEventLevel.Debug, LogTemplate)
.WriteTo.File(#"C:\logs\elbakogdabot.log", LogEventLevel.Debug, LogTemplate)
.Enrich.FromLogContext()
.CreateLogger();
(just to be clear: I took the code from a real project of mine, but the actual configuration of your logger could be different).
Then I would use it anywhere like this:
Log.Warning($"got a message for an unknown user: userid=[{userId}]");
This line could be thrown into any class and you don't have to do any extra initialization for that class.
UPD Update on implementing (2)
I guess in a typical enterprise app it would be inconvenient to have always remember to put the class name in the message every time you logging something. So I would go with static readonly field most of the time. With Serilog you can do it like that:
public class XYZService
{
private static readonly Serilog.ILogger log = Log.ForContext<XYZService>();
...
This way you both won't pollute the constructor, and will get the class name in all of your log messages automatically. I used to have this line in a ReSharper snippet, so I had to just type lg<TAB> in every new class.
I wanted to inject a logger to my controllers and I needed to pass extended info to the logger's constructor. For the purpose I've use RegisterWithContext:
container.RegisterWithContext<Common.Logging.ILogger>(context =>
{
if (context.ServiceType == null && !container.IsVerifying())
{
throw new InvalidOperationException(
"Can't request ILogger directly from container, " +
"it must be injected as a dependency.");
}
return new Common.Logging.NLogLogger(context.ImplementationType.FullName);
});
RegisterWithContext extension method explicitly registers the supplied delegate as Transient.
I need to inject the same Logger (Common.Logging.NLogLogger) in a service which happens to be singleton.
Before upgrading to SimpleInjector 3.0.6 things seemed to work as expected and container.Verify() was quite happy with the whole configuration.
After the upgrade the verifier returns a few errors:
[Lifestyle Mismatch] SearchEngineIndexerService (Singleton) depends on
ILogger (Transient). [Lifestyle Mismatch] MembershipService (Web
Request) depends on ILogger (Transient).
and it makes sense. I can understand why that happens and why it should be avoided.
I am trying to avoid the "Do I log too much" syndrome but, actually, I really need to do some logging in a couple of services.
I've tried to use RegisterConditional to register a different logger based on certain conditions but, of course, all the logger now should be registered conditional or I get this exception:
Type ILogger has already been registered as unconditional registration. For non-generic types, conditional and unconditional registrations can't be mixed.
What's the best approach to register a logger as transient for a controller and another one for a singleton service?
The reason you are seeing this exception now, is because v3.0.6 fixed some bugs that prevented the lifestyle mismatch warning from showing up in certain occasions.
It is best to ignore the RegisterWithContext extension method, because it has been superseded by the RegisterConditional method in v3. RegisterConditional however, only allows registering types; not delegates, because delegates allow you to make decisions based on runtime decisions, but it is bad practice to make runtime decisions during object graph resolution.
So instead, it is best to define a proxy logger class that allows forwarding the call to the real logger. For instance:
public sealed class Logger<T> : ILogger
{
private static readonly ILogger logger =
new Common.Logging.NLogLogger(typeof(T).FullName);
// Implement ILogger methods here
void ILogger.Log(string message) {
// Delegate to real logger
logger.Log(message);
}
}
This implementation can be registered as follows:
container.RegisterConditional(typeof(ILogger),
c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
The documentation describes this in more detail.
I have a custom trace listener, with a simple constructor;
public MyTraceListener(ISomething something)
{
_something = something;
}
This is wired to my web app through system.diagnostics in the web.config.. However, it never seems to fire this. How do I get unity to resolve this? Do I need to edit my web.config somehow? If I add a default constructor that fires, so would the correct approach be to have the default constructor do some sort of resolve on itself?
Unfortunately, you're kind of stuck. The tracing system doesn't give you the opportunity to take over object creation, so Unity doesn't get a chance to do constructor injection. The system will new up the trace listener.
The best you can do is to set your dependencies as properties, rather than constructor dependencies, and then grab the trace listener object after it's been created and call container.BuildUp on it to get properties injected.
Did you register ISomething with Unity either in your code or in your web.config?
Something like
var container = new UnityContainer().RegisterType<ISomething, Something>();
or via config file as described [here]
Do you call Unity to resolve your custom TraceListener?
var myTraceListener = container.Resolve<MyTraceListener>();
or do you inject your listener in some other class where it should be used so that Unity resolves the listener as a parameter?