I have a .Net Worker program that uses multiple NuGet packages that log information to ILogger. For each of the NuGet packages, I am delegating same "master" logger object. For each of the log information, I would like to now fetch and send to our internal chat. I trying to create an Event that will execute a "CallMeWhenLog" method every time a new string is added to ILogger.
public class TestClass
{
private readonly ILogger<TestClass> _logger;
public TestClass(ILogger<TestClass> logger)
{
_logger = logger;
}
public async Task Process(Message message,
CancellationToken cancellationToken = default)
{
_logger.LogInformation("New message!");
_logger.LogError("New message!", ex);
}
public void CallMeWhenLog(string loggedMessage)
{
var chatHandler = new ChatHandler();
chatHandler.SendMessage(loggedMessage);
}
}
I think it is possible by creating a subscription to ILogger but I never used events before. It sounds straight forward but I am a bit lost. For example, I would like to call "CallMeWhenLog" method just after _logger.LogInformation and _logger.LogError is executed.
From your comment it seems like you're using (ASP).NET Core.
You may want to look into creating your own Logger - docs, and registering it just like the other three logging provides (e.g. logging.AddEventLog()). You can implement posting to the chat in that custom logger.
Or take a look at the 3rd party providers: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0#third-party-logging-providers
Related
I have an Azure Function that listens for a ServiceBus and one of the roles of the process is to generate some GUIDs.
I can't share my code but it look something like this:
public class ServiceBusListenerFunction
{
private readonly IProcessService _processService;
public ServiceBusListenerFunction(IProcessService processService)
{
_processService = processService;
}
[FunctionName("MyServiceBusFunction")]
public async Task Run([ServiceBusTrigger("my-queue")] string message)
{
await _processService.Run(message);
}
}
I injected IProcessService as Transient in an atempt to inject for every Run call of my function.
My concern are all concurrent calls, even for a very little chance, generate duplicate GUID.
There is a way to manually inject IProccessService and all its dependencies for every call?
Thank you in advance!
i have started in the last few weeks working (or trying it) Simple MVC-App for notifications.
I can log informations to console but how can I add easy an file logger for the notification function for http-requests?.
From the scratch implementing with very much work I can realize it, but without any supporting functions for filelogging. I found the Ilogger to log in the Console. But is there an easy way to switch Ilogger from logging to console logging to file?
public class NotificationsController : ControllerBase
{
private readonly MyConfig config;
private readonly ILogger<NotificationsController> _logger;
public NotificationsController(MyConfig config, ILogger<NotificationsController> logger)
{
this.config = config;
_logger = logger;
}
[HttpGet]
public ActionResult<string> Get()
{
...
string Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
_logger.LogInformation(Message);
...
The example I modified is fromn here:
https://learn.microsoft.com/de-de/learn/modules/msgraph-changenotifications-trackchanges/5-exercise-change-notification
https://github.com/microsoftgraph/msgraph-training-changenotifications/tree/live
Thank you for reaching out. AS.NET Core doesn't include a logging provider for writing logs to files, see documentation on logging in .NET Core and ASP.NET. To write logs to files, consider using a third party logging provider.
Let me know whether this helps and if you have further questions.
I know you can use dependency injection of ILogger in program or controller, so the log message could appear in console and other loggers. However, in a .net core library, I would like to use Trace.TraceError() and Trace.TraceInformation() etc., and even use TraceSource.
Is is possible to make what in Trace.TraceError(...) appear in .NET Core console and other loggers?
Just include an ILogger in the constructor of your libary. Calls to Log methods on the logger will then get passed through to whatever LoggerProviders (Trace, Console etc) have been added in your configuration.
public class MyLib
{
private ILogger _logger;
public MyLib(ILogger<MyLib> logger)
{
_logger = logger;
}
public void DoSomething()
{
_logger.LogInformation("Info")
_logger.LogError("Error")
}
}
I want to register a TelemetryClient in a ServiceCollection using the AddScoped method and call Flush when the client is disposed. I don't have access to the TelemetryClient after the scope is finished to call it explicitly, and also I need to pass the TelemetryClient to a method of a third party method so I cannot use a wrapper.
I'm using the built-in Di container of .Net Core in Azure Functions.
I'm registering it like this:
services.AddScoped(x =>
{
return new TelemetryClient();
});
I would like to have a method like OnRelease in Autofac, so I could register it like the following. Notice the call to OnRelease:
services.AddScoped(x =>
{
return new TelemetryClient();
}).OnRelease(x=>
{
x.Flush();
});
You can wrap the TelemetryClient like this:
public interface ILogsStuff
{
void LogSomething(string message);
}
public class TelemetryClientLogger : ILogsStuff, IDisposable
{
private readonly TelemetryClient _telemetryClient;
public TelemetryClientLogger(TelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
public void LogSomething(string message)
{
_telemetryClient.TrackTrace(message);
}
public void Dispose()
{
_telemetryClient.Flush();
}
}
There's an added benefit, which is that whatever class you're injecting this into won't depend directly on TelemetryClient. You can define an interface that describes whatever you want to use the client for, and that's easier to both mock and replace with some other implementation if needed.
The suggestion was made to make TelemetryClient implement IDisposable and have it call Flush() when disposed, and this was the recommended solution.
The update to your question says:
I need to pass the TelemetryClient to a method of a third party method so I cannot use a wrapper.
That changes things somewhat, as now the intention is not to use the TelemetryClient but to resolve it and then pass it to a method of a 3rd-party library. That's peculiar because it means that this library forces code that uses it to have a hard dependency on TelemetryClient, and then presumably doesn't handle that client as expected.
At this point there are still solutions, but they're all ugly because they are solutions to a weird problem that shouldn't exist (and wasn't created by your code.)
If you're passing TelemetryClient to a method, that method writes to it, and then it must be flushed, why doesn't that library flush it?
You can likely solve that problem by passing the TelemetryClient to the method when you execute it and then flushing the client after the method executes.
CallSomeMethod(someArgument, _telemtryClient);
telemetryClient.Flush();
Or, to get the result closer to your question, you could do this, which would obtain the result but is still awkward and weird:
public class TelemetryClientWrapper : IDisposable
{
private readonly TelemetryClient _telemetryClient;
public TelemetryClientWrapper(TelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
public TelemetryClient Client => _telemetryClient;
public void Dispose()
{
_telemetryClient.Flush();
}
}
You can inject this class and pass it to the library's method like this:
CallSomeMethod(someArgument, _telemtryClientWrapper.Client);
The problem now is that your class has a hard dependency on TelemetryClient even though it doesn't use it, all so it can fix what the other library (presumably) isn't doing correctly with that client.
Having said all of that, perhaps in your scenario this is the answer:
The built-in service container is meant to serve the needs of the framework and most consumer apps. We recommend using the built-in container unless you need a specific feature that it doesn't support. Some of the features supported in 3rd party containers not found in the built-in container:
In other words, if you need what Autofac does, you can still use Autofac with IServiceCollection.
When using LibLog, is it possible to assert calls to the logger? Given the wiki lists the following example for usage:
public class MyClass
{
private static readonly ILog Logger = LogProvider.For<MyClass>();
}
Here the logger is an implementation detail hidden from the consumer, which is most of the benefit of using this library. Such that the library consumer does not have to worry about how loggers are instantiated. Looking at this blog post:
http://dhickey.ie/2015/06/capturing-log-output-in-tests-with-xunit2/
It seems that a lot of boiler plate is added to capture the log output, I'm not entirely sure about the approach, given that it also uses a redirected Serilog output in the unit test, something that seems odd given the library should only rely on the logging abstraction?
The only options I can currently think of are:
Inject the logger - This probably would be odd for the consumer of the library, and each library then would carry it's own ILogger definition that needs to be injected, defeating the advantages of the abstraction.
Wire up to a real logging framework - Set the current LogProvider for LibLog to use Log4Net or similar, and then somehow try and inject a mock / stub Logger into Log4Net, and assert calls via proxy.
Any relatively simple way to assert calls to the logger would be appreciated, but I suspect parallel test execution would cause problems even if it was possible to assert calls on the above logger?
In the logging config for almost all loggers you can configure then to throw exception when log fail.
Sample from nlog
<nlog throwExceptions="true">
... your nlog config
</nlog>
But in the abstraction created by LibLog you lost this features
What I've done in my project:
I've created my LoggerFactory. It exposes same static methods as NLogger.
public class LoggerFactory
{
private static ILoggerFactoryStrategy _loggerFactoryStrategy = new DummyLoggerFactoryStrategy();
public static void Initialize(ILoggerFactoryStrategy loggerFactoryStrategy)
{
_loggerFactoryStrategy = loggerFactoryStrategy;
}
public ILogger GetLogger<T>()
{
return _loggerFactoryStrategy.GetLogger<T>();
}
....
}
Dummy strategy can write just to debug output or do nothing. Another strategy could look smth like:
public class LoggerFactoryStrategy : ILoggerFactoryStrategy
{
public ILogger GetLogger<T>()
{
//create LibLog instance instead with LogProvider.For<T>()
var nlogger = LogManager.GetLogger(typeof(T).Name); //create instance of NLogger
return new NLogLogger(nlogger);
}
}
And NlogLogger wrapper could be smth like
internal class NLogLogger : ILogger
{
private readonly Logger _logger;
public NLogLogger(Logger logger)
{
_logger = logger;
}
public void Debug(string message)
{
_logger.Debug(message);
}
public void Warn(string message, params object[] args)
{
_logger.Warn(message, args);
}
public void Info(Exception exception)
{
_logger.Info(exception);
}
......
}
When application starts I initialize it with proper strategy what uses NLogger under the hood.
If I want to test calls to logger I can use mocked strategy.
This approach lets you to remove references to logger library across your solution, except your root projects and lets you switch from one to another if you need in the future.
Also, this allowed us to use NLogger in PCL projects.