Browse Source

Logging subsystem refactoring - use log4net (#145)

* Save the progress

* Add log4net Log appender for Windows service events

* Get rid of the IEventLogger API, we use log4net now
Oleg Nenashev 9 years ago
parent
commit
12c16e40a7

+ 28 - 0
src/Core/ServiceWrapper/Logging/WrapperServiceEventLogProvider.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using winsw.Logging;
+
+namespace winsw.Logging
+{
+    /// <summary>
+    /// Implements caching of the WindowsService reference in WinSW.
+    /// </summary>
+    public class WrapperServiceEventLogProvider : IServiceEventLogProvider
+    {
+        public WrapperService service {get; set;}
+
+        public EventLog locate()
+        {
+            WrapperService _service = service;
+            if (_service != null && !_service.IsShuttingDown)
+            {
+                return _service.EventLog;
+            }
+
+            // By default return null
+            return null;
+        }
+    }
+}

+ 54 - 55
src/Core/ServiceWrapper/Main.cs

@@ -20,10 +20,11 @@ using WMI;
 using ServiceType = WMI.ServiceType;
 using winsw.Native;
 using System.Reflection;
+using winsw.Logging;
 
 namespace winsw
 {
-    public class WrapperService : ServiceBase, EventLogger, IEventWriter
+    public class WrapperService : ServiceBase, EventLogger
     {
         private SERVICE_STATUS _wrapperServiceStatus;
 
@@ -34,6 +35,7 @@ namespace winsw
         internal WinSWExtensionManager ExtensionManager { private set; get; }
 
         private static readonly ILog Log = LogManager.GetLogger("WinSW");
+        private static readonly WrapperServiceEventLogProvider eventLogProvider = new WrapperServiceEventLogProvider();
 
         /// <summary>
         /// Indicates to the watch dog thread that we are going to terminate the process,
@@ -53,6 +55,13 @@ namespace winsw
             get { return Assembly.GetExecutingAssembly().GetName().Version; }
         }
 
+        /// <summary>
+        /// Indicates that the system is shutting down.
+        /// </summary>
+        public bool IsShuttingDown {
+            get { return _systemShuttingdown; }
+        }
+
         public WrapperService(ServiceDescriptor descriptor)
         {
             _descriptor = descriptor;
@@ -63,6 +72,9 @@ namespace winsw
             CanPauseAndContinue = false;
             AutoLog = true;
             _systemShuttingdown = false;
+
+            // Register the event log provider
+            eventLogProvider.service = this;
         }
 
         public WrapperService() : this (new ServiceDescriptor())
@@ -135,7 +147,7 @@ namespace winsw
                 }
                 catch (Exception e)
                 {
-                    WriteEvent("Thread failed unexpectedly",e);
+                    Log.Error("Thread failed unexpectedly",e);
                 }
             }).Start();
         }
@@ -171,7 +183,7 @@ namespace winsw
                 }
                 catch (Exception e)
                 {
-                    WriteEvent("Failed to log event in Windows Event Log: " + message + "; Reason: ", e);
+                    Log.Error("Failed to log event in Windows Event Log: " + message + "; Reason: ", e);
                 }
             }
         }
@@ -190,28 +202,11 @@ namespace winsw
                 }
                 catch (Exception e)
                 {
-                    WriteEvent("Failed to log event in Windows Event Log. Reason: ", e);
+                    Log.Error("Failed to log event in Windows Event Log. Reason: ", e);
                 }
             }
         }
 
-        private void WriteEvent(Exception exception)
-        {
-            //TODO: pass exception to logger
-            WriteEvent(exception.Message + "\nStacktrace:" + exception.StackTrace, Level.Error);
-        }
-
-        private void WriteEvent(String message, Exception exception)
-        {
-            //TODO: pass exception to logger
-            WriteEvent(message + "\nMessage:" + exception.Message + "\nStacktrace:" + exception.StackTrace, Level.Error);
-        }
-
-        private void WriteEvent(String message, Level logLevel = null, Exception ex = null)
-        {
-            Log.Logger.Log(GetType(), logLevel ?? Level.Info, message, ex);
-        }
-
         protected override void OnStart(string[] _)
         {
             _envs = _descriptor.EnvironmentVariables;
@@ -233,7 +228,7 @@ namespace winsw
                 catch (Exception e)
                 {
                     LogEvent("Failed to download " + d.From + " to " + d.To + "\n" + e.Message);
-                    WriteEvent("Failed to download " + d.From +" to "+d.To, e);
+                    Log.Error("Failed to download " + d.From +" to "+d.To, e);
                     // but just keep going
                 }
             }
