C# Web App: Custom Logger for external logging - c#

Hello c# experts,
I'm writing an AWS lambda function in c# which requires external logging (I have to write all my logs to an external endpoint). I have been reading many articles about the best practices. Also I did some research on c# libraries such as NLog, Log4Net etc (Some libraries aren't compatible with .Net Core).
My main requirement is that I do not want to pass a Logger object around everywhere in my code. Instead, I should be able to write an external log more like a static method call (Ex: Logger.sendLog("log message");).
Considering the above requirement and the threading issues, I have decided to implement my Logger as a singleton which works fine at the moment. But, I'm curious to know if there are any particular issues with this design. Please let me know if there is a better way to implement a custom Logger class which writes external logs.
Thank you.
interface ILogger
{
void init();
void setUserInfo(UserInfo userInfo);
void sendLog(Dictionary<LogKey, object> payload);
}
public sealed class Logger : ILogger
{
private static readonly Logger instance = new Logger();
private static LogObject logObject;
public static Logger Instance
{
get
{
return instance;
}
}
public void init()
{
logObject = new LogObject();
logObject.appId = AppConfig.appId;
logObject.application = AppConfig.appName;
logObject.version = AppConfig.appVersion;
logObject.environment = EnvConfig.Instance.clientConfig.environment;
logObject.clientName = EnvConfig.Instance.clientConfig.clientName;
}
public void setUserInfo(UserInfo userInfo)
{
logObject.userId = userInfo.userId;
logObject.userName = userInfo.userName;
}
public void sendLog(Dictionary<LogKey, object> payload)
{
setTimeStamp();
logObject.log_level = LogLevel.INFO.ToString();
logObject.payload = payload;
deliverLog(JsonConvert.SerializeObject(logObject));
resetLogObj();
}
private void deliverLog(string logStr)
{
// External API call here
}
private void setTimeStamp()
{
logObject.timestamp = DateTime.UtcNow;
}
private void resetLogObj()
{
logObject.payload = null;
}
}

Related

How to make Microsoft.Extensions.Logging available for all classes

I just started learning C#, and am redoing past Java projects. I am trying to use Microsoft.Extensions.Logging, and I want to be able to make it available for all my classes in my console application.
Examples I referred to creates a LoggerFactory in the Main() method:
https://www.tutorialsteacher.com/core/fundamentals-of-logging-in-dotnet-core
https://thecodeblogger.com/2021/05/11/how-to-enable-logging-in-net-console-applications/
How can I make MEL loggers available for all classes similar to how log4net/serilog does it? I did refer to microsoft documentation but I m not very familiar with Dependency Injection in C#.
I could use log4net instead, however I saw a question thread on SO, that suggested it's better to program to an logging abstraction, as you can easily change logging providers later on depending on your needs.
ie:
class MyDomain
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
private void SomeFunc()
{
_logger.Trace("this is a test");
}
}
You can create an interface that defines the methods that you want to use for logging.
eg
public interface ILog
{
void LogInformation(string message);
void LogWarning(string message);
void LogError(string message);
}
Then you can implement this into a class where you are using Microsoft.Extensions.Logging to log.
public class Log : ILog
{
private readonly ILogger _logger;
public Logger(ILogger<Logger> logger)
{
_logger = logger;
}
public void LogInformation(string message)
{
_logger.LogInformation(message);
}
}
Now you can use ILog in all the classes and in the future if you want to change the logging provider you can.
Note: This is a simple implementation. you can make this more dynamic depending on your needs.

How to correctly instantiate a static HttpClient in constructor

