diff --git a/devdocs/todo.txt b/devdocs/todo.txt index b71140f0..57d5906d 100644 --- a/devdocs/todo.txt +++ b/devdocs/todo.txt @@ -16,7 +16,7 @@ UPDATE all the things before commencing work - Find and read changes for 3.1 dotnet core and Note that that above is for v2.2 my controllers appear to date from the v1 .net core era and I will need to then move them to the .netcore 3.1 era changes - This is important and worth doing to make sure everything will work going forward (the swashbuckle [apicontroller] attribute debacle is proof of this) - +- Should not process jobs during boot, maybe serverstate=booting would be useful here? ------------------------------------------------------------------------------------------------------------------------------------- Need a sprint to get to a fully testable client with entry form, list and as much as possible all features from COMMON-* specs list diff --git a/server/AyaNova/ControllerHelpers/ApiCustomExceptionFilter.cs b/server/AyaNova/ControllerHelpers/ApiCustomExceptionFilter.cs index f2f3af1a..d2e89524 100644 --- a/server/AyaNova/ControllerHelpers/ApiCustomExceptionFilter.cs +++ b/server/AyaNova/ControllerHelpers/ApiCustomExceptionFilter.cs @@ -22,14 +22,24 @@ namespace AyaNova.Api.ControllerHelpers { private readonly ILogger log; - public ApiCustomExceptionFilter(ILoggerFactory logger) + // public ApiCustomExceptionFilter(ILoggerFactory logger) + // { + // if (logger == null) + // { + // throw new ArgumentNullException(nameof(logger)); + // } + + // this.log = logger.CreateLogger("Server Exception"); + // } + + public ApiCustomExceptionFilter(ILogger logger) { if (logger == null) { throw new ArgumentNullException(nameof(logger)); } - this.log = logger.CreateLogger("Server Exception"); + this.log = logger; } @@ -71,10 +81,10 @@ namespace AyaNova.Api.ControllerHelpers if (loggableError) log.LogError(context.Exception, "Error"); - + //Track this exception - IMetrics metrics = (IMetrics)ServiceProviderProvider.Provider.GetService(typeof(IMetrics)); - metrics.Measure.Meter.Mark(MetricsRegistry.UnhandledExceptionsMeter,context.Exception.GetType().ToString()); + IMetrics metrics = (IMetrics)ServiceProviderProvider.Provider.GetService(typeof(IMetrics)); + metrics.Measure.Meter.Mark(MetricsRegistry.UnhandledExceptionsMeter, context.Exception.GetType().ToString()); HttpResponse response = context.HttpContext.Response; diff --git a/server/AyaNova/Controllers/WidgetController.cs b/server/AyaNova/Controllers/WidgetController.cs index 87bd33de..533b1be8 100644 --- a/server/AyaNova/Controllers/WidgetController.cs +++ b/server/AyaNova/Controllers/WidgetController.cs @@ -376,6 +376,7 @@ namespace AyaNova.Api.Controllers [HttpGet("exception")] public ActionResult GetException() { + log.LogCritical("Widget::getexception->CRITICAL LOG TEST"); if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason)); throw new System.NotSupportedException("Test exception from widget controller"); diff --git a/server/AyaNova/Program.cs b/server/AyaNova/Program.cs index c50d9f40..6a34013e 100644 --- a/server/AyaNova/Program.cs +++ b/server/AyaNova/Program.cs @@ -33,10 +33,10 @@ namespace AyaNova ServerBootConfig.SetConfiguration(config); #region Initialize Logging -//NOTE: there is a logging issue that breaks all this with .net 3.1 hostbuilder vs webhostbuilder but webhostbuilder will be deprecated so we need to work around it -//the discussion about that is here: https://github.com/aspnet/AspNetCore/issues/9337 + //NOTE: there is a logging issue that breaks all this with .net 3.1 hostbuilder vs webhostbuilder but webhostbuilder will be deprecated so we need to work around it + //the discussion about that is here: https://github.com/aspnet/AspNetCore/issues/9337 -//NLOG OFFICIAL GUIDELINES FOR .net core 3.x https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3 + //NLOG OFFICIAL GUIDELINES FOR .net core 3.x https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3 //default log level NLog.LogLevel NLogLevel = NLog.LogLevel.Info; @@ -198,6 +198,12 @@ namespace AyaNova logger.Fatal(e, "BOOT: E1090 - AyaNova server can't start due to unexpected exception during initialization"); throw; } + finally + { + // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) + logger.Info("AyaNova server shutting down"); + NLog.LogManager.Shutdown(); + } } diff --git a/server/AyaNova/Startup.cs b/server/AyaNova/Startup.cs index ee90528f..93451cbd 100644 --- a/server/AyaNova/Startup.cs +++ b/server/AyaNova/Startup.cs @@ -22,23 +22,41 @@ using AyaNova.Util; using AyaNova.Generator; using AyaNova.Biz; + +using NLog.Web; +using NLog.Targets; +using NLog.Config; +using NLog.Extensions.Logging; + namespace AyaNova { public class Startup { ///////////////////////////////////////////////////////////// // - public Startup(ILogger logger, ILoggerFactory logFactory, Microsoft.AspNetCore.Hosting.IWebHostEnvironment hostingEnvironment) - { - _log = logger; + public Startup( Microsoft.AspNetCore.Hosting.IWebHostEnvironment hostingEnvironment) + {//ILogger logger, ILoggerFactory logFactory, + + // Get the factory for ILogger instances. + var nlogLoggerProvider = new NLogLoggerProvider(); + + // Create an ILogger. + _newLog = nlogLoggerProvider.CreateLogger(typeof(Startup).FullName); + + //x_log = logger; _hostingEnvironment = hostingEnvironment; - AyaNova.Util.ApplicationLogging.LoggerFactory = logFactory; + //AyaNova.Util.ApplicationLogging.LoggerFactory = logFactory; + //AyaNova.Util.ApplicationLogging.theLogger = _newLog; + AyaNova.Util.ApplicationLogging.LoggerProvider=nlogLoggerProvider; + //this must be set here ServerBootConfig.AYANOVA_CONTENT_ROOT_PATH = hostingEnvironment.ContentRootPath; } - private readonly ILogger _log; + private readonly ILogger _newLog; + + private readonly ILoggerx_log; private string _connectionString = ""; private readonly Microsoft.AspNetCore.Hosting.IWebHostEnvironment _hostingEnvironment; @@ -47,19 +65,20 @@ namespace AyaNova // public void ConfigureServices(IServiceCollection services) { - _log.LogDebug("BOOT: initializing services..."); + _newLog.LogDebug("BOOT: initializing services..."); //Server state service for shutting people out of api - _log.LogDebug("BOOT: init ApiServerState service"); + _newLog.LogDebug("BOOT: init ApiServerState service"); services.AddSingleton(new AyaNova.Api.ControllerHelpers.ApiServerState()); //Init controllers - _log.LogDebug("BOOT: init controllers"); + _newLog.LogDebug("BOOT: init controllers"); var MvcBuilder = services.AddControllers(config => { - config.Filters.Add(new AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter(AyaNova.Util.ApplicationLogging.LoggerFactory)); + // config.Filters.Add(new AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter(AyaNova.Util.ApplicationLogging.LoggerFactory)); + config.Filters.Add(new AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter(_newLog)); }); //Prevent default model binding automatic 400 page so we can consistently show *our* error to our specs @@ -69,7 +88,7 @@ namespace AyaNova options.SuppressModelStateInvalidFilter = true; }); - _log.LogDebug("BOOT: init JSON"); + _newLog.LogDebug("BOOT: init JSON"); MvcBuilder.AddNewtonsoftJson(options => { options.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc; @@ -80,41 +99,41 @@ namespace AyaNova //2019-10-17 METRICS will not work just yet with .netcore 3.1 see here https://github.com/AppMetrics/AppMetrics/issues/480 //awaiting a new release from them - //_log.LogDebug("BOOT: init Metrics service"); + //_newLog.LogDebug("BOOT: init Metrics service"); //MvcBuilder.AddMetrics(); - _log.LogDebug("BOOT: ensuring user and backup folders exist and are separate locations..."); + _newLog.LogDebug("BOOT: ensuring user and backup folders exist and are separate locations..."); FileUtil.EnsureUserAndUtilityFoldersExistAndAreNotIdentical(_hostingEnvironment.ContentRootPath); #region DATABASE _connectionString = ServerBootConfig.AYANOVA_DB_CONNECTION; //Check DB server exists and can be connected to - _log.LogDebug("BOOT: Testing database server connection..."); + _newLog.LogDebug("BOOT: Testing database server connection..."); //parse the connection string properly - DbUtil.ParseConnectionString(_log, _connectionString); + DbUtil.ParseConnectionString(_newLog, _connectionString); //Probe for database server //Will retry every 10 seconds for up to 5 minutes before bailing - if (!DbUtil.DatabaseServerExists(_log, "BOOT: waiting for db server ")) + if (!DbUtil.DatabaseServerExists(_newLog, "BOOT: waiting for db server ")) { var err = $"BOOT: E1000 - AyaNova can't connect to the database server after trying for 5 minutes (connection string is:\"{DbUtil.DisplayableConnectionString}\")"; - _log.LogCritical(err); + _newLog.LogCritical(err); throw new System.ApplicationException(err); } - _log.LogInformation("BOOT: Connected to database server - {0}", DbUtil.DisplayableConnectionString); + _newLog.LogInformation("BOOT: Connected to database server - {0}", DbUtil.DisplayableConnectionString); //ensure database is ready and present - DbUtil.EnsureDatabaseExists(_log); + DbUtil.EnsureDatabaseExists(_newLog); bool LOG_SENSITIVE_DATA = false; @@ -123,7 +142,7 @@ namespace AyaNova #endif - _log.LogDebug("BOOT: init EF service"); + _newLog.LogDebug("BOOT: init EF service"); services.AddEntityFrameworkNpgsql().AddDbContext( options => options.UseNpgsql(_connectionString @@ -143,7 +162,7 @@ namespace AyaNova // Add service and create Policy with options - _log.LogDebug("BOOT: init CORS service"); + _newLog.LogDebug("BOOT: init CORS service"); services.AddCors(options => { options.AddPolicy("CorsPolicy", @@ -223,7 +242,7 @@ namespace AyaNova ServerBootConfig.AYANOVA_JWT_SECRET = secretKey; var signingKey = new SymmetricSecurityKey(System.Text.Encoding.ASCII.GetBytes(ServerBootConfig.AYANOVA_JWT_SECRET)); - _log.LogDebug("BOOT: init Authorization service"); + _newLog.LogDebug("BOOT: init Authorization service"); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; @@ -254,7 +273,7 @@ namespace AyaNova #endregion - _log.LogDebug("BOOT: init Generator service"); + _newLog.LogDebug("BOOT: init Generator service"); services.AddSingleton(); } @@ -267,7 +286,7 @@ namespace AyaNova public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IWebHostEnvironment env, AyContext dbContext, IApiVersionDescriptionProvider provider, AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, IServiceProvider serviceProvider) { - _log.LogDebug("BOOT: configuring request pipeline..."); + _newLog.LogDebug("BOOT: configuring request pipeline..."); //this *may* be useful in the event of an issue so uncomment if necessary but errors during dev are handled equally by the logging, I think // if (env.IsDevelopment()) @@ -286,7 +305,7 @@ namespace AyaNova }); #region STATIC FILES - _log.LogDebug("BOOT: pipeline - static files"); + _newLog.LogDebug("BOOT: pipeline - static files"); app.UseDefaultFiles(); app.UseStaticFiles(); //Might need the following if the page doesn't update in the client properly @@ -304,19 +323,19 @@ namespace AyaNova // }); #endregion - _log.LogDebug("BOOT: pipeline - ROUTING"); + _newLog.LogDebug("BOOT: pipeline - ROUTING"); app.UseRouting();//this wasn't here for 2.2 but added for 3.0, needs to come before the stuff after - _log.LogDebug("BOOT: pipeline - CORS"); + _newLog.LogDebug("BOOT: pipeline - CORS"); app.UseCors("CorsPolicy"); #region AUTH / ROLES - _log.LogDebug("BOOT: pipeline - authentication"); + _newLog.LogDebug("BOOT: pipeline - authentication"); //Use authentication middleware app.UseAuthentication(); - _log.LogDebug("BOOT: pipeline - authorization"); + _newLog.LogDebug("BOOT: pipeline - authorization"); app.UseAuthorization(); @@ -348,7 +367,7 @@ namespace AyaNova #endregion - _log.LogDebug("BOOT: pipeline - ENDPOINTS"); + _newLog.LogDebug("BOOT: pipeline - ENDPOINTS"); app.UseEndpoints(endpoints => { endpoints.MapControllers(); @@ -356,7 +375,7 @@ namespace AyaNova #region SWAGGER - _log.LogDebug("BOOT: pipeline - api explorer"); + _newLog.LogDebug("BOOT: pipeline - api explorer"); // Enable middleware to serve generated Swagger as a JSON endpoint. app.UseSwagger(); app.UseSwaggerUI( @@ -393,21 +412,21 @@ namespace AyaNova if (ServerBootConfig.AYANOVA_PERMANENTLY_ERASE_DATABASE) { - _log.LogWarning("BOOT: AYANOVA_PERMANENTLY_ERASE_DATABASE is true, dropping and recreating database"); - Util.DbUtil.DropAndRecreateDb(_log); - AySchema.CheckAndUpdate(dbContext, _log); + _newLog.LogWarning("BOOT: AYANOVA_PERMANENTLY_ERASE_DATABASE is true, dropping and recreating database"); + Util.DbUtil.DropAndRecreateDb(_newLog); + AySchema.CheckAndUpdate(dbContext, _newLog); } //Check schema - _log.LogDebug("BOOT: db schema check"); - AySchema.CheckAndUpdate(dbContext, _log); + _newLog.LogDebug("BOOT: db schema check"); + AySchema.CheckAndUpdate(dbContext, _newLog); //Check database integrity - _log.LogDebug("BOOT: db integrity check"); - DbUtil.CheckFingerPrint(AySchema.EXPECTED_COLUMN_COUNT, AySchema.EXPECTED_INDEX_COUNT, _log); + _newLog.LogDebug("BOOT: db integrity check"); + DbUtil.CheckFingerPrint(AySchema.EXPECTED_COLUMN_COUNT, AySchema.EXPECTED_INDEX_COUNT, _newLog); //Initialize license - AyaNova.Core.License.Initialize(apiServerState, dbContext, _log); + AyaNova.Core.License.Initialize(apiServerState, dbContext, _newLog); //Ensure locales are present, not missing any keys and that there is a server default locale that exists LocaleBiz lb = new LocaleBiz(dbContext, 1, ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID, AuthorizationRoles.OpsAdminFull); @@ -419,7 +438,7 @@ namespace AyaNova //TESTING if (TESTING_REFRESH_DB) { - AyaNova.Core.License.Fetch(apiServerState, dbContext, _log); + AyaNova.Core.License.Fetch(apiServerState, dbContext, _newLog); Util.Seeder.SeedDatabase(Util.Seeder.SeedLevel.SmallOneManShopTrialDataSet, -7);//############################################################################################# } //TESTING @@ -446,10 +465,10 @@ namespace AyaNova //Log the active user count so it's in the log record - _log.LogInformation($"BOOT: Active techs - {UserBiz.ActiveCount}"); + _newLog.LogInformation($"BOOT: Active techs - {UserBiz.ActiveCount}"); //Log the license info so it's on the record - _log.LogInformation($"BOOT: License -\r\n=-=-=-=-=-=-=-=-=-=-\r\n{AyaNova.Core.License.LicenseInfo}=-=-=-=-=-=-=-=-=-=-"); + _newLog.LogInformation($"BOOT: License -\r\n=-=-=-=-=-=-=-=-=-=-\r\n{AyaNova.Core.License.LicenseInfo}=-=-=-=-=-=-=-=-=-=-"); @@ -457,7 +476,7 @@ namespace AyaNova apiServerState.SetOpen(); //final startup log - _log.LogInformation("BOOT: COMPLETED - SERVER IS NOW OPEN"); + _newLog.LogInformation("BOOT: COMPLETED - SERVER IS NOW OPEN"); } diff --git a/server/AyaNova/generator/Generate.cs b/server/AyaNova/generator/Generate.cs index 9baaf9fc..15d1f035 100644 --- a/server/AyaNova/generator/Generate.cs +++ b/server/AyaNova/generator/Generate.cs @@ -90,7 +90,7 @@ namespace AyaNova.Generator } catch (Exception ex) { - log.LogError("Generate::ProcessJobs resulted in exception error ", ex); + log.LogError(ex,"Generate::ProcessJobs result in exception error "); } //================================================================= diff --git a/server/AyaNova/util/ApplicationLogging.cs b/server/AyaNova/util/ApplicationLogging.cs index d37c0f14..31a9e6e3 100644 --- a/server/AyaNova/util/ApplicationLogging.cs +++ b/server/AyaNova/util/ApplicationLogging.cs @@ -1,5 +1,5 @@ using Microsoft.Extensions.Logging; - +using NLog.Extensions.Logging; namespace AyaNova.Util { @@ -8,9 +8,17 @@ namespace AyaNova.Util /// internal static class ApplicationLogging { - internal static ILoggerFactory LoggerFactory { get; set; }// = new LoggerFactory(); - internal static ILogger CreateLogger() => LoggerFactory.CreateLogger(); - internal static ILogger CreateLogger(string categoryName) => LoggerFactory.CreateLogger(categoryName); + internal static ILogger theLogger{get;set;} + internal static NLogLoggerProvider LoggerProvider { get; set; }// = new LoggerFactory(); + internal static ILogger CreateLogger() => LoggerProvider.CreateLogger(typeof(T).FullName); + //internal static ILogger CreateLogger(string categoryName) => theLogger; + internal static ILogger CreateLogger(string categoryName)=> LoggerProvider.CreateLogger(categoryName); + + + + // internal static ILoggerFactory LoggerFactory { get; set; }// = new LoggerFactory(); + // internal static ILogger CreateLogger() => LoggerFactory.CreateLogger(); + // internal static ILogger CreateLogger(string categoryName) => LoggerFactory.CreateLogger(categoryName); } } \ No newline at end of file