@@ -250,23 +245,14 @@ namespace winsw
             }
 
             LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
-            WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
+            Log.Info("Starting " + _descriptor.Executable + ' ' + startarguments);
 
             // Load and start extensions
-            ExtensionManager.LoadExtensions(this);
-            try
-            {
-                ExtensionManager.OnStart(this);
-            }
-            catch (ExtensionException ex)
-            {
-                LogEvent("Failed to start extension  " + ex.ExtensionId + "\n" + ex.Message, EventLogEntryType.Error);
-                WriteEvent("Failed to start extension  " + ex.ExtensionId, ex);
-                //TODO: Exit on error?
-            }
+            ExtensionManager.LoadExtensions();
+            ExtensionManager.FireOnWrapperStarted();
 
             LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
-            WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
+            Log.Info("Starting " + _descriptor.Executable + ' ' + startarguments);
 
             StartProcess(_process, startarguments, _descriptor.Executable);
             ExtensionManager.FireOnProcessStarted(_process);
@@ -288,7 +274,7 @@ namespace winsw
             }
             catch (Exception ex)
             {
-                WriteEvent("Shutdown exception", ex);
+                Log.Error("Shutdown exception", ex);
             }
         }
 
@@ -302,7 +288,7 @@ namespace winsw
             }
             catch (Exception ex)
             {
-                WriteEvent("Stop exception", ex);
+                Log.Error("Cannot stop exception", ex);
             }
         }
 
@@ -313,14 +299,14 @@ namespace winsw
         {
             string stoparguments = _descriptor.Stoparguments;
             LogEvent("Stopping " + _descriptor.Id);
-            WriteEvent("Stopping " + _descriptor.Id);
+            Log.Info("Stopping " + _descriptor.Id);
             _orderlyShutdown = true;
 
             if (stoparguments == null)
             {
                 try
                 {
-                    WriteEvent("ProcessKill " + _process.Id);
+                    Log.Debug("ProcessKill " + _process.Id);
                     ProcessHelper.StopProcessAndChildren(_process.Id, _descriptor.StopTimeout, _descriptor.StopParentProcessFirst);
                     ExtensionManager.FireOnProcessTerminated(_process);
                 }
@@ -345,29 +331,21 @@ namespace winsw
 
                 StartProcess(stopProcess, stoparguments, executable);
 
-                WriteEvent("WaitForProcessToExit "+_process.Id+"+"+stopProcess.Id);
+                Log.Debug("WaitForProcessToExit " + _process.Id + "+" + stopProcess.Id);
                 WaitForProcessToExit(_process);
                 WaitForProcessToExit(stopProcess);
                 SignalShutdownComplete();
             }
 
             // Stop extensions      
-            try
-            {
-                ExtensionManager.OnStop(this);
-            }
-            catch (ExtensionException ex)
-            {
-                LogEvent("Failed to stop extension  " + ex.ExtensionId + "\n" + ex.Message, EventLogEntryType.Error);
-                WriteEvent("Failed to stop extension  " + ex.ExtensionId, ex);
-            }
+            ExtensionManager.FireBeforeWrapperStopped();
 
             if (_systemShuttingdown && _descriptor.BeepOnShutdown) 
             {
                 Console.Beep();
             }
 
-            WriteEvent("Finished " + _descriptor.Id);
+            Log.Info("Finished " + _descriptor.Id);
         }
 
         private void WaitForProcessToExit(Process processoWait)
@@ -458,7 +436,7 @@ namespace winsw
             ps.EnvironmentVariables[WinSWSystem.ENVVAR_NAME_SERVICE_ID.ToLower()] = _descriptor.Id;
 
             processToStart.Start();
-            WriteEvent("Started " + processToStart.Id);
+            Log.Info("Started " + processToStart.Id);
 
             var priority = _descriptor.Priority;
             if (priority != ProcessPriorityClass.Normal)