I have a custom HttpClient implementation that needs some values from my configuration to work properly.
public class MyHttpClient : HttpClient
{
public MyHttpClient()
{
}
public MyHttpClient(string token)
{
Token = token;
}
public string Token { get; set; }
// ...
}
I now want to use this HttpClient in my application logic that uses IOptions<TOptions> for getting its configuration.
public class MyLogic
{
private static MyHttpClient Client;
public MyLogic(IOptions<MyConfig> config)
{
// How to instantiate Client with values from config?
}
}
Since HttpClient should be held as a static field, I struggle with the method I can inject the config value into the HttpClient. I came up with a few possibilities, but none of them seems to be fine.
Option 1: set config value each time
public class MyLogic
{
private static MyHttpClient Client = new MyHttpClient();
public MyLogic(IOptions<MyConfig> config)
{
Client.Token = config.Value.Token;
}
}
This seems to be the worst option, since the value of Token may change as the HttpClient uses it.
Option 2: lock each time
public class MyLogic
{
private static MyHttpClient Client;
private static readonly object ClientLock = new object();
public MyLogic(IOptions<MyConfig> config)
{
lock(ClientLock)
{
if (Client == null)
{
Client = new MyHttpClient(config.Value.Token);
}
}
}
}
This seems to be bad from a performance perspective, since each instantiation of MyLogic would create a lock and block all others.
Option 3: double-checked locking
public class MyLogic
{
private static volatile MyHttpClient Client;
private static readonly object ClientLock = new object();
public MyLogic(IOptions<MyConfig> config)
{
if (Client == null)
{
lock(ClientLock)
{
if (Client == null)
{
Client = new MyHttpClient(config.Value.Token);
}
}
}
}
}
This seems like the solution, however double-checked locks are hard to get right and should therefore be avoided.
Sadly, Lazy<T> doesn't seem to be an option since it also requires a static context for instance creation.
So, what would be the best option to instantiate the HttpClient in this situation?
#Damien_The_Unbeliever :
Since I can not add a comment, I have to do a "full reply":
The question goes into the direction, of how I can make sure that the HttpClient instance inside the shared project (which is neither Android nor iOS specific) correctly selects the Android HttpClient implementation when the App runs on an Android device.

Dependency Injection with classes other than a Controller class

