none
Setting Up ILogger inside a customer library RRS feed

  • Question

  • Hi,

    I have a utility library in .Net Core that needs to that uses ILogger interface to log into application insight. Originally it was built using TelemetryClient class but since the class is being deprecated only way I could use it is using ILogger.

    The below is the constructor for my custom logger and this Logs class use ILogger for app. insight

     internal Logs(ILogger logger)
    {
         iLogger = logger;
    }

    And the above constructor used in another class which would be called by a static method

    public Operation()
    {
        log = new Logs();
    }

    Now I want to find a way to put the ILogger in to the constructor without bringing it from startup class. Could you suggest me a way I could build this dependency object and pass into Logs class?

    Thanks in advance


    • Edited by Gayan19911212 Sunday, April 5, 2020 9:58 AM Changed the title
    Sunday, April 5, 2020 9:39 AM

All replies

  • Hi Gayan,

    I used below ILogger interface in a console application may be it helps you.

    These packages must first be added to your application

    <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.3" />
        <PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.3" />
        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.3" />
      </ItemGroup>
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using System;
    
    namespace ConsoleAppILogger
    {
        
    
        public class SimpleLog
        {
            private readonly ILogger Varlog;
    
    
            public SimpleLog(ILogger<SimpleLog> logger)
            {
                Varlog = logger;
    
    
            }
    
    
           public void CollectInfromation()
            {
                Varlog.LogInformation("This App Started at {0}", DateTime.Now);
                Varlog.LogCritical("Logging critical information.");
                Varlog.LogDebug("Logging debug information.");
                Varlog.LogError("Logging error information.");
                Varlog.LogTrace("Logging trace  ");
                Varlog.LogWarning("Logging warning.");
                Varlog.LogInformation("This App Ended at {0}", DateTime.Now);
            }
    
    
        }
    
          
    
        class Program
        {
            static void Main(string[] args)
            {
                
                var services = new ServiceCollection();
    
    
                //Call ConfigureServices 
                ConfigureServices(services);
    
                using (ServiceProvider serviceProvider = services.BuildServiceProvider())
                {
                    SimpleLog SimpleappLog = serviceProvider.GetService<SimpleLog>();
    
                    SimpleappLog.CollectInfromation();
                }
            }
    
            private static void ConfigureServices(ServiceCollection services)
            {
                services.AddLogging(config => config.AddConsole()).AddTransient<SimpleLog>();
            }
       
        }
    }
    


     


    Please remember to mark the replies as answers if they helped you :) ~

    Sunday, April 5, 2020 11:21 AM
  • Thanks for the reply Rebin Qadir.

    My problem is I do know how other developer uses this custom libraries. All I know it that, they would use it for their Azure functions. Could you provide more information on that?

    Sunday, April 5, 2020 1:15 PM
  • Thanks for the reply Rebin Qadir.

    My problem is I do know how other developer uses this custom libraries. All I know it that, they would use it for their Azure functions. Could you provide more information on that?

    Well, you need to find out what the logging is for. Is it a general logging solution to log activity? Is it a logging solution to log exceptions? 

    IMO, you should be asking these question in the ASP.NET Azure or ASP.NET Core forums in ASP.NET forums, since this clearly is an ASP.NET Core solution you are developing against.

    https://forums.asp.net/

    IMO, if the developers what logging in their .NET Core solution, then they should implement it themselves through Program.cs and Startup.cs.   

    You should learn how to write clean code, not use tight coupling and avoid 'static cling' coding, which is just as bad as using the 'new' keyword to instance an object. It's not optimal coding when using .NET Core.

    https://docs.microsoft.com/en-us/archive/msdn-magazine/2016/may/asp-net-writing-clean-code-in-asp-net-core-with-dependency-injection

    <copied>

    In addition to the new keyword, “static cling” is another source of tight coupling that makes applications more difficult to test and maintain.

    <end>

    Sunday, April 5, 2020 2:49 PM
  • Thanks for the reply DA924x

    First of all, even though primary purpose to serve Azure functions, it could be used other types of applications as well.

    Secondly, I have written extension for the ILogger and execute static methods from it. Are you trying to say, instead of new object inside have my extended ILogger interface use as an inheritance?

    Sunday, April 5, 2020 4:14 PM
  • First of all, even though primary purpose to serve Azure functions, it could be used other types of applications as well.

    Azure is Web that's what you're talking about. And if it was a Core desktop solution, there is the Program.cs, and one can make a Startup.cs, which would follow the same implementation of  using Ilogger or the Loggerfactory.

    Secondly, I have written extension for the ILogger and execute static methods from it. Are you trying to say, instead of new object inside have my extended ILogger interface use as an inheritance?

    You should understand the 'new is glue' principle and understand its  implementation leading to tightly coupled coding. Then you should look at the static class and its tight coupling ramifications in the same light that one would be looking at it in using the 'new' keyword to instance class.

    One of the key features of .NET Core is dependency  injection and use of its IoC called Services. You should have the developer DI your Logger object into a class/object,  and you should  DI ILogger into your Logger class with all it being done through the implementation in Program.cs and Startup.cs using Services for your Logger object. This promotes loose coupling and testability.  

    https://ardalis.com/new-is-glue

    Although the link is focused on ASP.NET Core, it can be applied to any type of .NET Core solution or even applied to a non Core .NET solution.

    https://docs.microsoft.com/en-us/archive/msdn-magazine/2016/may/asp-net-writing-clean-code-in-asp-net-core-with-dependency-injection

    To be honest, your Logger implementation wouldn't pass a code review.

    Sunday, April 5, 2020 5:10 PM
  • Hi DA924x,

    ILogger dependency injection is being use at constructor assign to whatevery value I have and use it for other purposes in other methods but extensions are written in static method and how would you propose once these extensions are being called how to access logger other methods.

    Please take a look at the code below and suggest me what things need to be changed, (Think the code not to be set up in program.cs or startup.cs). Anyway, provide me how you would arrange following

    public static class ExtensionLog
    {
      public static void LogMe(this ILogger logger, string msg)
      {
         LogMeStatic.Log(msg);
      }
    }
    
    
    public static class Logs{
    
      private static Execution exe = new Execution();
    
      public static void LogMeStatic(string msg)
      {
         exe.Log(msg);
      }
    }
    
    public sealed class Execution{
    
      private Logging logging;
    
      public Execution()
      {
          var loggerFactory = new LoggerFactory().CreateLogger<Logging>();
          logging= new Logging(loggerFactory);
      }
    }
    
    public class Logging {
    
      private ILogger logger;
    
      public Logging (ILogger logger)
      {
         this.logger = logger;
      }
    }

    Sunday, April 5, 2020 5:31 PM
  • I would have to say that I would not be happy with the implementation you are showing me for the following reasons:

    1) I couldn't mock out Logger in a unit test of my code, becuase I can't mock out a static class.

    2) I couldn't achieve full code coverage in a unit test of my code, becuase of the static class. 

    3) I can't run a unit test on my code, becuase the Logger is hitting the file system or database, which leads back to it being a static class not being able to mock it out.

    https://www.artima.com/weblogs/viewpost.jsp?thread=126923

    You may think that UT is not an integral part of software development, but it is now of days.

    4) I would want to be able to log an information message as opposed to an error message, like log.Info(msg) or log.Error(msg).

    If you're going to be a .NET architect, then you must look at other situations in how your solution will be used by other developers.



    • Edited by DA924x Sunday, April 5, 2020 6:13 PM
    Sunday, April 5, 2020 6:07 PM
  • Hi DA924x,

    I am asking this I know what you are saying but I could not interpret what you are saying into the code. How would I have an dependency injection into an extension method unless I have to manually inject that class. 

    Please elaborate more with a design if you can.

    Monday, April 6, 2020 3:27 PM
  • IMO, the key for you is to use the .NET Core IoC aka Services to DI your Logger object into any class/object where logging is required.

    This can be done by using a classlib project call it Logging.

    The classlib project is going to have a class call it Log. Log will be implementing an Interface Ilog.

    ILog will have several public methods that Log class  will implement, like InfoMsg(string msg), ErrorMsg(string msg), DebugMsg(string msg), etc. and etc.

    Becuase you are implementing an Interface on ILog on the Log class, it will have the ability to be mocked out during a unit test. And becuase of the ILog, it will allow Log to ne instanced in the IoC aka Services that is registered with ConfigureServices() in the Startup.cs

    SomeClass' method that is using your logger.

    public class SomeClass()
    {
          private ILog _log
          SomeClass(ILog log)
          {
             _log = log;
          }
    
          public void SomeMethod()
          {
              log.InfoMsg("I made it here.);
          }
    
    }

    Now about ILogger that Log needs in the Logging classlib project, which windup being a DLL a project will set reference to. The ILogger is esablished normally in StartUp.cs

    public class Log :ILog
    {
        ILogger _logger;
    
        public Log (ILogger<Log> logger)
        {
          _logger = logger;
        } 
    
        public void InfoMsg(string msg)
        {
    
           _logger.Log(msg);
        }
    |

    I am using Serilog that is esablished in Program.cs. You may have to do something different. Serilog uses ILogger.

     
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using Serilog;
    using ServiceLayer;
    using WebRazor3.x.Models;
    
    namespace WebRazor3.x
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
              
                Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger();
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddRazorPages();
    
                //Domain Model
                services.AddTransient<IAuthorDM, AuthorDM>();
                services.AddTransient<IPayRollDM, PayRollDM>();
                services.AddTransient<IArticleDM, ArticleDM>();
    
                //ServiceLayer
                services.AddTransient<IAuthorSvc, AuthorSvc>();
                services.AddTransient<IPayRollSvc, PayRollSvc>();
                services.AddTransient<IArticleSvc, ArticleSvc>();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerfactory)
            {
                            
                app.UseExceptionHandler("/Error");
                
                loggerfactory.AddSerilog();
    
                app.UseHttpsRedirection();
                app.UseStaticFiles();
    
                app.UseRouting();
    
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapRazorPages();
                });
            }
        }
    }
    

     I think that you could take the path. You can use your static class and the non static class in the same classlib project if you choose to do so and give the developer a choice on what o use.

     
    Monday, April 6, 2020 7:54 PM
  • Hi Gayan,

    Thank you for posting your questions here,

    Sorry for delay in responding if have created your project with .Net Core the project must have Startup class which is named Startup by convention  so we can give any name to the Startup class it will execute when the app started it looks like Booting in operating systems and initials set of operations if you projected have created with asp. net core I suggest you read this article everything is well explained about configuring Logging.
    https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1

    if you applications is WPF,WinForm you can take advantage from nlog library https://nlog-project.org/

    I used the following snipped code for collecting some information in a C# Console Application.

    For example I want to gather some information about what is going on inside customer object

     public class Customer
        {
            private readonly ILogger<Customer> logger;
    
            public Customer(ILogger<Customer> logger)
            {
                this.logger = logger;
            }
    
             
    
    
            public void CustomerLog()
            {
    
                logger.LogInformation("This App Started at {0}", DateTime.UtcNow);
                logger.LogError("Oops.. something went wrong");
                 
                try
                {
                    throw new Exception("Hi this is Exception");
                }
                catch (Exception ex)
                {
    
                    logger.LogCritical(ex, "Really bad exception has occured at .... {0}", DateTime.UtcNow);
                }
    
                logger.LogInformation("This App Ended at {0}", DateTime.UtcNow);
    
    
            }
    
        }
    The result


    Please remember to mark the replies as answers if they helped you :) ~




    Monday, April 6, 2020 8:14 PM
  • Hi DA924x,

    Your suggestion is aboslutly right. If I use DI, I would use it that way. However, in my case, I have to write using this extenstions. Therefore, I need some suggestion with extensions rather than creating new DI set. 

    Tuesday, April 7, 2020 2:47 AM
  • Hi DA924x,

    Your suggestion is aboslutly right. If I use DI, I would use it that way. However, in my case, I have to write using this extenstions. Therefore, I need some suggestion with extensions rather than creating new DI set. 

    Extensions involve using a static class. I have previously explained the 'static cling' situation of using a static class, the tight coupling it presents and the inability to write clean code to  reduce tight coupling of code using a static class.

    I have also explained that your solution would never make it out of a .NET Core code review being reviewed by seasoned professional .NET developers. It wouldn't make it.

    The  developer or developers that is/are directing you to do this really don't understand the principles of OOP. They don't understand how to not write code that is tightly coupled. They don't understand how a static class makes unit testing any code that involves using a static class difficult.  

    https://objcsharp.wordpress.com/2013/07/08/why-static-code-is-bad/

    <copied>

    Because static methods aren’t attached to an instance of an object, they are essentially “global code” that can be called from anywhere. Sounds great, right? That seems like it makes code “easier” to write, but it comes at the cost of design, maintainability, and testability. Static code violates the core principles of object-oriented programming (OOP), encourages tight coupling, and makes unit testing very difficult.

    <end>

    I can't help other than to tell you not to use a static class and  not to use an extension implementing a static class  for a logging solution to be used globally in a .NET Core solution.

    Tuesday, April 7, 2020 5:08 AM
  • Hi DA924x,

    what you are saying make sense, and it is completely true. Its tightly couple and if I'm to change certain thing may create chaos. However, I have chosen this way Microsoft has build few extensions for the application insight (Microsoft.Extensions.Logging : class LoggerExtensions). Moreover, certain methods are obsolete and have to use the ILogger. 

    Tuesday, April 7, 2020 7:01 AM
  • https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1

    Logging fundamentals are being explained in the link. The problem is that you have decided to implement  a global logging solution by using extensions. IMO, it's not an optimal choice. There is nothing wrong in making an extortion when appropriate, but I don't think it is viable for some kind of global logging solution concerning .NET Core.  

    Tuesday, April 7, 2020 4:03 PM