@@ -509,6 +487,7 @@ namespace winsw
             try
             {
                 Run(args);
+                Log.Info("Completed. Exit code is 0");
                 return 0;
             }
             catch (WmiException e)
@@ -554,7 +533,7 @@ namespace winsw
             
             if (isCLIMode) // CLI mode, in-service mode otherwise
             {               
-                Log.Debug("Starting ServiceWrapper in CLI mode");
+                Log.Info("Starting ServiceWrapper in the CLI mode");
 
                 // Get service info for the future use
                 Win32Services svc = new WmiRoot().GetCollection<Win32Services>();
@@ -761,17 +740,25 @@ namespace winsw
                 throw new Exception("Unknown command: " + args[0]);
 
             }
+            else
+            {
+                Log.Info("Starting ServiceWrapper in the service mode");
+            }
             Run(new WrapperService());
         }
 
         private static void InitLoggers(ServiceDescriptor d, bool enableCLILogging)
         {
+            // TODO: Make logging levels configurable
             Level logLevel = Level.Debug;
+            Level eventLogLevel = Level.Warn;
 
             // Legacy format from winsw-1.x: (DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - " + message);
             PatternLayout pl = new PatternLayout { ConversionPattern = "%d %-5p - %m%n" };
             pl.ActivateOptions();
 
+            List<IAppender> appenders = new List<IAppender>();
+
             // wrapper.log
             String wrapperLogPath = Path.Combine(d.LogDirectory, d.BaseName + ".wrapper.log");
             var wrapperLog = new FileAppender
@@ -785,7 +772,7 @@ namespace winsw
                 Layout = pl
             };
             wrapperLog.ActivateOptions();
-            BasicConfigurator.Configure(wrapperLog);
+            appenders.Add(wrapperLog);
 
             // Also display logs in CLI if required
             if (enableCLILogging)
@@ -794,11 +781,23 @@ namespace winsw
                 {
                     Name = "Wrapper console log", 
                     Threshold = logLevel,
-                    Layout = pl               
+                    Layout = pl,
                 };
                 consoleAppender.ActivateOptions();
-                ((Logger)Log.Logger).AddAppender(consoleAppender);
+                appenders.Add(consoleAppender);
             }
+
+            // System log
+            var systemEventLogger = new ServiceEventLogAppender 
+            {
+                Name = "System event log",
+                Threshold = eventLogLevel,
+                provider  = eventLogProvider
+            };
+            systemEventLogger.ActivateOptions();
+            appenders.Add(systemEventLogger);
+
+            BasicConfigurator.Configure(appenders.ToArray());
         }
 
         private static string ReadPassword()

+ 1 - 0
src/Core/ServiceWrapper/winsw.csproj

@@ -79,6 +79,7 @@
     </Compile>
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="SigIntHelper.cs" />
+    <Compile Include="Logging\WrapperServiceEventLogProvider.cs" />
   </ItemGroup>
   <ItemGroup>
     <Content Include="manifest.xml" />

+ 3 - 3
src/Core/WinSWCore/Extensions/AbstractWinSWExtension.cs

@@ -9,17 +9,17 @@ namespace winsw.Extensions
         public abstract String DisplayName { get; }
         public WinSWExtensionDescriptor Descriptor { get; set; }
 
-        public virtual void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger)
+        public virtual void Configure(ServiceDescriptor descriptor, XmlNode node)
         {
             // Do nothing
         }
 
-        public virtual void OnStart(IEventWriter eventWriter)
+        public virtual void OnWrapperStarted()
         {
             // Do nothing
         }
 
-        public virtual void OnStop(IEventWriter eventWriter)
+        public virtual void BeforeWrapperStopped()
         {
             // Do nothing
         }

+ 3 - 3
src/Core/WinSWCore/Extensions/IWinSWExtension.cs

@@ -29,14 +29,14 @@ namespace winsw.Extensions
         /// </summary>
         /// <param name="descriptor">Service descriptor</param>
         /// <param name="node">Configuration node</param>
-        void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger);
+        void Configure(ServiceDescriptor descriptor, XmlNode node);
 
         /// <summary>
         /// Start handler. Called during startup of the service before the child process.
         /// </summary>
         /// <param name="logger">Logger</param>
         /// <exception cref="ExtensionException">Any error during execution</exception>
