Log error using serilog when host. crashes .net 6 top level statements program.cs - serilog

We are using serilog in .net 6 console application. Below code is sample program.cs. When host crashes how do we capture the exception in the configured logger?
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
IHost host = Host.CreateDefaultBuilder(args)
.UseSerilog((ctx, lc) => lc
.ReadFrom.Configuration(ctx.Configuration))
.ConfigureServices((context, services) =>
{
services.AddHostedService<Worker>();
})
.Build();
try
{
await host.RunAsync();
}
catch(Exception ex)
{
// Need to log the error
//
throw;
}

Related

Trying to implement Microsoft.AspNetCore.SystemWebAdapters between a ASP.NET MVC and ASP.NET Core MVC site

We are trying to use the Microsoft.AspNetCore.SystemWebAdapters to implement a incremental move from a our existing ASP.NET MVC site to a ASP.NET Core MVC site. https://devblogs.microsoft.com/dotnet/incremental-asp-net-to-asp-net-core-migration/
After following the various guides and example projects supplied by MS I am hitting an error generated by a YARP protocol method which is called by the following method in the SystemWebAdapters:
public Task InvokeAsync(HttpContext context)
=> context.GetEndpoint()?.Metadata.GetMetadata() is { IsDisabled: false } ? SetUserAsync(context) : _next(context);
The follow exception is raised:
ArgumentException: Invalid destination prefix. (Parameter 'destinationPrefix')
Yarp.ReverseProxy.Forwarder.HttpForwarder.CreateRequestMessageAsync(HttpContext context,
string destinationPrefix, HttpTransformer transformer, ForwarderRequestConfig requestConfig, bool isStreamingRequest, ActivityCancellationTokenSource activityToken)
Yarp.ReverseProxy.Forwarder.HttpForwarder.SendAsync(HttpContext context, string destinationPrefix, HttpMessageInvoker httpClient, ForwarderRequestConfig requestConfig, HttpTransformer transformer)
Yarp.ReverseProxy.Forwarder.ForwarderMiddleware.Invoke(HttpContext context)
Yarp.ReverseProxy.Health.PassiveHealthCheckMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Code
This is the code in the program.cs in the Core site, I'm not using SSL at the moment:
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.SystemWebAdapters;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
// These must match the data protection settings in MvcApp Startup.Auth.cs for cookie sharing to work
var sharedApplicationName = "CCM";
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine(Path.GetTempPath(), "sharedkeys", sharedApplicationName)))
.SetApplicationName(sharedApplicationName);
builder.Services.AddAuthentication()
.AddCookie("SharedCookie", options => options.Cookie.Name = ".AspNet.ApplicationCookie");
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddSystemWebAdapters()
.AddRemoteAppClient(remote => remote
.Configure(options =>
{
options.RemoteAppUrl = new(builder.Configuration["ReverseProxyAddress"]);
options.ApiKey = "test-key";
})
.AddAuthentication(true)
.AddSession())
.AddJsonSessionSerializer(options => options.KnownKeys.Add("Example", typeof(Example)));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
//app.UseExceptionHandler("/Home/Error");
//app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSystemWebAdapters();
app.MapGet("/current-principals-with-metadata", (HttpContext ctx) =>
{
var user1 = Thread.CurrentPrincipal;
var user2 = ClaimsPrincipal.Current;
return "done";
}).WithMetadata(new SetThreadCurrentPrincipalAttribute(), new SingleThreadedRequestAttribute());
app.MapGet("/current-principals-no-metadata", (HttpContext ctx) =>
{
var user1 = Thread.CurrentPrincipal;
var user2 = ClaimsPrincipal.Current;
return "done";
});
app.UseEndpoints(endpoints =>
{
app.MapDefaultControllerRoute();
// This method can be used to enable session (or read-only session) on all controllers
//.RequireSystemWebAdapterSession();
app.MapReverseProxy();
});
app.Run();
This is the section from the global.asax.cs:
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
ViewEngines.Engines.Insert(0, new BillingViewEngine());
ViewEngines.Engines.Remove(ViewEngines.Engines.OfType<WebFormViewEngine>().FirstOrDefault());
LocalisationConfig.RegisterAdapters();
SystemWebAdapterConfiguration.AddSystemWebAdapters(this)
.AddProxySupport(options => options.UseForwardedHeaders = true)
.AddJsonSessionSerializer(options => options.KnownKeys.Add("Example", typeof(Example)))
.AddRemoteAppServer(remote => remote
.Configure(options => options.ApiKey = "test-key")
.AddAuthentication()
.AddSession());
Any pointers would be useful as I've hit a bit of a wall with it.

