Serilog writes duplicate entries in it's output - c#

I have written a Windows Service which logs its operations using Serilog
The application also uses Autofac for dependency Injection support and Seq for structured logging.
using Autofac;
public class ContainerInitiator
{
public static IContainer BuildContainer()
{
var _builder = new ContainerBuilder();
var logger = LoggerUtility.CreateLogger();
_builder.RegisterInstance(logger).As<ILogger>().SingleInstance();
var container = _builder.Build();
ContainerFactory.SetContainer(container);
return container;
}
}
Here is Log utility class
public class LoggerUtility
{
private static string connectionString = ConfigurationManager.AppSettings["applicationName"];
public static ILogger CreateLogger()
{
return GetLoggerConfiguration().CreateLogger();
}
private static LoggerConfiguration GetLoggerConfiguration()
{
var config = new LoggerConfiguration()
.ReadFrom.AppSettings();
config = config.
Enrich.WithProperty("ApplicationName", connectionString).
Enrich.WithExceptionDetails().
Enrich.WithMachineName().
Enrich.WithProcessId().
Enrich.WithThreadId().
ReadFrom.AppSettings();
return config;
}
}
Here is my service class, which is logging the activities
public class Engine : IEngine
{
private readonly ILogger _logger;
public RedistributeEngine(IContainerFactory containerFactory)
{
_logger = containerFactory.GetInstance<ILogger>();
}
public void Start()
{
_logger.Information("Engine started!");
}
}
As you see below Seq logs, it has two entries on the same time!

You're reading the configuration from App.Settings twice:
private static LoggerConfiguration GetLoggerConfiguration()
{
var config = new LoggerConfiguration()
.ReadFrom.AppSettings(); // <<<<<<<<<<<< *#*#*#*#*#*#*#*#
config = config.
Enrich.WithProperty("ApplicationName", connectionString).
Enrich.WithExceptionDetails().
Enrich.WithMachineName().
Enrich.WithProcessId().
Enrich.WithThreadId().
ReadFrom.AppSettings(); // <<<<<<<<<<<< *#*#*#*#*#*#*#*#
return config;
}

Related

Make one more dependency injection inside already dependency injection class

I write console app on .net core and want to add Nlog into classes. I add dependency injection in program class
public static async Task Main(string[] args)
{
var logger = LogManager.GetCurrentClassLogger();
try
{
var configuration = GetConfiguration("app.config");
var serviceProvider = BuildDI(configuration);
using (serviceProvider as IDisposable)
{
var tcpController = serviceProvider.GetRequiredService<TcpController>();
var median = await tcpController.GetMedian();
Console.WriteLine(median.ToString());
}
}
catch (Exception ex)
{
logger.Error(ex);
throw;
}
finally
{
LogManager.Shutdown();
}
}
I generate ServiceProvider in this method
private static IServiceProvider BuildDI(IConfiguration config)
{
return new ServiceCollection()
.AddSingleton<IConfiguration>(config)
.AddSingleton<TcpController>()
.AddLogging(loggingBuilder =>
{
loggingBuilder.ClearProviders();
loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
loggingBuilder.AddNLog(config);
})
.BuildServiceProvider();
}
In private static IConfiguration GetConfiguration(string configName) all logic to generate configuration object. I guess we don't need this code for this question.
In TcpController i got private TcpRepository _tcpRepository { get; } field that create in constructor.
public TcpController(IConfiguration configuration, ILogger<TcpController> logger)
{
this._configuration = configuration;
this._logger = logger;
this._tcpRepository = new TcpRepository(configuration);
}
I want to use it field like Transient. I must in TcpController repeat method private static IServiceProvider BuildDI(IConfiguration config) or in c# we have another way to make this?

.NET Core Console App | Hangfire With Dependency Injection