-        void OnStart(IEventWriter logger);
+        void OnWrapperStarted();
 
         /// <summary>
         /// Handler, which is being invoked once the child process is started.
@@ -59,6 +59,6 @@ namespace winsw.Extensions
         /// </summary>
         /// <param name="logger">Logger</param>
         /// <exception cref="ExtensionException">Any error during execution</exception>
-        void OnStop(IEventWriter logger);
+        void BeforeWrapperStopped();
     }
 }

+ 33 - 16
src/Core/WinSWCore/Extensions/WinSWExtensionManager.cs

@@ -22,26 +22,43 @@ namespace winsw.Extensions
         }
 
         /// <summary>
-        /// Starts all extensions
+        /// Notifies all extensions that the wrapper is being started.
+        /// They are supposed to run the initialization logic.
+        /// If any extensions fails, WinSW startup should be interrupted.
         /// </summary>
-        /// <exception cref="ExtensionException">Start failure</exception>
-        public void OnStart(IEventWriter logger) 
+        /// <exception cref="Exception">Start failure</exception>
+        public void FireOnWrapperStarted() 
         {
             foreach (var ext in Extensions)
             {
-                ext.Value.OnStart(logger);     
+                try
+                {
+                    ext.Value.OnWrapperStarted();
+                }
+                catch (ExtensionException ex)
+                {
+                    Log.Fatal("onWrapperStarted() handler failed for " + ext.Value.DisplayName, ex);
+                    throw ex; // Propagate error to stop the startup
+                }
             }
         }
 
         /// <summary>
-        /// Stops all extensions
+        /// Notifies all extensions that the wrapper is being stopped.
+        /// If an error happens, further extensions will be tried
         /// </summary>
