I want to have a logger which is "made available" to my application. My application logs interesting things, and subscribers subscribe to those messages. For example a subscriber might put message in a database, or the windows event log, or a message bus, etc.
A simplified reproduction of what I've written is below:
using System;
using System.Reactive.Subjects;
namespace ConsoleApplication17
{
internal interface ILogger
{
void Log(String message);
}
internal class Logger : ILogger
{
private readonly Subject<String> _logMessagesObservable = new Subject<string>();
public void Log(string json)
{
_logMessagesObservable.OnNext(json);
}
public IObservable<String> LogMessagesObservable { get { return _logMessagesObservable; } }
}
internal class ConsoleLogListener
{
public ConsoleLogListener(IObservable<String> messages)
{
messages.Subscribe(Console.WriteLine);
}
}
internal class WindowsEventListener
{
public WindowsEventListener(IObservable<String> messages)
{
messages.Subscribe(WriteToEventLog);
}
private void WriteToEventLog(String message)
{
//Write to event log
}
}
class Program
{
static void Main(string[] args)
{
Logger logger = new Logger();
//Link up any subscribers
new ConsoleLogListener(logger.LogMessagesObservable);
new WindowsEventListener(logger.LogMessagesObservable);
//... more listeners go here.
DoSomeWork(logger);
}
private static void DoSomeWork(ILogger logger)
{
logger.Log("Something interesting");
logger.Log("Something else interesting");
}
}
}
But I'm not happy about the code which looks like this:
//Link up any subscribers
new ConsoleLogListener(logger.LogMessagesObservable);
new WindowsEventListener(logger.LogMessagesObservable);
//... more listeners go here.
It irks me for some reason but I can't put my finger on it. I guess it just looks odd to new up a listener like that and not keep the reference to it.
Is there a better / more accepted pattern for what I'm trying to do?
I'd probably expect the listeners to implement IObserver<string>, then you'd connect the two like so:
logger.LogMessagesObservable.Subscribe(new ConsoleLogListener());
Related
I Know
IoC is a design principle which recommends the inversion of different kinds of controls in object-oriented design to achieve loose coupling between application classes.
But I have confilct with the following code:
static void Main(string[] args)
{
ProductService ProductService = new ProductService(new LogInDB());
ProductService.Log();
Console.ReadKey();
}
public class ProductService
{
private readonly Ilog log;
public ProductService(Ilog _log)
{
log = _log;
}
public void Log()
{
log.Log();
}
}
public interface Ilog
{
void Log();
}
public class LogInFile : Ilog
{
public void Log()
{
Console.WriteLine("Log Into File");
}
}
public class LogInDB : Ilog
{
public void Log()
{
Console.WriteLine("Log Into Data Base");
}
}
What is difference between previous and next code
In the first code I used IOC (and added product service) but next I'm using just late binding
but i see IOC not added any value
static void Main(string[] args)
{
Ilog logObj = new new LogInDB();
logObj.Log();
//I still able to using LogInDB
//Ilog logObj = new new LogInDB();
//logObj.Log();
Console.ReadKey();
}
public interface Ilog
{
void Log();
}
public class LogInFile : Ilog
{
public void Log()
{
Console.WriteLine("Log Into File");
}
}
public class LogInDB : Ilog
{
public void Log()
{
Console.WriteLine("Log Into Data Base");
}
}
This depends on your defintion of value. One advantage of IoC would be a better testability of your code, which many would argue adds a lot of value. You can easily inject mocked classes into your test code and only test the class you want to test.
By the way your example is not compileable because of the line Ilog logObj = new new LogInDB();
I will keep it simple. I have two services in my ASP.NET Core WebAPI project.
ServiceA and ServiceB.
Service A is responsible for sending emails when Service B raises an event. Service B has an event handler and a delegate and the event is raised correctly. However, the problem is in Service A when trying to handle the event via += EventHandlingMethod();
The handler method EventHandlingMethod() is never called. I have placed a breakpoint inside the method but it never triggers, event after method EventRaised() has been called from Service B correctly.
Is it possible because Service A is set up in Startup.cs as services.AddTransient<IServiceA, ServiceA>();?
I have provided a simple example of publish-subscribe pattern. This is synchronous but if you are looking for an asynchronous version you can use channels or any other message broker such as RabbitMQ / NServiceBus etc.
public class PublishSubscribeMiddleMan: IPubSub
{
Dictionary<Type, List<ISubscriber>> pubSub = new Dictionary<Type, List<ISubscriber>>();
public void PublishEvent<Publisher>(Publisher publisher)
{
Type t = publisher.GetType();
if (pubSub.TryGetValue(t, out var subscribers))
{
subscribers.ForEach(subscriber => subscriber.EventHandlingMethod());
}
}
public void Subscribe<Publisher>(ISubscriber subscriber)
{
Type t = typeof(Publisher);
if (pubSub.TryGetValue(t, out var subscribers))
{
subscribers.Add(subscriber);
}
else pubSub.Add(t, new List<ISubscriber> { subscriber });
}
}
public interface ISubscriber
{
void EventHandlingMethod();
}
public interface IPubSub
{
void Subscribe<Publisher>(ISubscriber subscriber);
void PublishEvent<Publisher>(Publisher publisher);
}
public class ServiceA : IServiceA
{
private readonly IPubSub publishSubscribe;
public ServiceA(IPubSub publishSubscribe)
{
this.publishSubscribe = publishSubscribe;
}
public void RaiseEvent()
{
publishSubscribe.PublishEvent(this);
}
}
public interface IServiceA
{
void RaiseEvent();
}
public class ServiceB : ISubscriber
{
public ServiceB(IPubSub publishSubscribe)
{
publishSubscribe.Subscribe<ServiceA>(this);
}
public void EventHandlingMethod()
{
//throw new NotImplementedException();
}
}
You would need to register the PubSub inside ConfigureServices as shown:
services.AddScoped<IPubSub, PublishSubscribeMiddleMan>();
services.AddTransient<IServiceA, ServiceA>();
I have a winforms application with lot's of classes, and in every class I need to write to a log if something goes wrong.
Today I made a logger function that I initialize in every class object for using it inside.
For example I have a main logic class that have log and one more class that's running different logic that should have a log.
Today I am using:
Initialize log object in class contractor for working with it.
passing log object to the contractor.
what will be the best architecture for initialize it one time and use it in every class (Not doing it static).
My logger class:
namespace MyLogger
{
public class Logger : IMessageLogger
{
IMessageLogger _messageLogger;
public Logger(IMessageLogger messageLogger)
{
_messageLogger = messageLogger;
}
public void Log(string message)
{
_messageLogger.Log(message);
}
}
public interface IMessageLogger
{
void Log(string message);
}
public class FileLogger : IMessageLogger
{
string _filePath = Environment.CurrentDirectory;
public string filePath
{
get { return _filePath; }
set { _filePath = value; }
}
public FileLogger(string filePath)
{
_filePath = filePath;
}
public void Log(string message)
{
string strFileName = Path.Combine(_filePath, String.Format("{0}{1}.log", _filePath, DateTime.Now.ToString("yyyyMMdd")));
using (StreamWriter writer = new StreamWriter(strFileName, true))
{
writer.WriteLine(DateTime.Now.ToString("[dd/MM/yyyy hh:mm:ss]") + " -> " + message);
};
}
}
public class ConsoleLogger : IMessageLogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
}
I believe the best way is to implement it via DependencyInjection, you should read about it online.
But if you want a quick and easy solution, simply implement a Singleton pattern for your logger, like such -
public class Logger : IMessageLogger
{
private IMessageLogger _messageLogger;
private static Logger _instance;
public static Logger Instance
{
get
{
if (_instance == null)
{
// Pick one:
_instance = new Logger(new FileLogger("SomePath"));
_instance = new Logger(new ConsoleLogger());
}
return _instance;
}
}
private Logger(IMessageLogger messageLogger)
{
_messageLogger = messageLogger;
}
public void Log(string message)
{
_messageLogger.Log(message);
}
}
And to write a log use this line -
Logger.Instance.Log("This is a log message!");
There are two sides to my answer.
The first, to get the behavior you want, make the class and methods static. This creates one instance for the lifetime of your application and you can just use FileLogger.Log wherever you need it without having to new a new FileLogger up.I am not sure why you are adverse to using a static though, so you could get the same behavior with a singleton.
The second is that you should not be writing your own logger. There are so many good, open source options available. Look at log4net, nLog or even the built in TraceSource to save yourself the effort of re-inventing the wheel.
Just going through understanding events and delegates and I have a question regarding outputting a different string using the same event and delegate and if it possible. Take this example.
class Program
{
static void Main(string[] args)
{
Publisher b = new Publisher();
Subscriber a = new Subscriber(b);
b.OnLog("12");
}
}
class Publisher
{
public delegate void LogHandler(string message);
public event LogHandler Log;
public void OnLog(string message)
{
//Fire the event - notifying all subscribers
if (Log != null)
Log(message);
}
}
class Subscriber
{
public Subscriber(Publisher pub)
{
//Where B is used - subscribe to it's public event
pub.Log += new Publisher.LogHandler(HandleSomethingHappening);
pub.Log += new Publisher.LogHandler(HandleSomethingHappeningStop);
}
public void HandleSomethingHappening(string message)
{
Console.WriteLine(message);
}
public void HandleSomethingHappeningStop(string message)
{
Console.WriteLine(message);
Console.ReadLine();
}
}
Here, both methods
HandleSomethingHappening and HandleSomethingHappeningStop will output "12". If I wanted handleSomethingHappeningStop to output, lets say "24", do I need to create a new event associated with the same delegate to do this?
so for example
pub.LogOutput24 += new Publisher.LogHandler(HandleSomethingHappeningStop);
Thanks
I have a the following code setup
public interface ILogger
{
void WriteData(string Data);
}
public class Logger : ILogger
{
public void WriteData(string Data)
{
//Write to disk
}
}
public interface ILogic
{
void ProcessData(string Data);
}
public class Logic : ILogic
{
private ILogger Logger;
public Logic(ILogger Logger)
{
this.Logger = Logger;
}
public void ProcessData(string Data)
{
//Do stuff
Logger.WriteData("Data to write");
}
}
public class MainEntryPointClass
{
private BlockingCollection<string> DataInCollection;
private Task DataInTask;
private CancellationTokenSource CancellationTokenSource;
public Start()
{
InitializeDataIn();
}
private void InitializeDataIn()
{
CancellationTokenSource = new CancellationTokenSource();
DataInCollection = new BlockingCollection<DataInContents>();
DataInTask = Task.Factory.StartNew(() => ProcessDataIn(CancellationTokenSource.Token));
}
private void ProcessDataIn(CancellationToken CancelToken)
{
while (!CancelToken.IsCancellationRequested)
{
foreach (var item in DataInCollection.GetConsumingEnumerable())
{
Logic.ProcessData(item);
}
}
}
}
So I create a new Task in my main class and then data is added to the DataInCollection to queue up data as it comes in, we're talking every 30ms or so. This gets processed successfully.
I now want to write data to file on a separate thread just so if there is a disk problem the main logic checking isn't impacted. If there is a disk problem then the logic can continue. I'm just not sure where I do the file writing on the separate thread? Is it in the Main class, the Logic class or the Logger class?
It's Logger's responsibility to ensure that it doesn't block the caller. It can use many different strategies for that. You don't want to bake those strategies into the class that uses it.
I'd enqueue the message into a BlockingCollection<T>, and have a single IO thread write it out to the disk.
I also recommend imitating an existing logging interface, such as Common.Logging's ILog so you can easily switch to an existing logging framework if your "no third party" requirement gets ever lifted.
Something like:
class AsyncLogger:ILogger
{
public AsyncLogger(ILogger backingLogger)
{
new Thread(()=>
{
while(true)
{
var data=_queue.Take();
_backingLogger.WriteData(data);
}
}
).Start();
}
public void WriteData(string data)
{
_queue.Enqueue(data);
}
}
(I omitted stuff like a termination condition for IO thread, fields, handling of multiple loggers,...)
The Logger class is hopefully responsible for logging. So it seems like the right place to log the incoming data to disk.