Goal:
Fundamentally I am trying to add a background job, that has dependencies injected, to a console application.
Problem:
Although the jobs are queued, they are never executed.
Program.cs
var appSettings = ConfigHelper.GetConfig();
Console.WriteLine("Initialising Hangfire Server...");
GlobalConfiguration.Configuration.UseSqlServerStorage(appSettings.ConnectionString);
using (var server = new BackgroundJobServer())
{
Console.WriteLine("Hangfire Server started.");
var t = serviceProvider.GetService<ITestService>();
t.Test();
Console.ReadKey();
}
ServiceProviderFactory.cs
public static void Setup()
{
IServiceCollection services = new ServiceCollection();
...
services.AddDbContext<Db>(x => x.UseSqlServer(appSettings.ConnectionString));
services.AddTransient<IInsertLogJob, InsertLogJob>();
services.AddTransient<ITestService, TestService>();
_serviceProvider = services.BuildServiceProvider();
}
TestService.cs
public interface ITestService
{
void Test();
}
public class TestService : ITestService
{
private readonly ILogger<TestService> _logger;
public TestService(ILogger<TestService> logger)
{
_logger = logger;
}
public void Test()
{
logger.LogInformation("Test");
_logger.LogError("Error");
}
}
Logger.cs
public class Logger : ILogger
{
...
Log log = new Log()
{
Message = message,
EventId = eventId.Id,
ObjectId = eventId.Name,
LogLevel = logLevel.ToString(),
CreatedTime = DateTime.Now
};
BackgroundJob.Enqueue<IInsertLogJob>(j => j.Insert(log));
}
InsertLogJob.cs
public interface IInsertLogJob
{
void Insert(Log log);
}
public class InsertLogJob : IInsertLogJob
{
private Db _dataContext;
public InsertLogJob(Db context)
{
_dataContext = context;
}
public void Insert(Log log)//<-- this never happens
{
_dataContext.Logs.Add(log);
_dataContext.SaveChanges();
}
}
DB Record
So all the code up to the point where the data has to be inserted into the database runs, the Hangfire job gets inserted as per the picture above, but the code is never executed.

Read a value from appsettings.json in 1.0.0-rc1-final

In one of my concrete class. I have the method.
public class Call : ICall
{
......
public Task<HttpResponseMessage> GetHttpResponseMessageFromDeviceAndDataService()
{
var client = new HttpClient();
var uri = new Uri("http://localhost:30151");
var response = GetAsyncHttpResponseMessage(client, uri);
return response;
}
Now I put the url into appsettings.json.
{
"AppSettings": {
"uri": "http://localhost:30151"
}
}
And I created a Startup.cs
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
}
and now I get stuck.
EDIT
By the way, I don't have a controller, it is a console application.
The preferred way to read configuration from appSettings.json is using dependency injection and the built or (or 3rd party) IoC container. All you need is to pass the configuration section to the Configure method.
public class AppSettings
{
public int NoRooms { get; set; }
public string Uri { get; set; }
}
services.Configure<AppSettings>(Configuration.GetSection("appsettings"));
This way you don't have to manually set the values or initialize the AppSettings class.
And use it in your service:
public class Call : ICall
{
private readonly AppSettings appSettings;
public Call(IOptions<AppSettings> appSettings)
{
this.appSettings = appSetings.Value;
}
public Task<HttpResponseMessage>GetHttpResponseMessageFromDeviceAndDataService()
{
var client = new HttpClient();
var uri = new Uri(appSettings.Uri);
var response = GetAsyncHttpResponseMessage(client, uri);
return response;
}
}
The IoC Container can also be used in a console application, you just got to bootstrap it yourself. The ServiceCollection class has no dependencies and can be instantiated normally and when you are done configuring, convert it to an IServiceProvider and resolve your main class with it and it would resolve all other dependencies.
public class Program
{
public static void Main(string[] args)
{
var configurationBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
var configuration = configurationBuilder.Build()
.ReloadOnChanged("appsettings.json");
var services = new ServiceCollection();
services.Configure<AppSettings>(configuration.GetSection("appsettings"));
services.AddTransient<ICall, Call>();
// add other services
// after configuring, build the IoC container
IServiceProvider provider = services.BuildServiceProvider();
Program program = provider.GetService<Program>();
// run the application, in a console application we got to wait synchronously
program.Wait();
}
private readonly ICall callService;
// your programs main entry point
public Program(ICall callService)
{
this.callService = callService;
}
public async Task Run()
{
HttpResponseMessage result = await call.GetHttpResponseMessageFromDeviceAndDataService();
// do something with the result
}
}
Create a static class
public static class AppSettings
{
public static IConfiguration Configuration { get; set; }
public static T Get<T>(string key)
{
if (Configuration == null)
{
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
var configuration = builder.Build();
Configuration = configuration.GetSection("AppSettings");
}
return (T)Convert.ChangeType(Configuration[key], typeof(T));
}
}
then access the settings anywhere you want like
var uri = AppSettings.Get<string>("uri");
var rooms = AppSettings.Get<int>("noRooms");
appsettings.json example
{
"AppSettings": {
"uri": "http://localhost:30151",
"noRooms": 100
}
}
You can access data from the IConfigurationRoot as following:
Configuration["AppSettings:uri"]
Like suggested in the comment I would put the information in a seperate class for that info and pass it into the DI container.
the class
public class AppSettings {
public string Uri { get; set; }
}
DI
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(new AppSettings() { Uri = Configuration["AppSettings:uri"] });
// ...
}
Controller
public class DemoController
{
public HomeController(IOptions<AppSettings> settings)
{
//do something with it
}
}