-        /// <exception cref="ExtensionException">Stop failure</exception>
-        public void OnStop(IEventWriter logger)
+        public void FireBeforeWrapperStopped()
         {
             foreach (var ext in Extensions)
-            {
-                ext.Value.OnStop(logger);
+            { 
+                try
+                {
+                    ext.Value.BeforeWrapperStopped();
+                }
+                catch (ExtensionException ex)
+                {
+                    Log.Error("beforeWrapperStopped() handler failed for " + ext.Value.DisplayName, ex);
+                }
             }
         }
 
@@ -86,17 +103,17 @@ namespace winsw.Extensions
         //TODO: Implement loading of external extensions. Current version supports internal hack
         #region Extension load management
 
-        /// <summary>
+        
         /// Loads extensions according to the configuration file.
         /// </summary>
         /// <param name="logger">Logger</param>
         /// <exception cref="Exception">Loading failure</exception>
-        public void LoadExtensions(IEventWriter logger)
+        public void LoadExtensions()
         {
             var extensionIds = ServiceDescriptor.ExtensionIds;
             foreach (String extensionId in extensionIds) 
             {
-                LoadExtension(extensionId, logger);
+                LoadExtension(extensionId);
             }
         }
 
@@ -106,7 +123,7 @@ namespace winsw.Extensions
         /// <param name="id">Extension ID</param>
         /// <param name="logger">Logger</param>
         /// <exception cref="Exception">Loading failure</exception>
-        private void LoadExtension(string id, IEventWriter logger)
+        private void LoadExtension(string id)
         {
             if (Extensions.ContainsKey(id))
             {
@@ -127,7 +144,7 @@ namespace winsw.Extensions
                 extension.Descriptor = descriptor;
                 try
                 {
-                    extension.Configure(ServiceDescriptor, configNode, logger);
+                    extension.Configure(ServiceDescriptor, configNode);
                 }
                 catch (Exception ex)
                 { // Consider any unexpected exception as fatal
@@ -135,11 +152,11 @@ namespace winsw.Extensions
                     throw ex;
                 }
                 Extensions.Add(id, extension);
-                logger.LogEvent("Extension loaded: "+id, EventLogEntryType.Information);
+                Log.Info("Extension loaded: " + id);
             }
             else
             {
-                logger.LogEvent("Extension is disabled: " + id, EventLogEntryType.Warning);
+                Log.Warn("Extension is disabled: " + id);
             }
             
         }

+ 19 - 0
src/Core/WinSWCore/Logging/IServiceEventLogProvider.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+namespace winsw.Logging
+{
+    /// <summary>
+    /// Indicates that the class may reference the event log
+    /// </summary>
+    public interface IServiceEventLogProvider
+    {
+        /// <summary>
+        /// Locates Event Log for the service.
+        /// </summary>
+        /// <returns>Event Log or null if it is not avilable</returns>
+        EventLog locate();
+    }
+}

+ 43 - 0
src/Core/WinSWCore/Logging/ServiceEventLogAppender.cs

@@ -0,0 +1,43 @@
+using log4net.Appender;
+using log4net.Core;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+namespace winsw.Logging
+{
+    /// <summary>
+    /// Implementes service Event log appender for log4j.
+    /// The implementation presumes that service gets initialized after the logging.
+    /// </summary>
+    public class ServiceEventLogAppender : AppenderSkeleton
+    {
+        public IServiceEventLogProvider provider { get; set; }
+
+        override protected void Append(LoggingEvent loggingEvent)
+        {
+            EventLog eventLog = provider.locate();
+            if (eventLog != null)
+            {
+                // We write the event iff the provider is ready
+                eventLog.WriteEntry(loggingEvent.RenderedMessage, toEventLogEntryType(loggingEvent.Level));
+            }
+        }
+
+        private static EventLogEntryType toEventLogEntryType(Level level)
+        {
+            if (level.Value >= Level.Error.Value)
+            {
+                return EventLogEntryType.Error;
+            }
+            if (level.Value >= Level.Warn.Value)
+            {
+                return EventLogEntryType.Warning;
+            }
+
+            // All other events will be posted as information
+            return EventLogEntryType.Information;
+        }
+    }
+}

+ 0 - 13
src/Core/WinSWCore/Util/IEventWriter.cs

@@ -1,13 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Diagnostics;
-
-namespace winsw.Util
-{
-    public interface IEventWriter
-    {
-        void LogEvent(String message);
-        void LogEvent(String message, EventLogEntryType type);
-    }
-}

+ 3 - 1
src/Core/WinSWCore/WinSWCore.csproj

@@ -54,12 +54,13 @@
     <Compile Include="Extensions\WinSWExtensionDescriptor.cs" />
     <Compile Include="Extensions\WinSWExtensionManager.cs" />
     <Compile Include="LogAppenders.cs" />
+    <Compile Include="Logging\ServiceEventLogAppender.cs" />
+    <Compile Include="Logging\IServiceEventLogProvider.cs" />
     <Compile Include="Native\Advapi32.cs" />
     <Compile Include="Native\Kernel32.cs" />
     <Compile Include="PeriodicRollingCalendar.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="ServiceDescriptor.cs" />
-    <Compile Include="Util\IEventWriter.cs" />
     <Compile Include="Util\ProcessHelper.cs" />
     <Compile Include="Util\SigIntHelper.cs" />
     <Compile Include="Util\XmlHelper.cs" />
@@ -71,6 +72,7 @@
   <ItemGroup>
     <None Include="packages.config" />
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

+ 2 - 2
src/Plugins/RunawayProcessKiller/RunawayProcessKillerExtension.cs

@@ -43,7 +43,7 @@ namespace winsw.Plugins.RunawayProcessKiller
             this.Pidfile = pidfile;
         }
 