Run Nopcommerce on console application

I want to use NopCommerce libraries for a console project (for migrate data transfer).
I create a console project and add the base libraries (Nop.Core, Nop.Data, Nop.Service, Nop.Web.Freamework) to it, I also copy the App_Data folder.
I also use Class Startup.cs to run the project
namespace Nop.Console
{
class Program
{
static void Main(string[] args)
{
using var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder => webBuilder
.ConfigureAppConfiguration(config =>
{
config
.AddJsonFile(NopConfigurationDefaults.AppSettingsFilePath, true, true)
.AddEnvironmentVariables();
})
.UseStartup<Startup>())
.Build();
var engine = EngineContext.Create();
var cs = engine.Resolve<ICategoryService>();
System.Console.WriteLine("Hello World!");
}
}
}
But engine.Resolve(); return null;
Any idea?
I'm not 100% sure if this is the best solution, but at least works for me for NopCommerce 4.40.4. The code in my Console application is following:
/// <summary>
/// EngineContextInitializer
/// </summary>
public static class EngineContextInitializer
{
/// <summary>
/// Runs this instance.
/// </summary>
public static async Task<IHost> RunAsync()
{
string[] args = Array.Empty<string>();
var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder => webBuilder
.ConfigureAppConfiguration(config =>
{
config
.AddJsonFile(NopConfigurationDefaults.AppSettingsFilePath, true, true)
.AddEnvironmentVariables();
})
.UseStartup<Startup>())
.Build();
//start the program, a task will be completed when the host starts
await host.StartAsync();
return host;
}
}
The usage of EngineContextInitializer is following:
private async Task GetProductById(int id)
{
using var host = await EngineContextInitializer.RunAsync();
IEngine engine = EngineContext.Current;
var productService = engine.Resolve<IProductService>();
var product = await productService.GetProductByIdAsync(id);
await host.StopAsync();
}
So what does make the difference between your and mine code?
I'm running the code:
await host.StartAsync();
Most importantly I'm disposing IHost after I got the product from IProductService.
On the line is the difference:
var host = Host.CreateDefaultBuilder(args)
And 1 more thing, you don't need to call EngineContext.Create(), because if you check the class and Current getter you will see the following code:
/// <summary>
/// Gets the singleton Nop engine used to access Nop services.
/// </summary>
public static IEngine Current
{
get
{
if (Singleton<IEngine>.Instance == null)
{
Create();
}
return Singleton<IEngine>.Instance;
}
}
Please don't forget to dispose IHost as it implements IDisposable to avoid memory leaks.
Disclaimer: I'm not part of NopCommerce dev team, usage is at your own risk. Please follow official NopCommerce documentation or response on the topic on their website (documentation, forum or GitHub).

How to inject and use Serilog (Ilogger) when web-api startup

