using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Core.Common.BaseDelftTools.Workflow; using log4net.Appender; using log4net.Core; namespace Core.Common.Base { /// /// Class add logmessage to models when they are 'done'. /// Class works by catch logAppender message durring running states of activity /// Running state is a when the activity is active : /// Initializing, Executing, Cancelling /// public class RunningActivityLogAppender : IAppender { private readonly IList doneStates = new[] { ActivityStatus.Cleaned, ActivityStatus.Failed, ActivityStatus.Cancelled }; private readonly IList runningStates = new[] { ActivityStatus.Executing, ActivityStatus.Initializing, ActivityStatus.Cancelling, ActivityStatus.Finishing, ActivityStatus.Cleaning }; /// shouldnt it be a dictionary of stringbuilders? private readonly IDictionary> activityLogs; // this is ThreadLocal as a hack to ensure we can keep logfiles from several models (eg. threads) seperate. private readonly ThreadLocal> runningActivities = new ThreadLocal>(() => new HashSet()); private readonly object runningActivitiesLock = new object(); private readonly object activityLogsLock = new object(); private IActivityRunner activityRunner; //THIS constructor is used by Log4Net instantiation.. //to make the appender reachable from the app we use a singleton construction public RunningActivityLogAppender() { activityLogs = new Dictionary>(); Instance = this; } public static RunningActivityLogAppender Instance { get; set; } public IActivityRunner ActivityRunner { get { return activityRunner; } set { if (activityRunner != null) { activityRunner.ActivityStatusChanged -= ActivityRunnerActivityStatusChanged; } activityRunner = value; if (activityRunner != null) { activityRunner.ActivityStatusChanged += ActivityRunnerActivityStatusChanged; } } } public string Name { get; set; } public void Close() {} public void DoAppend(LoggingEvent loggingEvent) { //loggingEvent.RenderedMessage; foreach (var act in GetRunningActivitiesThreadSafe()) { var message = "[" + loggingEvent.TimeStamp.ToString("HH:mm:ss") + "]:" + loggingEvent.RenderedMessage + Environment.NewLine; using (new TryLock(activityLogsLock)) { if (!activityLogs.ContainsKey(act)) { activityLogs[act] = new List(); } activityLogs[act].Add(message); } } } private void ActivityRunnerActivityStatusChanged(object sender, ActivityStatusChangedEventArgs e) { //if an activity goes into a running state i want to know it... if (runningStates.Contains(e.NewStatus)) { using (new TryLock(runningActivitiesLock)) { runningActivities.Value.Add((IActivity) sender); } } //it is done...commit the log else if (doneStates.Contains(e.NewStatus)) { var activity = (IActivity) sender; SendLogToActivity(activity); using (new TryLock(runningActivitiesLock)) { runningActivities.Value.Remove(activity); } using (new TryLock(activityLogsLock)) { activityLogs.Remove(activity); // DON'T forget to remove, otherwise memory leak! } } //it changed state into a non running state...suspend logging else { var activity = (IActivity) sender; using (new TryLock(runningActivitiesLock)) { runningActivities.Value.Remove(activity); } } //TODO: when do we commit the log the activity? } private void SendLogToActivity(IActivity activity) { if (!GetActivityLogActivitiesThreadSafe().Contains(activity)) { return; } using (new TryLock(activityLogsLock)) { // TODO: Add log item and/or append log text } } private ICollection GetActivityLogActivitiesThreadSafe() { using (new TryLock(activityLogsLock)) { return activityLogs.Keys.ToList(); } } private IEnumerable GetRunningActivitiesThreadSafe() { using (new TryLock(runningActivitiesLock)) { return runningActivities.Value.ToList(); } } } }