From 02080b2402613d8a3e578f56cb6aff2d5779fac2 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Fri, 20 Jul 2018 14:05:57 -0400 Subject: [PATCH] Add logging to the host watchdog --- src/Tgstation.Server.Host.Console/Program.cs | 35 ++++++++--- src/Tgstation.Server.Host.Service/Program.cs | 4 +- .../ServerService.cs | 37 ++++++++--- .../Tgstation.Server.Host.Service.csproj | 5 +- .../IWatchdogFactory.cs | 7 ++- .../Watchdog.cs | 62 ++++++++++++++----- .../WatchdogFactory.cs | 5 +- 7 files changed, 117 insertions(+), 38 deletions(-) diff --git a/src/Tgstation.Server.Host.Console/Program.cs b/src/Tgstation.Server.Host.Console/Program.cs index f7ab19864e..b407ddbcb1 100644 --- a/src/Tgstation.Server.Host.Console/Program.cs +++ b/src/Tgstation.Server.Host.Console/Program.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; +using System.Collections.Generic; +using System.Threading.Tasks; using Tgstation.Server.Host.Watchdog; namespace Tgstation.Server.Host.Console @@ -13,11 +16,29 @@ namespace Tgstation.Server.Host.Console /// internal static IWatchdogFactory WatchdogFactory { get; set; } = new WatchdogFactory(); - /// - /// Entrypoint for the application - /// - /// The arguments for the - /// A representing the running operation - internal static Task Main(string[] args) => WatchdogFactory.CreateWatchdog().RunAsync(args, default); + /// + /// Entrypoint for the application + /// + /// The arguments for the + /// A representing the running operation + internal static async Task Main(string[] args) + { + using (var loggerFactory = new LoggerFactory()) + { + var arguments = new List(args); + var trace = arguments.Remove("--trace-host-watchdog"); + var debug = arguments.Remove("--debug-host-watchdog"); + + loggerFactory.AddConsole(trace ? LogLevel.Trace : debug ? LogLevel.Debug : LogLevel.Information, true); + + if (trace && debug) + { + loggerFactory.CreateLogger(nameof(Program)).LogCritical("Please specify only 1 of --trace-host-watchdog or --debug-host-watchdog!"); + return; + } + //default CancellationToken because the Host handles that internally + await WatchdogFactory.CreateWatchdog(loggerFactory).RunAsync(arguments.ToArray(), default).ConfigureAwait(false); + } + } } } diff --git a/src/Tgstation.Server.Host.Service/Program.cs b/src/Tgstation.Server.Host.Service/Program.cs index 1a5a1a9dcf..742b7e7ac6 100644 --- a/src/Tgstation.Server.Host.Service/Program.cs +++ b/src/Tgstation.Server.Host.Service/Program.cs @@ -1,4 +1,5 @@ using McMaster.Extensions.CommandLineUtils; +using Microsoft.Extensions.Logging; using System; using System.Collections.Specialized; using System.Configuration.Install; @@ -97,7 +98,8 @@ namespace Tgstation.Server.Host.Service installer.Uninstall(null); } else - ServiceBase.Run(new ServerService(new WatchdogFactory())); + using (var loggerFactory = new LoggerFactory()) + ServiceBase.Run(new ServerService(new WatchdogFactory(), loggerFactory)); } /// diff --git a/src/Tgstation.Server.Host.Service/ServerService.cs b/src/Tgstation.Server.Host.Service/ServerService.cs index 1242ea88fc..97ff6944ea 100644 --- a/src/Tgstation.Server.Host.Service/ServerService.cs +++ b/src/Tgstation.Server.Host.Service/ServerService.cs @@ -1,4 +1,8 @@ -using System; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.EventLog; +using Microsoft.Extensions.Logging.EventLog.Internal; +using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.ServiceProcess; using System.Threading; @@ -10,7 +14,7 @@ namespace Tgstation.Server.Host.Service /// /// Represents a as a /// - sealed class ServerService : ServiceBase + sealed class ServerService : ServiceBase, IEventLog { /// /// The canonical windows service name @@ -20,7 +24,7 @@ namespace Tgstation.Server.Host.Service /// /// The for the /// - IWatchdog watchdog; + readonly IWatchdog watchdog; /// /// The recieved from of @@ -32,18 +36,33 @@ namespace Tgstation.Server.Host.Service /// CancellationTokenSource cancellationTokenSource; - /// - /// Construct a - /// - /// The to create with - public ServerService(IWatchdogFactory watchdogFactory) + /// + /// Construct a + /// + /// The to create with + /// The for + public ServerService(IWatchdogFactory watchdogFactory, ILoggerFactory loggerFactory) { if (watchdogFactory == null) throw new ArgumentNullException(nameof(watchdogFactory)); + if(loggerFactory == null) + throw new ArgumentNullException(nameof(loggerFactory)); + + loggerFactory.AddEventLog(new EventLogSettings + { + EventLog = this + }); + ServiceName = Name; - watchdog = watchdogFactory.CreateWatchdog(); + watchdog = watchdogFactory.CreateWatchdog(loggerFactory); } + /// + public int MaxMessageSize => (int)EventLog.MaximumKilobytes * 1024; + + /// + public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) => EventLog.WriteEntry(message, type, eventID, category); + /// [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "cancellationTokenSource")] protected override void Dispose(bool disposing) diff --git a/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj b/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj index 2ea140835e..ed7a2783e3 100644 --- a/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj +++ b/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj @@ -90,7 +90,10 @@ - 2.2.4 + 2.2.5 + + + 2.1.1 diff --git a/src/Tgstation.Server.Host.Watchdog/IWatchdogFactory.cs b/src/Tgstation.Server.Host.Watchdog/IWatchdogFactory.cs index a1b203761a..9534709a2f 100644 --- a/src/Tgstation.Server.Host.Watchdog/IWatchdogFactory.cs +++ b/src/Tgstation.Server.Host.Watchdog/IWatchdogFactory.cs @@ -1,4 +1,6 @@ -namespace Tgstation.Server.Host.Watchdog +using Microsoft.Extensions.Logging; + +namespace Tgstation.Server.Host.Watchdog { /// /// Factory for creating s @@ -8,7 +10,8 @@ /// /// Create a /// + /// The to use for error reporting /// A new - IWatchdog CreateWatchdog(); + IWatchdog CreateWatchdog(ILoggerFactory loggerFactory); } } diff --git a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs index ef74711534..e4c3fc6a74 100644 --- a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.Logging; +using System; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -24,38 +25,67 @@ namespace Tgstation.Server.Host.Watchdog /// readonly IIsolatedAssemblyContextFactory isolatedAssemblyLoader; + /// + /// The for the + /// + readonly ILogger logger; + /// /// Construct a /// /// The value of /// The value of /// The value of - public Watchdog(IServerFactory initialServerFactory, IActiveAssemblyDeleter activeAssemblyDeleter, IIsolatedAssemblyContextFactory isolatedAssemblyLoader) + /// The value of + public Watchdog(IServerFactory initialServerFactory, IActiveAssemblyDeleter activeAssemblyDeleter, IIsolatedAssemblyContextFactory isolatedAssemblyLoader, ILogger logger) { this.initialServerFactory = initialServerFactory ?? throw new ArgumentNullException(nameof(initialServerFactory)); this.activeAssemblyDeleter = activeAssemblyDeleter ?? throw new ArgumentNullException(nameof(activeAssemblyDeleter)); this.isolatedAssemblyLoader = isolatedAssemblyLoader ?? throw new ArgumentNullException(nameof(isolatedAssemblyLoader)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// public async Task RunAsync(string[] args, CancellationToken cancellationToken) { - //first run the host we started with - var serverFactory = initialServerFactory; - var assemblyPath = serverFactory.GetType().Assembly.Location; - do - { - var server = serverFactory.CreateServer(args); - await server.RunAsync(cancellationToken).ConfigureAwait(false); + logger.LogInformation("Host watchdog starting..."); + try + { + //first run the host we started with + logger.LogTrace("Running with initial server factory..."); + var serverFactory = initialServerFactory; + logger.LogTrace("Determining location of host assembly..."); + var assemblyPath = serverFactory.GetType().Assembly.Location; + logger.LogDebug("Path to initial host assembly: {0}", assemblyPath); + do + using (logger.BeginScope("Host invocation")) + { + var server = serverFactory.CreateServer(args); + logger.LogTrace("Running server..."); + await server.RunAsync(cancellationToken).ConfigureAwait(false); + logger.LogInformation("Active host exited."); - if (server.UpdatePath == null) - break; + if (server.UpdatePath == null) + break; - activeAssemblyDeleter.DeleteActiveAssembly(assemblyPath); - File.Move(server.UpdatePath, assemblyPath); - serverFactory = isolatedAssemblyLoader.CreateIsolatedServerFactory(assemblyPath); - } - while (!cancellationToken.IsCancellationRequested); + logger.LogInformation("Update path is set to \"{0}\", attempting host assembly hotswap...", server.UpdatePath); + activeAssemblyDeleter.DeleteActiveAssembly(assemblyPath); + logger.LogTrace("Moving new host assembly in place..."); + File.Move(server.UpdatePath, assemblyPath); + logger.LogTrace("Atttempting to create new server factory..."); + serverFactory = isolatedAssemblyLoader.CreateIsolatedServerFactory(assemblyPath); + } + while (!cancellationToken.IsCancellationRequested); + } + catch (OperationCanceledException) + { + logger.LogDebug("Exiting due to cancellation..."); + } + catch (Exception e) + { + logger.LogCritical("Error running host assembly! Exception: {0}", e); + } + logger.LogInformation("Host watchdog exiting..."); } } } diff --git a/src/Tgstation.Server.Host.Watchdog/WatchdogFactory.cs b/src/Tgstation.Server.Host.Watchdog/WatchdogFactory.cs index ddb00dafe0..436e051429 100644 --- a/src/Tgstation.Server.Host.Watchdog/WatchdogFactory.cs +++ b/src/Tgstation.Server.Host.Watchdog/WatchdogFactory.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace Tgstation.Server.Host.Watchdog @@ -8,6 +9,6 @@ namespace Tgstation.Server.Host.Watchdog { /// [ExcludeFromCodeCoverage] - public IWatchdog CreateWatchdog() => new Watchdog(new ServerFactory(), RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? (IActiveAssemblyDeleter)new WindowsActiveAssemblyDeleter() : new PosixActiveAssemblyDeleter(), new IsolatedAssemblyContextFactory()); + public IWatchdog CreateWatchdog(ILoggerFactory loggerFactory) => new Watchdog(new ServerFactory(), RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? (IActiveAssemblyDeleter)new WindowsActiveAssemblyDeleter() : new PosixActiveAssemblyDeleter(), new IsolatedAssemblyContextFactory(), loggerFactory.CreateLogger()); } }