At this point I'm injecting things into my Controllers with ease, in some cases building my own ResolverServices class. Life is good.
What I cannot figure out how to do is get the framework to automatically inject into non-controller classes. What does work is having the framework automatically inject into my controller IOptions, which is effectively the configuration for my project:
public class MessageCenterController : Controller
{
private readonly MyOptions _options;
public MessageCenterController(IOptions<MyOptions> options)
{
_options = options.Value;
}
}
I'm thinking whether I can do the same for for my own classes. I assume I'm close when I mimic the controller, like this:
public class MyHelper
{
private readonly ProfileOptions _options;
public MyHelper(IOptions<ProfileOptions> options)
{
_options = options.Value;
}
public bool CheckIt()
{
return _options.SomeBoolValue;
}
}
I think where I'm failing is when I call it like this:
public void DoSomething()
{
var helper = new MyHelper(??????);
if (helper.CheckIt())
{
// Do Something
}
}
The problem I have tracking this down is practically everything that talks about DI is talking about it at the controller level. I tried hunting down where it happens in the Controller object source code, but it gets kinda crazy in there.
I do know I can manually create an instance of IOptions and pass it to the MyHelper constructor, but it seems like I should be able to get the framework do that since it works for Controllers.
Below is a working example of using DI without anything that involves MVC Controllers. This is what I needed to do to understand the process, so maybe it will help somebody else.
The ShoppingCart object gets, via DI, an instance of INotifier (which notifies the customer of their order.)
using Microsoft.Extensions.DependencyInjection;
using System;
namespace DiSample
{
// STEP 1: Define an interface.
/// <summary>
/// Defines how a user is notified.
/// </summary>
public interface INotifier
{
void Send(string from, string to, string subject, string body);
}
// STEP 2: Implement the interface
/// <summary>
/// Implementation of INotifier that notifies users by email.
/// </summary>
public class EmailNotifier : INotifier
{
public void Send(string from, string to, string subject, string body)
{
// TODO: Connect to something that will send an email.
}
}
// STEP 3: Create a class that requires an implementation of the interface.
public class ShoppingCart
{
INotifier _notifier;
public ShoppingCart(INotifier notifier)
{
_notifier = notifier;
}
public void PlaceOrder(string customerEmail, string orderInfo)
{
_notifier.Send("admin#store.com", customerEmail, $"Order Placed", $"Thank you for your order of {orderInfo}");
}
}
public class Program
{
// STEP 4: Create console app to setup DI
static void Main(string[] args)
{
// create service collection
var serviceCollection = new ServiceCollection();
// ConfigureServices(serviceCollection)
serviceCollection.AddTransient<INotifier, EmailNotifier>();
// create service provider
var serviceProvider = serviceCollection.BuildServiceProvider();
// This is where DI magic happens:
var myCart = ActivatorUtilities.CreateInstance<ShoppingCart>(serviceProvider);
myCart.PlaceOrder("customer#home.com", "2 Widgets");
System.Console.Write("Press any key to end.");
System.Console.ReadLine();
}
}
}
Let's say MyHelper is used by MyService which in turn is used by your controller.
The way to resolve this situation is:
Register both MyService and MyHelper in Startup.ConfigureServices.
services.AddTransient<MyService>();
services.AddTransient<MyHelper>();
The controller receives an instance of MyService in its constructor.
public HomeController(MyService service) { ... }
MyService constructor will in turn receive an instance of MyHelper.
public MyService(MyHelper helper) { ... }
The DI framework will be able resolve the whole object graph without problems. If you are worried about new instances being created every time an object is resolved, you can read about the different lifetime and registration options like the singleton or request lifetimes.
You should be really suspicious when you think you have to manually create an instance of some service, as you might end up in the service locator anti-pattern. Better leave creating the objects to the DI Container. If you really find yourself in that situation (let's say you create an abstract factory), then you could use the IServiceProvider directly (Either request an IServiceProvider in your constructor or use the one exposed in the httpContext).
var foo = serviceProvider.GetRequiredService<MyHelper>();
I would recommend reading the specific documentation about the ASP.Net 5 DI framework and about dependency injection in general.
Unfortunately there is no direct way. The only way I managed to make it work is by creating a static class and using that everywhere else as below:
public static class SiteUtils
{
public static string AppName { get; set; }
public static string strConnection { get; set; }
}
Then in your startup class, fill it in as below:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//normal as detauls , removed for space
// set my variables all over the site
SiteUtils.strConnection = Configuration.GetConnectionString("DefaultConnection");
SiteUtils.AppName = Configuration.GetValue<string>("AppName");
}
Although this is bad pattern, as this will stay for the whole life cycle of the application and I couldn't find better way to use it outside controller.
Here's a more complete example to directly answer the OP's question, based on the current .NET Core 2.2 DI documentation here. Adding this answer since it may help someone that's new to .NET Core DI, and because this question is Google's top search result.
First, add an interface for MyHelper:
public interface IMyHelper
{
bool CheckIt();
}
Second, update the MyHelper class to implement the interface (in Visual Studio, press ctrl-. to implement the interface):
public class MyHelper : IMyHelper
{
private readonly ProfileOptions _options;
public MyHelper(IOptions<ProfileOptions> options)
{
_options = options.Value;
{
public bool CheckIt()
{
return _options.SomeBoolValue;
}
}
Third, register the interface as a framework-provided service in the DI service container. Do this by registering the IMyHelper service with the concrete type MyHelper in the ConfigureServices method in Startup.cs.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IMyHelper, MyHelper>();
...
}
Fourth, create a private variable to reference an instance of the service. Pass the service as an argument in the constructor (via constructor injection) then initialize the variable with the service instance. Reference any properties or call methods on this instance of the custom class via the private variable.
public class MessageCenterController : Controller
{
private readonly MyOptions _options;
private readonly IMyHelper _myHelper;
public MessageCenterController(
IOptions<MyOptions> options,
IMyHelper myHelper
)
{
_options = options.value;
_myHelper = myHelper;
}
public void DoSomething()
{
if (_myHelper.CheckIt())
{
// Do Something
}
}
}
You may use Activator.CreateInstance(). Here is a wrapper function for it. The way you use this is as follows.
var determinedProgrammatically = "My.NameSpace.DemoClass1"; // implements IDemo interface
var obj = CreateInstance<My.NameSpace.IDemo, string>(determinedProgrammatically, "This goes into the parameter of the constructor.", "Omit this parameter if your class lives in the current assembly");
Now you have an instance of obj which is instantiated from type determined programmatically. This obj can be injected into non controller classes.
public TInterface CreateInstance<TInterface, TParameter>(string typeName, TParameter constructorParam, string dllName = null)
{
var type = dllName == null ? System.Type.GetType(typeName) :
System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName.StartsWith(dllName, System.StringComparison.OrdinalIgnoreCase)).GetType(typeName);
return (TInterface)System.Activator.CreateInstance(type, constructorParam);
}
PS: You may iterate through System.AppDomain.CurrentDomain.GetAssemblies() to determine the name of the assembly that houses your class. This name is used in the 3rd parameter of the wrapper function.
TL;DR: You can save a singleton in a static var and then access it form other classes, but this an anti-pattern, use with caution.
Long version:
As per this question Resolving instances with ASP.NET Core DI from within ConfigureServices
Any services registered in ConfigureServices() can then be injected
into the Configure() method
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<FooService>();
}
public void Configure(IApplicationBuilder app, FooService fooService)
{
FooServiceInstance = fooService;
}
public static FooService FooServiceInstance { get; private set; }
And then call it from your other code MyStartupClass.FooService.DoStuff()

