Error on creating multiple instance of eventsource

Topics: Semantic Logging Application Block
Oct 12, 2013 at 3:44 PM
Hello,

I'm unable to create multiple instance of an event source.

The code below rise the following exception:

An instance of EventSource with Guid d075ffa3-1353-5805-a049-917ea12a8a61 already exists.
using System;
using System.Diagnostics.Tracing;
using Microsoft.Practices.EnterpriseLibrary.SemanticLogging;

namespace SemanticLoggingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var listener1 = FlatFileLog.CreateListener("log1.txt");
            var logger1 = new Logger();
            listener1.EnableEvents(logger1, EventLevel.Informational, Keywords.All);
            logger1.SomeEvent("Hello");

            var listener2 = FlatFileLog.CreateListener("log2.txt");
            var logger2 = new Logger();
            listener2.EnableEvents(logger2, EventLevel.Informational, Keywords.All);
            logger2.SomeEvent("Hi");
        }
    }

    class Logger : EventSource
    {
        [Event(1, Message="Log: {0}", Level = EventLevel.Informational)]
        public void SomeEvent(String txt)
        {
            this.WriteEvent(1, txt);
        }
    }
}
How can I create multiple instance of the same logger?

Thanks,
Antonio
Oct 12, 2013 at 9:53 PM
You can only have one instance of an EventSource. If you notice, in most of the examples the EventSource is a singleton. A typical implementation would be like this:
private static readonly Lazy<Logger > Instance =
    new Lazy<Logger >(() => new Logger ());

private Logger ()
{
}

public static Logger Log
{
    get { return Instance.Value; }
}
What is the problem that you are trying to solve? Are you trying to write to a separate log file based on a certain criteria (e.g. log file per thread)?

If so, you can use Reactive Extensions to achieve that behavior.

This example uses a CorrelationManager to achieve a separate log file per activity (plus a common log file for all log entries). A similar approach can be used for a variety of scenarios.
    public class Logger : EventSource
    {
        private static readonly Lazy<Logger> Instance = new Lazy<Logger>(() => new Logger());

        private Logger()
        {
        }

        public static Logger Log
        {
            get { return Instance.Value; }
        }

        [Event(1, Message = "Log: {0}", Level = EventLevel.Informational)]
        public void SomeEvent(String txt)
        {
            this.WriteEvent(1, txt);
        }
    }

    public static class ActivityName
    {
        public static readonly string Method1Activity = "Method1Activity";
        public static readonly string Method2Activity = "Method2Activity";
    }

    class Program
    {
        static void Main(string[] args)
        {
            Initialize();

            Logger.Log.SomeEvent("Starting...");

            Method1();
        }

        static void Initialize()
        {
            ObservableEventListener method1Listener = new ObservableEventListener();
            method1Listener.EnableEvents(Logger.Log, EventLevel.LogAlways, Keywords.All);
            method1Listener.ShouldLogIf(e =>
            {
                return ShouldLog(ActivityName.Method1Activity);
            }).LogToFlatFile("Method1.txt");

            ObservableEventListener method2Listener = new ObservableEventListener();
            method2Listener.EnableEvents(Logger.Log, EventLevel.LogAlways, Keywords.All);
            method2Listener.ShouldLogIf(e =>
            {
                return ShouldLog(ActivityName.Method2Activity);
            }).LogToFlatFile("Method2.txt");

            ObservableEventListener commonListener = new ObservableEventListener();
            commonListener.EnableEvents(Logger.Log, EventLevel.LogAlways, Keywords.All);
            commonListener.LogToFlatFile("common.txt");
        }

        static bool ShouldLog(string activityName)
        {
            return Trace.CorrelationManager.LogicalOperationStack.Count > 0 &&
                   Trace.CorrelationManager.LogicalOperationStack.Peek().ToString() == activityName;
        }

        static void Method1()
        {
            try 
            {
                Trace.CorrelationManager.StartLogicalOperation(ActivityName.Method1Activity);
                Logger.Log.SomeEvent("Method 1 message");
                Method2();
            }
            finally
            {
                Trace.CorrelationManager.StopLogicalOperation();
            }
        }

        static void Method2()
        {
            try
            {
                Trace.CorrelationManager.StartLogicalOperation(ActivityName.Method2Activity);
                Logger.Log.SomeEvent("Method 2 message");
            }
            finally
            {
                Trace.CorrelationManager.StopLogicalOperation();
            }
        }
    }

    public static class SemanticLoggingExtensions
    {
        public static IObservable<T> ShouldLogIf<T>(this IObservable<T> stream, Func<T, bool> shouldLog)
        {
            return Observable.Create<T>(observer =>
            {
                var subscription = stream.Synchronize().Subscribe(item =>
                {
                    if (shouldLog(item))
                    {
                        observer.OnNext(item);
                    }
                });

                return subscription;
            });
        }
    }

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Oct 12, 2013 at 11:16 PM
Edited Oct 12, 2013 at 11:19 PM
Hi Randy,

