I use log4net to implement logging in my .NET app.
However I do not want to distribute the 300kb log4net.dll for every user but instead send this dll to the user if he has problems and I want him to provide logs.
So, is it possible to make my app run whether or not the dll is there?
Of course for logging the dll would be needed, but if no logging is needed, the app should run without the dll.
Yes, it is possible.
First create an interfase with all your log methods:
public interface ILogger
{
void Write(string message);
// And much more methods.
}
Now create two instances, a dummy instance (lets call it DummyLogger), and a instance which will send its messages to Log4Net (Log4NetLogger).
To finish, create a factory class:
static public class LogFactory
{
static public ILogger CreateLogger()
{
if (/*Check if Log4Net is available*/)
return new Log4NetLogger();
else
return new DummyLogger();
}
}
You could check if Log4Net is available by simply checking if the file is in your bin-folder. Something like:
File.Exists(AppDomain.CurrentDomain.BaseDirectory + "Log4Net.dll")
But I can imagine that you want to do other checks, like if it exists in the GAC or whatever.
Now you can use your factory to create your logger and "write" messages to the log:
ILogger logger = LoggerFactory.CreateLogger();
logger.Write("I am logging something!");
Related
Within a C# project we currently use static methods on BsonSerializer to register serializers for specific types. This happens once on app startup.
However, our acceptance tests start the app up before every test and shut it down after every test, and the second time the app starts up it fails when RegisterSerializer is called, as the registration from the previous test is still in the registry as it's a global static.
Is there any way to register serializers without relying on global statics? Or another strategy for avoiding this problem when running tests?
If you're using the MongoDB serializer, you could check if the serializer is already registered before registering it:
if(BsonSerializer.LookupSerializer<YourCusomType>().GetType() != typeof(YourCusomTypeSerializer))
{
BsonSerializer.RegisterSerializer(new YourCusomTypeSerializer());
}
If you go down this route you should take into account multithreaded scenarios, the above code is not thread safe.
Another option would be to register your own provider and skip the individual serializer registrations:
public class YourCustomSerializationProvider : IBsonSerializationProvider
{
public IBsonSerializer GetSerializer(Type type)
{
if (type == typeof(YourCusomType)) return new YourCusomTypeSerializer();
// fall back to Mongo's defaults
return null;
}
}
// Where you previously registered individual serializers you will now register your provider instead
BsonSerializer.RegisterSerializationProvider(new YourCustomSerializationProvider());
This approach would be a bit more IoC friendly and would give you a bit more control.
Can you register these serializers in static constructor? This way you can call it any number of times you want, the static constructor will not execute more than once.
public class BsonSerializerRegisterer
{
static BsonSerializerRegisterer()
{
BsonSerializer.RegisterSerializer(typeof(DateTime), new DateTimeSerializer(DateTimeKind.Utc));
BsonSerializer.RegisterSerializer(typeof(decimal), new DecimalSerializer(BsonType.Decimal128));
BsonSerializer.RegisterSerializer(typeof(decimal?), new NullableSerializer<decimal>(new DecimalSerializer(BsonType.Decimal128)));
BsonSerializer.RegisterSerializer(new EnumSerializer<MyAwesomeEnum>(BsonType.String));
}
public static void RegisterSerializers()
{
}
}
And then call:
BsonSerializerRegisterer.RegisterSerializers()
A nice article that can help any future readers
https://www.mydevhub.com/mongodb/adding-custom-type-converter-to-mongodb-in-c/
My solution was to add all BsonSerializer.* invokations in the static constructor of StartUp.cs
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'm currently playing around with the IoC concept (with a WPF app) and I haven't decided on the tool I'll used with it just yet as I'm still trying to get the grasp of it but I'm confused as to how this would be configured regarding the specific parameters each component.
I understand how you define the relevant library in the config file and how it will determine which one should be used by the app and what its lifespan should be but what about each library requiring its own specific set of parameters.
Where do you get these from and when do you pass them on?
Taking your typical logger for example.
I have the following interface:
public interface ILogger
{
void Write(string message);
}
I have the logger class itself:
public class Logger : ILogger
{
private readonly ILogger _logger;
public Logger (ILogger logger)
{
_logger = logger;
}
public void Write(string message)
{
_logger.Write(message);
}
}
I then define multiple loggers each requiring their own parameter, so I implemented the following:
a) database logger: where a connection string is required so that I can log my message to a database.
public void LoggerDb: ILogger
{
public void Write(string message)
{
}
public ConnectionString {get; set;}
}
b) file logger: where a filename is required so that I can log my message to the relevant log file.
public void LoggerFile: ILogger
{
public void Write(string message)
{
}
public Filename {get; set;}
}
c) console logger: where no parameter is required as I just want to output my message to a console window.
public void LoggerConsole: ILogger
{
public void Write(string message)
{
}
}
In my console test app, I've got the following code in the Program.cs:
static void Main(string[] args)
{
string logTypeId = "d";
ILogger logType;
if (logTypeId == "d")
{
logType = new LoggerDb("Data Source=....");
}
else if (logTypeId == "f"
{
logType = new LoggerFile("c:\\mylog.txt");
}
else
{
logType = new LoggerConsole();
}
Logger logger = new Logger(logType);
logger.Write("Message 1");
logger.Write("Message 2");
logger.Write("Message 3");
}
I understand this is not how the code would be if I used an IoC tool. I'm just trying to highlight what I'm trying to achieve and I'm trying to get answers to the following questions:
Can this be achieved using an IoC tool i.e. pass specific parameter depending on the logger type that's used/defined in the IoC section of the app.config?
Is this the correct approach i.e. Having specific loggers with their own constructors parameters? If not, can you explain why and what should be the correct approach. I don't mind the IoC tool you use. I just want to understand how this should be done.
Where should these additional parameters be stored in the app.config?
First, note that in order to implement DI via an IoC, it is by no means required to configure your container in a configuration file (although it's certainly an option and many containers support it).
Most IoC containers these days also allow you to specify your setup in code. So I guess the answer is: it really depends on the IoC container you plan to use. My opinion: avoid xml-based configuration if you can; it's often a pain to maintain and brings little value if you ask me. In your code-based configuration you can still refer to configuration parameters from app.config or other.
You can also turn the question around: is it a requirement to have the container configuration in a separate file (and why)? If yes, look for a container that supports this well. But most do.
Some examples of configuration using a code-based DSL:
Autofac modules: http://docs.autofac.org/en/latest/configuration/modules.html
StructureMap: http://structuremap.github.io/registration/registry-dsl/
Some examples of xml configuration:
Autofac: http://docs.autofac.org/en/latest/configuration/xml.html
Spring.NET container: http://www.springframework.net/doc-latest/reference/html/objects.html
structuremap: http://docs.structuremap.net/configuring-structuremap/structuremap-xml-configuration/
It depends ;)
I can't speak for all DependencyInjection Tools, but many of them should support this functionality.
I don't see anything that speak against this. If you want to call different Loggers explicitly, you can do this. But you can also use some kind of LogListeners. One for DB, one for File and so on. And your Logger just delegates the LogMessage to all Loggers. But this depends on what you want or need ;)
This also depends on the implementation of the Logger. It's common to store the ConnectionString in the config. The other parameters are too specific, but you you can store them in config, too.
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.
I am wrapping the patterns & practices Enterprise Library Logging Application Block for an application written in .NET.
I want to be able to subclass a logger (i.e to provide domain specific logging).
What is the best way to do this?
For e.g, I have a static Logger class at the moment, but this does not allow me to specialize it for domain specific logging.
For example,
Log(MyDomainObj obj, string msg)
Check out NLog. They use this sort of pattern:
private static Logger myDomainLogger = LogManager.GetCurrentClassLogger();
You can then specialize the output based on the class that myDomainLogger belongs to.
More detail:
class MyDomain
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
private void SomeFunc()
{
_logger.Trace("this is a test");
}
}
Then in your output you can have it output "MyDomain.SomeFunc" as part of the "this is a test" message.
Also, checkout log4net. I never found the EL's logging to be as flexible as log4net. I chose log4net since I was already familiar with using log4j.
protected readonly log4net.ILog LOG = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
Doing it this way, I can get logs like this:
2009-07-15 09:48:51,674 [4420] DEBUG
SampleNamespace.SampleClass [(null)] -
Sample message you want to output
You could even do better than that. Write a wrapper class that wraps either Nlog or log4net or whatnot. You can then use that wrapper class (maybe use an interface to it if you really want to decouple things) in your code. This way, if you decide to change logger class, you need to change just one class and not edit all your classes.