-        public override void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger)
+        public override void Configure(ServiceDescriptor descriptor, XmlNode node)
         {
             // We expect the upper logic to process any errors
             // TODO: a better parser API for types would be useful
@@ -57,7 +57,7 @@ namespace winsw.Plugins.RunawayProcessKiller
         /// This method checks if the PID file is stored on the disk and then terminates runaway processes if they exist.
         /// </summary>
         /// <param name="logger">Unused logger</param>
-        public override void OnStart(IEventWriter logger)
+        public override void OnWrapperStarted()
         {
             // Read PID file from the disk
             int pid;

+ 14 - 13
src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.cs

@@ -4,6 +4,7 @@ using System.Xml;
 using System.Diagnostics;
 using winsw.Extensions;
 using winsw.Util;
+using log4net;
 
 namespace winsw.Plugins.SharedDirectoryMapper
 {
@@ -14,6 +15,8 @@ namespace winsw.Plugins.SharedDirectoryMapper
 
         public override String DisplayName { get { return "Shared Directory Mapper"; } }
 
+        private static readonly ILog Logger = LogManager.GetLogger(typeof(SharedDirectoryMapper));
+
         public SharedDirectoryMapper()
         {
         }
@@ -24,7 +27,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
             _entries.Add(config);
         }
 
-        public override void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger)
+        public override void Configure(ServiceDescriptor descriptor, XmlNode node)
         {
             var nodes = XmlHelper.SingleNode(node, "mapping", false).SelectNodes("map");
             if (nodes != null)
@@ -41,30 +44,30 @@ namespace winsw.Plugins.SharedDirectoryMapper
             }
         }
 
-        public override void OnStart(IEventWriter eventWriter)
+        public override void OnWrapperStarted()
         {
             foreach (SharedDirectoryMapperConfig config in _entries)
             {
                 if (config.EnableMapping)
                 {
-                    eventWriter.LogEvent(DisplayName + ": Mapping shared directory " + config.UNCPath + " to " + config.Label, EventLogEntryType.Information);
+                    Logger.Info(DisplayName + ": Mapping shared directory " + config.UNCPath + " to " + config.Label);
                     try
                     {
                         _mapper.MapDirectory(config.Label, config.UNCPath);
                     }
                     catch (MapperException ex)
                     {
-                        HandleMappingError(config, eventWriter, ex);
+                        HandleMappingError(config, ex);
                     }
                 }
                 else
                 {
-                    eventWriter.LogEvent(DisplayName + ": Mapping of " + config.Label + " is disabled", EventLogEntryType.Warning);
+                    Logger.Warn(DisplayName + ": Mapping of " + config.Label + " is disabled");
                 }
             }
         }
 
-        public override void OnStop(IEventWriter eventWriter)
+        public override void BeforeWrapperStopped()
         {
             foreach (SharedDirectoryMapperConfig config in _entries)
             {
@@ -76,18 +79,16 @@ namespace winsw.Plugins.SharedDirectoryMapper
                     }
                     catch (MapperException ex)
                     {
-                        HandleMappingError(config, eventWriter, ex);
+                        HandleMappingError(config, ex);
                     }
                 }
             }
         }
 
-        private void HandleMappingError(SharedDirectoryMapperConfig config, IEventWriter eventWriter, MapperException ex) {
-            String prefix = "Mapping of " + config.Label+ " ";
-            eventWriter.LogEvent(prefix + "STDOUT: " + ex.Process.StandardOutput.ReadToEnd(), EventLogEntryType.Information);
-            eventWriter.LogEvent(prefix + "STDERR: " + ex.Process.StandardError.ReadToEnd(), EventLogEntryType.Information);
-
-            throw new ExtensionException(Descriptor.Id, DisplayName + ": " + prefix + "failed", ex);
+        private void HandleMappingError(SharedDirectoryMapperConfig config, MapperException ex) {
+            Logger.Error("Mapping of " + config.Label + " failed. STDOUT: " + ex.Process.StandardOutput.ReadToEnd() 
+                + " \r\nSTDERR: " + ex.Process.StandardError.ReadToEnd(), ex);
+            throw new ExtensionException(Descriptor.Id, DisplayName + ": Mapping of " + config.Label + "failed", ex);
         }
     }
 }

+ 15 - 0
src/Plugins/SharedDirectoryMapper/SharedDirectoryMapper.csproj

@@ -11,6 +11,8 @@
     <AssemblyName>SharedDirectoryMapper</AssemblyName>
     <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <RestorePackages>true</RestorePackages>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -34,6 +36,9 @@
     <AssemblyOriginatorKeyFile>$(SolutionDir)..\winsw_key.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="log4net">
+      <HintPath>..\..\packages\log4net.2.0.3\lib\net20-full\log4net.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
@@ -50,7 +55,17 @@
       <Name>WinSWCore</Name>
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+  </Target>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
   <Target Name="BeforeBuild">

+ 4 - 0
src/Plugins/SharedDirectoryMapper/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="log4net" version="2.0.3" targetFramework="net20" />
+</packages>