I'm developing a .NET core 3.1 Console application (web-api).
I use a Serilog service (it is basically using the Microsoft.Extensions.Logging).
The Serilog is injected and can be used in the FW Controllers methods.
Now - I need something a little bit different - Whenever the system is starting up (after being down) I need to make an http post request - you can see it when executing the ConnectionInitiator.Initiate(), in the startup method. In that same scope (method\class) - I need to use the logger, in order to log some data. Now - If the request would be through the controller - the logger, would be available (by the DI).
To make a long story short - I need somehow to inject the Ilogger to the class or to make it available in some other way. I've tried use the logger in the startUp, but this seems to be impossible (since .net core 3.0 - if I understand correctly)
See my code:
Program.cs:
public class Program
{
public static void Main(string[] args)
{
var loggerConfig = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
//Reading the appconfig.json
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(loggerConfig).CreateLogger();
try
{
Log.Information("System Started up");
CreateWebHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "THE APPLICATION FAILED TO START UP");
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return WebHost.CreateDefaultBuilder(args).ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
}).UseSerilog().UseStartup<Startup>();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
StartUp.cs
public class Startup
{
public Startup(IConfiguration configuration/*, Microsoft.Extensions.Logging.ILogger logger*/)
{
Configuration = configuration;
ConnectionInitiator.Initiate(configuration/*, logger*/);
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddControllers();
services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().SetIsOriginAllowed((host) => true).AllowCredentials());
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
ConnectionInitiator.cs:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace AAA.BL
{
public static class ConnectionInitiator
{
private static readonly object threadlock = new object();
private static MyConfig myConfig;
private static ILogger ilogger;
/*
public ConnectionInitiator(ILogger _logger)
{
ilogger = _logger;
}
*/
public static void/*async Task*/ Initiate(IConfiguration configuration/*, ILogger ilogger*/)
{
HttpRequester httpRequester = new HttpRequester();
if (myConfig == null)
{
myConfig = new myConfig(configuration);
}
IssueOTPResponse response = /*await*/ httpRequester.PostSomething(myConfig, ilogger).Result; //Double check thread safe singleton implementation
if (response.ststuacode != 200)
{
ilogger.logcritical($"critical error when initiate connection (connectioninitiator): {response.statusdescription}");
}
}
}
}
It seems like the answer is much simpler that I expected - By using the Serilog and was added as a service in the Configure method - It can be reached globally (in every place of the namepsace) by using the static class Log and its static method Logger, for example:
Log.Logger.Information("XXXXX");

MassTransit v6 UseSendFilter

public void ConfigureServices(IServiceCollection services)
{
//other configuration
services.AddScoped<IMyDependency, MyDependency>(); //register dependency
services.AddScoped(typeof(MySendFilter<>)); //register generic filter
services.AddMassTransit(x =>
{
x.UsingRabbitMq((context, cfg) =>
{
cfg.UseSendFilter(typeof(MySendFilter<>), context); //generic filter
});
});
}
https://masstransit-project.com/advanced/middleware/scoped.html#usage
I am trying to execute the code above. I created a .net-core 3.1 app and installed MassTransit, MassTransit.AspNetCore, MassTransit.Extensions.DependencyInjection, and MassTransit.RabbitMQ (v6.3.2). However, neither UseSendFilter nor UsingRabbitMq methods could be found.

ASP.NET Core 2.0 Error starting Kestrel in Kubernetes

Created a new ASP.NET Core 2.0 project and it runs fine locally.
Then after running it in a Docker container locally it also works fine. But when I try to use the Docker image in a Kubernetes pod, it will run for a couple minutes and then give me this:
Unhandled Exception: System.InvalidOperationException: A path base can
only be configured using IApplicationBuilder.UsePathBase().
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.
<BindAddressAsync>d__7.MoveNext()
Here is my Program.cs:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true,
ReactHotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
When this error was happening, we were using the FROM microsoft/aspnetcore-build:1.1 base image as our build and runtime. At the time we were encountering the error, we had simply tried to upgrade to FROM microsoft/aspnetcore-build:2.0. I'm not certain what specifically the issue with this image was, but Kubernetes didn't like it.
At a later date, we switched the dockerfile to multistage; building with FROM microsoft/aspnetcore-build:1.1 and running with FROM microsoft/dotnet:1.1-runtime, and when we upgraded that to the corresponding 2.0 versions, we didn't encounter this error again.

Resources