How to use only single NLog instance in whole DLL - c#

I want to use NLog in DLL this is my code
namespace TestLogging
{
public class Class2
{
public void foo()
{
LogWrapper.initlog();
LogWrapper.write("Error message");
}
}
public static class LogWrapper
{
private static bool isInit = false;
private static Logger _logger = null;
public static void initlog()
{
// Make sure logger is initialized only once
if (!isInit)
{
var config = new LoggingConfiguration();
var fileTarget = new FileTarget();
config.AddTarget("file", fileTarget);
// Step 3. Set target properties
fileTarget.Layout = #"${date:format=HH\:mm\:ss} - ${message}";
fileTarget.FileName = "c:/myFolder/" + "${date:format=yyyy-MM-dd}.log";
var rule2 = new LoggingRule("*", LogLevel.Debug, fileTarget);
config.LoggingRules.Add(rule2);
// Step 5. Activate the configuration
LogManager.Configuration = config;
// Example usage
_logger = LogManager.GetLogger("Example");
isInit = true;
}
}
public static void write(string s)
{
if (isInit == false)
throw new Exception("Not initialized");
_logger.Error(s);
}
}
}
My goal is that many different classes in DLL should be able to use the NLog via this log wrapper. Configuration should be done once. Is my approach correctly implemented? Is it thread safe?

declare a static object at the class level
private static object _locker = new object();
in the initlog put a lock on it
lock(_locker)
{
//your code
}
in your write method do the same
lock(_locker)
{
if (isInit == false)
throw new Exception("Not initialized");
_logger.Error(s);
}

Related

How can I get NLog to inject dependencies into a target?

I have a custom NLog log target class that looks like this:
public class MyTarget : AsyncTaskTarget
{
public MyTarget() {}
public MyTarget(INeedThisThingToFunction thing)
{
Thing = thing;
}
public INeedThisThingToFunction Thing { get; set; }
public override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken cancellationToken)
{
Thing.Use();
return null;
}
}
I cannot figure out how to ensure that the second constructor gets called. I've done this in Program.cs:
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
ConfigureLogging(host.Services.GetAutofacRoot());
LogManager.GetCurrentClassLogger().Info("Hi mom");
host.Run();
}
private static void ConfigureLogging(IComponentContext container) {
ConfigurationItemFactory.Default.CreateInstance = type =>
{
if (type != typeof(MyTarget) return Activator.CreateInstance(type);
var thing = new ThingTheTargetNeedsToFunction();
return new MyTarget(thing);
}
LogManager.Configuration.Reload();
}
I've tried a number of other things, too, but that comes the closest to doing something. When LogManager.Configuration.Reload() is called, the CreateInstance code fires; but when the Info method fires, the Thing property on the target is null.
Is there a better way to do this? Like, a way that works?
Using .NET Core 3, NLog, Autofac.
If the Thing is only available after having built the host, then you can do it like this:
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).UseNLog().Build();
ConfigureLogging(host.Services.GetAutofacRoot());
LogManager.GetCurrentClassLogger().Info("Hi mom");
host.Run();
}
private static void ConfigureLogging(IComponentContext container)
{
var defaultConstructor = ConfigurationItemFactory.Default.CreateInstance;
ConfigurationItemFactory.Default.CreateInstance.CreateInstance = type =>
{
if (type == typeof(MyTarget))
{
var thing = new ThingTheTargetNeedsToFunction();
return new MyTarget(thing);
}
return defaultConstructor(type);
};
// Reload config and assign the newly reloaded config
LogManager.Configuration = LogManager.Configuration?.Reload();
}
Then make sure that your custom MyTarget can handle that it is running in "disabled mode" where Thing is unassigned:
[Target("MyTarget")]
public class MyTarget : AsyncTaskTarget
{
public MyTarget() {}
public MyTarget(INeedThisThingToFunction thing)
{
Thing = thing;
}
public INeedThisThingToFunction Thing { get; set; }
public override await Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken cancellationToken)
{
if (Thing == null)
return null; // Handle that `Thing` is unassigned by default-constructor
await Thing.UseAsync().ConfigureAwait(false);
}
}

release static WCF resource