Castle Windsor - multiple implementation of an interface

While registering components in Castle Windsor, how do we bind specific implementation of an interface to a component that has a dependency on that interface. I know in advance which implementation needs to be used by the component.
For example i created a sample console application based on code from several blogs and tutorials.
Following is the code.
public interface IReport
{
void LogReport();
}
public interface ILogger
{
string Log();
}
public class FileLogger : ILogger
{
public string Log()
{
return "Logged data to a file";
}
}
public class DatabaseLogger : ILogger
{
public string Log()
{
return "Logged data to a database";
}
}
public class McAfeeService : IReport
{
private readonly ILogger _logger;
public McAfeeService(ILogger logger)
{
this._logger = logger;
}
public void LogReport()
{
string getLogResult = this._logger.Log();
Console.WriteLine("McAfee Scan has " + getLogResult);
}
}
public class NortonService : IReport
{
private readonly ILogger _logger;
public NortonService(ILogger logger)
{
this._logger = logger;
}
public void LogReport()
{
string getLogResult = this._logger.Log();
Console.WriteLine("Norton Scan has " + getLogResult);
}
}
class Program
{
private static IWindsorContainer container;
static void Main(string[] args)
{
// Register components
container = new WindsorContainer();
container.Register(Component.For<IReport>().ImplementedBy<NortonService>());
container.Register(Component.For<ILogger>().ImplementedBy<FileLogger>());
IReport service = container.Resolve<IReport>();
service.LogReport();
Console.ReadLine();
}
}
I would like NortonService to always use a Filelogger and McAfeeService to use a Database Logger.
In the above program i am unable to bind NortonService to FileLogger.
How to do it?
The above answers lead me to inline dependencies and the feature service override
Here is the registration code:
container.Register(Component.For<IReport>().ImplementedBy<NortonService>().Named("nortonService"));
container.Register(Component.For<ILogger>().ImplementedBy<FileLogger>());
container.Register(Component.For<ILogger>().ImplementedBy<DatabaseLogger>());
container.Register(
Component.For<IReport>().ImplementedBy<McAfeeService>().Named("mcafeeService")
.DependsOn(Dependency.OnComponent<ILogger, DatabaseLogger>())
);
IReport mcafeescan = container.Resolve<IReport>("mcafeeService");
mcafeescan.LogReport();
IReport nortonscan = container.Resolve<IReport>("nortonService");
nortonscan.LogReport();
Output:
McAfee Scan has Logged data to a database
Norton Scan has Logged data to a file
I had a problem very like this, two implementation of one interface and two implementation of another interface. I wanted to force usage of particular implementations of those interfaces.
My class structure looked like this -
I looked at the naming convention, but didn't really like it. Instead I used the following -
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IMessageLoader>().ImplementedBy<MessageLoaderDatabase>()
,Component.For<IMessageLoader>().ImplementedBy<MessageLoaderFile>()
,Component.For<IMessageOfTheDayService>().ImplementedBy<MessageOfTheDayServiceDatabase>()
.DependsOn(Dependency.OnComponent<IMessageLoader, MessageLoaderDatabase>())
,Component.For<IMessageOfTheDayService>().ImplementedBy<MessageOfTheDayServiceFile>()
.DependsOn(Dependency.OnComponent<IMessageLoader, MessageLoaderFile>())
,Component.For<MessageOfTheDayController>().LifestyleTransient()
.DependsOn(Dependency.OnComponent<IMessageOfTheDayService, MessageOfTheDayServiceFile>())
);
Full info about this approach is here. In the source code provided with that post I show two other ways of achieving the same result.
If you want to do it at runtime, This can be acheived through IHandlerSelector. Write a class that implements IHandlerSelector. It provides a method SelectHandler which will let you define the condition for binding conditionally at runtime. A Handler in this case is a component in Windsor that participates in instance construction. Refer here for more details.
My answer maybe not the best one, you can use naming method to resolve multi implementation:
container.Register(Component.For(typeof(ILogger))
.ImplementedBy(typeof(FileLogger))
.Named("FileLoggerIoC")
.LifestylePerWebRequest() ,
Component.For(typeof(ILogger))
.ImplementedBy(typeof(DatabaseLogger))
.Named("DatabaseLoggerIoC")
.LifestylePerWebRequest());
In your calling functions, you need to resolve it by name :-
var fileLog = container.Resolve("FileLoggerIoC", typeof(ILogger));
var DbLog = container.Resolve("DatabaseLoggerIoC", typeof(ILogger));
Mine method maybe not the best one as people don't like service locator to get the components, you can use this as temporary solution.