+ 4 - 6
src/Test/winswTests/Extensions/RunawayProcessKillerTest.cs

@@ -2,7 +2,6 @@
 using NUnit.Framework;
 using winsw.Extensions;
 using winsw.Plugins.SharedDirectoryMapper;
-using winswTests.util;
 using winsw.Plugins.RunawayProcessKiller;
 
 namespace winswTests.Extensions
@@ -11,7 +10,6 @@ namespace winswTests.Extensions
     class RunawayProcessKillerExtensionTest : ExtensionTestBase
     {
         ServiceDescriptor _testServiceDescriptor;
-        readonly TestLogger _logger = new TestLogger();
 
         string testExtension = getExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
             
@@ -41,7 +39,7 @@ namespace winswTests.Extensions
         public void LoadExtensions()
         {
             WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
-            manager.LoadExtensions(_logger);
+            manager.LoadExtensions();
             Assert.AreEqual(1, manager.Extensions.Count, "One extension should be loaded");
 
             // Check the file is correct
@@ -56,9 +54,9 @@ namespace winswTests.Extensions
         public void StartStopExtension()
         {
             WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
-            manager.LoadExtensions(_logger);
-            manager.OnStart(_logger);
-            manager.OnStop(_logger);
+            manager.LoadExtensions();
+            manager.FireOnWrapperStarted();
+            manager.FireBeforeWrapperStopped();
         }
     }
 }

+ 4 - 6
src/Test/winswTests/Extensions/SharedDirectoryMapperTest.cs

@@ -2,7 +2,6 @@
 using NUnit.Framework;
 using winsw.Extensions;
 using winsw.Plugins.SharedDirectoryMapper;
-using winswTests.util;
 
 namespace winswTests.Extensions
 {
@@ -10,7 +9,6 @@ namespace winswTests.Extensions
     class SharedDirectoryMapperTest : ExtensionTestBase
     {
         ServiceDescriptor _testServiceDescriptor;
-        readonly TestLogger _logger = new TestLogger();
 
         string testExtension = getExtensionClassNameWithAssembly(typeof(SharedDirectoryMapper));
 
@@ -48,7 +46,7 @@ namespace winswTests.Extensions
         public void LoadExtensions()
         {
             WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
-            manager.LoadExtensions(_logger);
+            manager.LoadExtensions();
             Assert.AreEqual(2, manager.Extensions.Count, "Two extensions should be loaded");
         }
 
@@ -56,9 +54,9 @@ namespace winswTests.Extensions
         public void StartStopExtension()
         {
             WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
-            manager.LoadExtensions(_logger);
-            manager.OnStart(_logger);
-            manager.OnStop(_logger);
+            manager.LoadExtensions();
+            manager.FireOnWrapperStarted();
+            manager.FireBeforeWrapperStopped();
         }
     }
 }

+ 0 - 19
src/Test/winswTests/Util/TestLogger.cs

@@ -1,19 +0,0 @@
-using System;
-using System.Diagnostics;
-using winsw.Util;
-
-namespace winswTests.util
-{
-    class TestLogger : IEventWriter
-    {
-        public void LogEvent(String message)
-        {
-            Console.WriteLine(message);
-        }
-
-        public void LogEvent(String message, EventLogEntryType type)
-        {
-            Console.WriteLine("[" + type + "]" + message);
-        }
-    }
-}

+ 0 - 1
src/Test/winswTests/winswTests.csproj

@@ -61,7 +61,6 @@
     <Compile Include="MainTest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="ServiceDescriptorTests.cs" />
-    <Compile Include="Util\TestLogger.cs" />
     <Compile Include="Util\CLITestHelper.cs" />
   </ItemGroup>
   <ItemGroup>

+ 1 - 0
src/packages/repositories.config

@@ -3,5 +3,6 @@
   <repository path="..\Core\ServiceWrapper\packages.config" />
   <repository path="..\Core\WinSWCore\packages.config" />
   <repository path="..\Plugins\RunawayProcessKiller\packages.config" />
+  <repository path="..\Plugins\SharedDirectoryMapper\packages.config" />
   <repository path="..\Test\winswTests\packages.config" />
 </repositories>