I have declared static resource as follows:
public DiagnosticModule : TestModule
{
private static ChannelFactory<IDiagnosticService> _diagnosticsChannelFactory;
private static ChannelFactory<IDiagnosticService> DiagnosticsChannelFactory => _diagnosticsChannelFactory ?? throw new FaultException<ApciFault>(new Fault{ FaultMessage = "Diagnostic Service Channel is NOT initialized yet" },string.Empty);
static DiagnosticModule()
{
.............
_diagnosticsChannelFactory = new ChannelFactory<IDiagnosticService>(new NetTcpBinding(SecurityMode.None),endPoint);
.............
}
public static void func1(...)
{
IDiagnosticService v5DiagnosticInformationClient = null;
try
{
v5ClearDiagnosticInformationClient = DiagnosticsChannelFactory.CreateChannel();
...................
((IClientChannel)v5ClearDiagnosticInformationClient).Close();
}
catch(Exception e)
{
((IClientChannel)v5ClearDiagnosticInformationClient).Abort();
}
}
All the functions within the DiagnosticModule class are static and I am looking for a way to call so that I can close the factory I have instantiated inside the static constructor:
DiagnosticsChannelFactory.Close();
Where to call the above snippet inside the class ?

DI container giving new instance every time?

It seems my DI container makes a new instance for ChromeDriver (IWebDriver) each time I try and get it from the container? All of this happened after refactoring my code. I suddenly needed Selenium by reference for the methods below, otherwise, it wouldn't update the DOM throughout new page loads as I was passing it by value.
Here are the original methods before refactoring,
public static bool ElementExists(IWebDriver selenium, By selector)
{
try
{
selenium.FindElement(selector);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
public static void WaitForElements(IWebDriver selenium, List<By> selectors, string name = "")
{
new ConsoleLogger().Trace("Waiting for " + (string.IsNullOrEmpty(name) ? selectors.Count + " items" : name) + ", give us a second.");
while (selectors.Where(x => ElementExists(selenium, x)).Count() < selectors.Count)
{
Thread.Sleep(100);
}
}
I thought hmm, this is going to be a tricky one. I needed some sort of static instance that I could always pass by reference, I refactored it to this.
public static bool ElementExists(By selector)
{
var selenium = Reusables.GetServiceProvider().GetService<IWebDriver>();
try
{
selenium.FindElement(selector);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
Reusables class:
public static class Reusables
{
public static IDependencyProvider DependencyProvider;
public static IServiceProvider GetServiceProvider()
{
return DependencyProvider.BuildServiceProvider();
}
}
Program:
private static void Main(string[] args)
{
var diProvider = new DependencyProvider();
Reusables.DependencyProvider = diProvider;
Console.ForegroundColor = ConsoleColor.White;
Console.CancelKeyPress += (sender, eArgs) => {
QuitEvent.Set();
eArgs.Cancel = true;
};
Console.WriteLine();
Console.CursorVisible = false;
/*var config = serviceProvider.GetService<IConfigProvider>();
config.Load("https://kskdkskd.kdskdkk", new WebClient());*/
var scraper = Reusables.GetServiceProvider().GetService<IScraperHandler>();
scraper.Start();
QuitEvent.WaitOne();
}
Not sure if its needed, but here's how I register my dependencies:
public class DependencyProvider : ServiceCollection, IDependencyProvider
{
public DependencyProvider()
{
Register();
}
public void Register()
{
this.AddSingleton<IAuthProvider, AuthProvider>();
var options = new ChromeOptions();
options.AddArguments("--disable-notifications");
options.SetLoggingPreference(LogType.Browser, LogLevel.Off);
this.AddSingleton<IWebDriver>(provider =>
new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), options)
);
var links = File.ReadAllLines("***");
this.AddSingleton<IHttpHandler, HttpHandler>();
this.AddSingleton<IEnumerable>(stack => new Stack<string>(links.ToList()));
this.AddSingleton<IScraperHandler, ScraperHandler>();
this.AddSingleton<IConfigProvider, JsonConfigProvider>();
}
}
You are building a new service provider every time Reusables.GetServiceProvider is invoked. Which will result in a new provider each time, resulting in a new instance every time the new service provider gets a service.
If the goal is to have a single provider then a singleton is required.
public static class Reusables {
public static IDependencyProvider DependencyProvider;
private static Lazy<IServiceProvider> serviceProvider =
new Lazy<IServiceProvider>(() => DependencyProvider.BuildServiceProvider());
public static IServiceProvider GetServiceProvider() {
return serviceProvider.Value;
}
}
Not a big fan of the above design but it should work.

how to implement autofac dependency injection with parameter in constructor or with any condition?

Before using any dependency injection I'm using simple interface to make a loosely coupled code like below -
Program.cs
class Program
{
static void Main(string[] args)
{
Customer obj = new Customer(new OracleData());
obj.Add();
}
}
Customer.cs
public class Customer
{
private Idata iobj;
public Customer(Idata newiobj)
{
iobj = newiobj;
}
public void Add()
{
iobj.AddData();
}
}
OracleData.cs
public class OracleData : Idata
{
public void AddData()
{
throw new NotImplementedException();
}
}
SQLData.cs
public class SQLData : Idata
{
public void AddData()
{
throw new NotImplementedException();
}
}
Idata.cs
public interface Idata
{
void AddData();
}
Now I tried this using Autofac DI library -
I wonder it always its last injected class object , how to set resolve object using constructor or with any condition?
code using Autofac DI -
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<Customer>();
builder.RegisterType<OracleData>().As<Idata>();
builder.RegisterType<SQLData>().As<Idata>();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var app = scope.Resolve<Idata>();
app.AddData();
}
//commented
//Customer obj = new Customer(new OracleData());
//obj.Add();
}
If you need to define which implementation you will use at the entry point you can do something like this:
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<Customer>();
if(args[0] == "oracle")
builder.RegisterType<OracleData>().As<Idata>();
else
builder.RegisterType<SQLData>().As<Idata>();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var app = scope.Resolve<Idata>();
app.AddData();
}
//commented
//Customer obj = new Customer(new OracleData());
//obj.Add();
}