Unit testing an IExtension<OperationContext> for use with WCF Service

I'm trying to develop a extension (IExtension<OperationContext>) for System.ServiceModel.ObjectContext using TDD. The extension is to be used as storage for a lifetime manager to be used with Windsor Castle.
The problem lies in abstracting (mocking) the OperationContext. As it is a static object that gets automatically created during runtime I don't really know how to mock it (without TypeMock - which I don't have).
An OperationContext can be newed up if I supply a channel object that implements IChannelFactory, however - that interface is scary complex, and I don't know what stuff I have to implement in a stub to get it working properly.
Hosting the service and calling it doesn't populate the OperationContext either...
[TestFixtureSetUp]
public void FixtureSetup()
{
_serviceHost = new TypeResolverServiceHost(typeof(AilDataService));
_serviceHost.AddServiceEndpoint(typeof (IAilDataService), new BasicHttpBinding(), SvcUrl);
_serviceHost.Open();
var endpointAddress = new EndpointAddress(SvcUrl);
_ailDataService = ChannelFactory<IAilDataService>.CreateChannel(new BasicHttpBinding(), endpointAddress);
}
[TestFixtureTearDown]
public void FixtureCleanup()
{
_serviceHost.Close();
}
[Test]
public void Can_Call_Service()
{
var reply = _ailDataService.GetMovexProductData("169010", new TaskSettings{MovexDatabase = "MVXCDTATST", MovexServer = "SEJULA03"});
Assert.That(reply, Is.Not.Null);
// This fails
Assert.That(OperationContext.Current!=null);
}
Any tips?
This is what I ended up doing:
[TestFixture]
public class WcfPerSessionLifestyleManagerTests
{
private const string SvcUrl = "http://localhost:8732/Design_Time_Addresses/JulaAil.DataService.WcfService/AilDataService/";
private TypeResolverServiceHost _serviceHost;
private ChannelFactory<IAilDataService> _factory;
private IAilDataService _channel;
private WindsorContainer _container;
[Test]
public void Can_Populate_OperationContext_Using_OperationContextScope()
{
using (new OperationContextScope((IContextChannel) _channel))
{
Assert.That(OperationContext.Current, Is.Not.Null);
}
}
}

Categories