Structure Map - Collection of Dependencies in Constructor

I have the following code in my Registry:
Scan(x =>
{
x.Assembly(Assembly.GetExecutingAssembly());
x.AddAllTypesOf<IXmlExtractor>();
});
This code adds all instances of IXmlExtractor in my assembly to the IoC container.
Now I want to use all found instances in my class, but I prefer to inject via constructor than to use ObjectFactory in a method. Hopefully code below illustrates what I want to do:
class Engine : IEngine
{
private readonly ILog _logger;
private readonly ICurveConfigRepository _configRepo;
private readonly IDataExtractor _dataExtractor;
public Engine(ILog logger,
ICurveConfigRepository configRepo,
IDataExtractor dataExtractor
/* SomeCollectionOfIXmlExtractors allExtractors */)
{
_logger = logger;
_configRepo = configRepo;
_dataExtractor = dataExtractor;
}
public void Run(string mode)
{
_logger.Info(string.Format("About to run retrieve config for '{0}' mode.", mode));
var config = _configRepo.LoadConfig(mode);
_logger.Info("Config retrieved, extracting data");
var data = _dataExtractor.GetCurves(config);
_logger.Info("Data extracted");
// Foreach IXmlExtractor instance, call .DoStuff(data) with it.
// I don't like using ObjectFactory here - see comment in constructor
var extractors = ObjectFactory.GetAllInstances<IXmlExtractor>();
}
}
Hopefully this is clear, please comment back if further explanation is required.
C#: 4.0
StructureMap: 2.5.4.0
Thanks,
Graeme
You have already correctly registered all instances with the container.
Scan(x =>
{
x.Assembly(Assembly.GetExecutingAssembly());
x.AddAllTypesOf<IXmlExtractor>();
});
All you need to do is declare an array variable and StructureMap will take care of the constructor injection automatically.
class Engine : IEngine
{
private readonly ILog _logger;
private readonly ICurveConfigRepository _configRepo;
private readonly IDataExtractor _dataExtractor;
private readonly IXmlExtractors[] _allExtractors;
public Engine(ILog logger,
ICurveConfigRepository configRepo,
IDataExtractor dataExtractor
IXmlExtractors[] allExtractors)
{
_logger = logger;
_configRepo = configRepo;
_dataExtractor = dataExtractor;
_allExtractors = allExtractors;
}
public void Run(string mode)
{
_logger.Info(string.Format("About to run retrieve config for '{0}' mode.", mode));
var config = _configRepo.LoadConfig(mode);
_logger.Info("Config retrieved, extracting data");
var data = _dataExtractor.GetCurves(config);
_logger.Info("Data extracted");
// Use extractors...
foreach (var extractor in _allExtractors)
{
extractor.DoStuff(data);
}
}
}

How to Inject Log4Net Logging through Castle.Windsor in NServiceBus

I would like NServiceBus to use the logger provided by the Castle.Windsor container, rather than providing it myself directly. Is there a way to achieve this? Following doesn't work as an exception raised on start.
public class EndPointConfig : IConfigureThisEndpoint, IWantCustomInitialization, IWantCustomLogging
{
private ILogger _logger = NullLogger.Instance;
public ILogger Logger
{
get { return _logger; }
set { _logger = value; }
}
public void Init()
{
SetLoggingLibrary.Log4Net(null, Logger);
Configure.With()
.CastleWindsorBuilder(Host.Init.CreateSubsystems())
.RunTimeoutManager()
.UseNHibernateTimeoutPersister();
}
}
Host.Init.CreateSubsystems() returns a Castle.Windsor container, with a registered logger.
If you supply a container alreday bootstrapped w/ a ILogger registered to CastleWindsorBuilder there's nothing else to be done: NServiceBus will use your logger settings as per log4net configuration(separate file or within app.config according to logger registration in the container)
SetLoggingLibrary.Log4Net(null, Logger); is no needed.
public class MessageEndpoint : IConfigureThisEndpoint, AsA_Server, IWantCustomInitialization
{
public void Init()
{
var container = new WindsorContainer();
var installerFactory = new MyInstallerFactory();
container.Install(FromAssembly.This(installerFactory));
var logger = container.Resolve<ILogger>();
logger.Debug("Container bootstrapped");
Configure.With()
.DisableTimeoutManager()
.CastleWindsorBuilder(container)
.JsonSerializer();
logger.Debug("Bus configured");
}
}

Categories

Resources