Ripple Effect: OutOfMemoryException

I have been trying to learn about Roslyn and see if it works for my needs.
In a very simple project I am trying to create a simple ‘Ripple Effect’, which is for each iteration causing a new assembly to be loaded and eventually after 500 iterations it crashes (OutOfMemoryException)
Is there a way to do this without causing it to explode?
class Program
{
static void Main(string[] args)
{
string code = #"
IEnumerable<double> combined = A.Concat(B);
return combined.Average();
";
Globals<double> globals = new Globals<double>()
{
A = new double[] { 1, 2, 3, 4, 5 },
B = new double[] { 1, 2, 3, 4, 5 },
};
ScriptOptions options = ScriptOptions.Default;
Assembly systemCore = typeof(Enumerable).Assembly;
options = options.AddReferences(systemCore);
options = options.AddImports("System");
options = options.AddImports("System.Collections.Generic");
options = options.AddImports("System.Linq");
var ra = CSharpScript.RunAsync(code, options, globals).Result;
for (int i = 0; i < 1000; i++)
{
ra = ra.ContinueWithAsync(code).Result;
}
}
}
public class Globals<T>
{
public IEnumerable<T> A;
public IEnumerable<T> B;
}
Exception Image
Everytime you use CSharpScript.Run or Evaluate method you are actually loading a new script (a .dll) which happens to be quite large. In order to avoid this you need te cache the script that you are executing by doing so:
_script = CSharpScript.Create<TR>(code, opts, typeof(Globals<T>)); // Other options may be needed here
Having _script cached you can now execute it by:
_script.RunAsync(new Globals<T> {A = a, B = b}); // The script will compile here in the first execution
If you have a few scripts to load with your application each time, this is the easiest thing to do. However a better solution is to use a separate AppDomain and load the script isolated. Here is one way of doing it:
Create a script executor proxy as MarshalByRefObject:
public class ScriptExecutor<TP, TR> : CrossAppDomainObject, IScriptExecutor<TP, TR>
{
private readonly Script<TR> _script;
private int _currentClients;
public DateTime TimeStamp { get; }
public int CurrentClients => _currentClients;
public string Script => _script.Code;
public ScriptExecutor(string script, DateTime? timestamp = null, bool eagerCompile = false)
{
if (string.IsNullOrWhiteSpace(script))
throw new ArgumentNullException(nameof(script));
var opts = ScriptOptions.Default.AddImports("System");
_script = CSharpScript.Create<TR>(script, opts, typeof(Host<TP>)); // Other options may be needed here
if (eagerCompile)
{
var diags = _script.Compile();
Diagnostic firstError;
if ((firstError = diags.FirstOrDefault(d => d.Severity == DiagnosticSeverity.Error)) != null)
{
throw new ArgumentException($"Provided script can't compile: {firstError.GetMessage()}");
}
}
if (timestamp == null)
timestamp = DateTime.UtcNow;
TimeStamp = timestamp.Value;
}
public void Execute(TP parameters, RemoteCompletionSource<TR> completionSource)
{
Interlocked.Increment(ref _currentClients);
_script.RunAsync(new Host<TP> {Args = parameters}).ContinueWith(t =>
{
if (t.IsFaulted && t.Exception != null)
{
completionSource.SetException(t.Exception.InnerExceptions.ToArray());
Interlocked.Decrement(ref _currentClients);
}
else if (t.IsCanceled)
{
completionSource.SetCanceled();
Interlocked.Decrement(ref _currentClients);
}
else
{
completionSource.SetResult(t.Result.ReturnValue);
Interlocked.Decrement(ref _currentClients);
}
});
}
}
public class Host<T>
{
public T Args { get; set; }
}
Create a proxy object to share data between script execution app domain and the main domain:
public class RemoteCompletionSource<T> : CrossAppDomainObject
{
private readonly TaskCompletionSource<T> _tcs = new TaskCompletionSource<T>();
public void SetResult(T result) { _tcs.SetResult(result); }
public void SetException(Exception[] exception) { _tcs.SetException(exception); }
public void SetCanceled() { _tcs.SetCanceled(); }
public Task<T> Task => _tcs.Task;
}
Create this helper abstract type that all the other remote ones need to inherit from:
public abstract class CrossAppDomainObject : MarshalByRefObject, IDisposable
{
private bool _disposed;
/// <summary>
/// Gets an enumeration of nested <see cref="MarshalByRefObject"/> objects.
/// </summary>
protected virtual IEnumerable<MarshalByRefObject> NestedMarshalByRefObjects
{
get { yield break; }
}
~CrossAppDomainObject()
{
Dispose(false);
}
/// <summary>
/// Disconnects the remoting channel(s) of this object and all nested objects.
/// </summary>
private void Disconnect()
{
RemotingServices.Disconnect(this);
foreach (var tmp in NestedMarshalByRefObjects)
RemotingServices.Disconnect(tmp);
}
public sealed override object InitializeLifetimeService()
{
//
// Returning null designates an infinite non-expiring lease.
// We must therefore ensure that RemotingServices.Disconnect() is called when
// it's no longer needed otherwise there will be a memory leak.
//
return null;
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
Disconnect();
_disposed = true;
}
}
Here is how we use it:
public static IScriptExecutor<T, R> CreateExecutor<T, R>(AppDomain appDomain, string script)
{
var t = typeof(ScriptExecutor<T, R>);
var executor = (ScriptExecutor<T, R>)appDomain.CreateInstanceAndUnwrap(t.Assembly.FullName, t.FullName, false, BindingFlags.CreateInstance, null,
new object[] {script, null, true}, CultureInfo.CurrentCulture, null);
return executor;
}
public static AppDomain CreateSandbox()
{
var setup = new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase };
var appDomain = AppDomain.CreateDomain("Sandbox", null, setup, AppDomain.CurrentDomain.PermissionSet);
return appDomain;
}
string script = #"int Square(int number) {
return number*number;
}
Square(Args)";
var domain = CreateSandbox();
var executor = CreateExecutor<int, int>(domain, script);
using (var src = new RemoteCompletionSource<int>())
{
executor.Execute(5, src);
Console.WriteLine($"{src.Task.Result}");
}
Note the usage of RemoteCompletionSource within a using block. If you forget to dispose it you will have memory leaks because instances of this object on the other domain (not the caller) will never get GCed.
Disclaimer: I took the idea of RemoteCompletionSource from
here, also the idea for the CrossAppDomainObject from public domain.

Categories