thanks for your response, I have noticed that all the examples use always a static logger.

I'm working on a project that launch long running jobs. Every job is represented by a given object which has is own logger. This is a typical case of a SaaS service that launch jobs on a per user command. Every job must log its activity to a different logger instance (which mean a different log file or table).

With this pattern every domains has its own logger, what is common to all domains are just the listeners which can be defined in the infrastructure layer.

An example is given in the code below:
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Practices.EnterpriseLibrary.SemanticLogging;

namespace SemanticLoggingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var jobManager = new JobManager();
            
            // create a job due to an external request
            jobManager.StartNewJob();

            // create another job due to an external request
            jobManager.StartNewJob();

            // ...
        }
    }

    class JobManager
    {
        private readonly EventListener[] _listeners = new[] { ConsoleLog.CreateListener() };

        public void StartNewJob()
        {
            var job = new LongRunningJob(_listeners);
            job.Start();
        }
    }

    class LongRunningJob
    {
        private readonly Logger _logger = new Logger();

        public LongRunningJob(IEnumerable<EventListener> listeners)
        {
            listeners.ToList().ForEach(listener => listener.EnableEvents(_logger, EventLevel.Informational, Keywords.All));
        }

        public void Start()
        {
            Task.Factory.StartNew(() =>
                {
                    _logger.SomeEvent("Start");
                    
                    // start an async long running job
                    Thread.Sleep(new Random().Next(1, 5));

                    _logger.SomeEvent("Completed");
                });
        }
    }

    class Logger : EventSource
    {
        [Event(1, Message="Log: {0}", Level = EventLevel.Informational)]
        public void SomeEvent(String txt)
        {
            this.WriteEvent(1, txt);
        }
    }
}
Your example starts to be a little bit too complex if I don't know in advance how many different loggers I have.

Antonio
Oct 13, 2013 at 1:59 AM
I think you just need to think about it the other way: instead of thinking about loggers think about listeners.

So your statement could be restated:

"I'm working on a project that launch long running jobs. Every job is represented by a given object which has is own listener. This is a typical case of a SaaS service that launch jobs on a per user command. Every job must log its activity to a different listener instance (which mean a different log file or table).

With this pattern every domains has its own listener, what is common to all domains are just the logger which can be defined in the infrastructure layer."

You can assign a unique identifier to every job (frequently this is already the case and unique identifier already exists) and filter the listener events based on that ID using the CallContext. In my example I initialize all listeners at the beginning but this is not required -- listeners can be created (and disposed) within the job.

So one way to change around your example would be something like the following. You can add abstractions to generalize (so it can be used in a general purpose infrastructure library).
    class Program
    {
        static void Main(string[] args)
        {
            var jobManager = new JobManager();

            for (int i = 0; i < 100; i++)
            {
                // create a job due to an external request
                jobManager.StartNewJob();
            }
        }
    }

    public class JobManager
    {
        // Common Loggers
        private readonly EventListener[] _listeners = new[] { ConsoleLog.CreateListener() };

        public JobManager()
        {
            // Init shared loggers
            _listeners.ToList().ForEach(listener => listener.EnableEvents(Logger.Log, EventLevel.Informational, Keywords.All));
        }

        public void StartNewJob()
        {
            var job = new LongRunningJob();
            job.Start();
        }
    }

    public class LongRunningJob
    {
        private readonly Logger _logger = Logger.Log;
        private readonly Guid id;

        public LongRunningJob()
        {
            this.id = Guid.NewGuid();

            ObservableEventListener jobListener = new ObservableEventListener();

            jobListener.ShouldLogIf(e =>
                {
                    object contextId = CallContext.LogicalGetData("id");
                    return contextId != null && id == (Guid)contextId;
                }).LogToFlatFile("Job_" + id + ".txt");

            jobListener.EnableEvents(Logger.Log, EventLevel.Informational, Keywords.All);
        }

        private bool ShouldLog(Guid id)
        {
            return this.id == id;
        }

        public void Start()
        {
            CallContext.LogicalSetData("id", id);

            Task.Factory.StartNew(() =>
            {
                _logger.SomeEvent("Start Job: " + id);

                // start an async long running job
                Thread.Sleep(new Random().Next(1, 5));

                _logger.SomeEvent("Completed Job: " + id);
            });
        }
    }
 
An alternative approach would be to attach a unique identifier to the payload of the Logger (instead of the context). This job ID could then be filtered on.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Oct 13, 2013 at 4:45 AM
Edited Oct 13, 2013 at 4:49 AM
In effect, thinking about listeners instead than loggers will resolve my problem. I just need to better understand how to decouple the code in order to avoid to instantiate the listeners inside the object, this will make the code more portable.

Thanks,
Antonio