I added healthcheck to asp.net core application. When healthcheck is fail - return "Unhealthy", but I can't get any information why it failed. There is no logs - in console, in logger with file. How can i see the error, exception?
I simplified the code for .net6 webapplication
Programm.cs
using WebApi6HealthCheck;
using Microsoft.EntityFrameworkCore;
using Serilog;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((ctx, lc) => lc
.WriteTo.Console());
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddDbContext<OneProfileContext>(opt =>
opt.UseSqlServer("Data Source=fake-dbsql-S2016;Initial Catalog=S7OneProfile;User Id=kos_debugger;Password=SecretPassword;Connect Timeout=3;"));
builder.Services.AddHealthChecks()
.AddDbContextCheck<OneProfileContext>(tags: new[] { "live" });
var app = builder.Build();
app.UseHttpsRedirection();
app.MapControllers();
app.MapHealthChecks("/health/live", new HealthCheckOptions()
{
Predicate = (check) => check.Tags.Contains("live")
});
app.Run();
There is no datebase and healthcheck for dbcontext will fail.
In console I see this image
References
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="6.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
UPDATE
If i call dbContext from controller method i see exception in console - and this is right,good,fan. But I want see same exception when call HealthCheck - /health/live
WeatherForecastController.cs
using Microsoft.AspNetCore.Mvc;
namespace WebApi6HealthCheck.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
private readonly OneProfileContext _dbContext;
public WeatherForecastController(ILogger<WeatherForecastController> logger, OneProfileContext dbContext)
{
_logger = logger;
_dbContext = dbContext;
}
[HttpGet(Name = "GetWeatherForecast")]
public string Get()
{
var appEvent=_dbContext.ApplicationEvents.FirstOrDefault();
return "Hello,world";
}
}
}
GET https://localhost:7197/WeatherForecast
My suggestion is below: add logs on Console with .NET Core and Serilog
1.Install the Serilog, Serilog.AspNetCore, and Serilog.Extensions.Logging NuGet packages to integrate the basic functionalities of Serilog.
2.Download the Serilog.Sinks.Console and Serilog.Sinks.Async NuGet packages to use the Console as a destination of your logs.
3.Update the Program class to specify that the application must use Serilog.
Log.Logger = new LoggerConfiguration().CreateLogger();
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((hostingContext, loggerConfiguration) =>
loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration));
4.Use ILogger<T> instead of Serilog.ILogger
Since we have bound the Serilog logger to the one native on .NET - the one coming from Microsoft.Extensions.Logging - we can use the native logger everywhere in the project.
Add a dependency to ILogger<T> in your constructor, where T is the name of the class itself:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Getting random items. There are {AvailableItems} possible values");
_logger.LogWarning("This is a warning");
try
{
throw new ArgumentException();//you can modify codes to catch your health-check
}
catch (Exception ex)
{
_logger.LogError(ex, "And this is an error");
}
return View();
}
}
5.Define the settings in the appsettings.json file instead of directly in the code
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console" ],
"MinimumLevel": {
"Default": "Verbose",
"Override": {
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning",
"System": "Error"
}
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"formatter": "Serilog.Formatting.Compact.RenderedCompactJsonFormatter, Serilog.Formatting.Compact"
}
}
]
}
}
]
},
"AllowedHosts": "*"
}
Result:
I find a link,hoping it can help you.
Related
I have a .Net Core 5.0 project and I am trying to login to outlook application with this project. The purpose of the project is to get the calendar List, schedule work, etc. But when I try to login I get the following error. What is the reason?
My codes are below and I have ClientId and TenantId taken from Outlook account.
With my Localhost address given in the RedirectUrl part of the Outlook account.(http://localhost:5000)
Startup.cs
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.OpenApi.Models;
using System.Threading.Tasks;
namespace EvetOutlookAPI
{
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.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options => {
this.Configuration.GetSection("AzureAd").Bind(options);
options.Events.OnRedirectToIdentityProvider = context => {
if (context.HttpContext.Items.ContainsKey("allowRedirect"))
{
return Task.CompletedTask;
}
context.HandleResponse();
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
};
});
services.AddAuthorization(options => {
options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "EvetOutlookAPI", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "EvetOutlookAPI v1"));
}
app.UseCors(policyBuilder =>
policyBuilder.AllowCredentials().SetIsOriginAllowed(origin =>
true).AllowAnyHeader().WithExposedHeaders("Location"));
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
}
}
}
appsettings.json
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "https://dev.azure.com/",
"ClientId": "***********",
"TenantId": "*************",
"CallbackPath": "/signin-oidc"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Controller;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace EvetOutlookAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class LoginController : Controller
{
[HttpGet]
public ActionResult IsUserLoggedIn()
{
if (!this.HttpContext.User.Identity.IsAuthenticated)
{
return this.Unauthorized();
}
return this.Accepted();
}
[HttpGet("Authenticate")]
public async Task Login()
{
if (!this.HttpContext.User.Identity.IsAuthenticated)
{
this.HttpContext.Items.Add("allowRedirect", true);
await this.HttpContext.ChallengeAsync();
return;
}
this.HttpContext.Response.Redirect("http://localhost:5000");
}
}
}
Maybe the reason is cookies were not being set as secure.
By default, when the OIDC middleware middle generates its correlation cookie (and nonce) cookies, it sets the "SameSite" property to "None". Try using SameSiteMode.Lax.
Another way if you're using Chrome against localhost, you may have run into a change in Chrome cookie-handling behavior.
To verify, navigate to chrome://flags/ and change "Cookies without SameSite must be secure" to "Disabled".
Net core application. I am trying to log exceptions but this is not working as expected. Below is my configuration
Program.cs
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((hostContext, loggerConfiguration) =>
{
loggerConfiguration.ReadFrom.Configuration(hostContext.Configuration);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration["APPINSIGHTS_CONNECTIONSTRING"]);
services.AddApplicationInsightsTelemetry();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<ExceptionMiddleware>();
app.UseSerilogRequestLogging();
}
}
ExceptionMiddleware.cs
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
//private readonly ILogger _logger;
private readonly ILogger _logger = Serilog.Log.ForContext<ExceptionMiddleware>();
public ExceptionMiddleware(RequestDelegate next, ILogger logger)
{
_logger = logger;
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
// _logger.Error($"Something went wrong: {ex}");
_logger.Error(ex.Message, $"Something went wrong:");
await HandleExceptionAsync(httpContext, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var user = string.Empty;
if (context.User.Claims.Any())
user = context.User.Claims?.FirstOrDefault(cl => cl.Type.Contains("preferred_username"))?.Value ?? "Anonymous User";
context.Response.ContentType = "application/json";
context.Response.StatusCode = ConfigurateExceptionTypes(exception);
await context.Response.WriteAsync(new Models.ErrorDetails()
{
UserName = user,
StatusCode = context.Response.StatusCode,
Message = exception.Message
}.ToString());
}
private static int ConfigurateExceptionTypes(Exception exception)
{
int httpStatusCode;
switch (exception)
{
case var _ when exception is ValidationException:
httpStatusCode = (int)HttpStatusCode.BadRequest;
break;
default:
httpStatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
return httpStatusCode;
}
}
AppSettings.json
"Serilog": {
"Using": [],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "ApplicationInsights",
"Args": {
"instrumentationKey": "",
"restrictedToMinimumLevel": "Information",
"telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
}
}
],
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithProcessId",
"WithThreadId"
]
}
This is not logging exceptions as expected. I can see status code 500 in app insights but I want to see exception message logged as well. Can someone help me to understand what could be I am missing here. Any help would be appreciated. Thanks
Try adding this values in your appsettings.json:
"Serilog":
{
"Using":
["Serilog",
"Serilog.Sinks.ApplicationInsights",
"Serilog.Sinks.Console"],
...
}
Just try to configure Logger in Startup.cs
var log = new LoggerConfiguration()
.WriteTo
.ApplicationInsights(serviceProvider.GetRequiredService<TelemetryConfiguration>(), TelemetryConverter.Traces)
.CreateLogger();
Whether you choose Events or Traces, if the LogEvent contains any exceptions it will always be sent as ExceptionTelemetry.
In Application Insights you can configure whether the exceptions appear as Exceptions vs Traces
See here
I am running one asp.net core application version 3.1. Where i have to read connection string value in appsettings.json file. I found many examples related to it, But none of them works for me.
Below are the code used:
appsettings.json file i have provided the connection string value like below:
"ConnectionStrings": {
"ConnectionString1": "data source=192.xxx.x.xxx; database=MyDatabase; user id=myuser; password=mypass; Pooling=false; Connection Lifetime=10000;"
}
In Startup.cs file i have the code like below:
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.AddControllers();
services.AddSingleton<IConfiguration>(Configuration);
}
Now in the controller i have used code like below:
IConfiguration configure;
public MyAPIController(IConfiguration _config)
{
configure = _config;
}
public IActionResult GetSummary([FromBody] ReportParameters rp)
{
try
{
var connection = configure.GetValue<string>("ConnectionStrings:ConnectionString1");
var connection1 = configure.GetSection("ConnectionStrings").GetSection("ConnectionString1").Value;
var connection2 = configure["ConnectionStrings:ConnectionString1"];
var connection3 = configure.GetConnectionString("ConnectionString1");
return Ok(SomeValue);
}
catch (Exception ex)
{
return BadRequest(ex.ToString() + Environment.NewLine + "Error Path: " + Request.Path);
}
}
But none of the above code is working to get the connection string value.
Please suggest.
If you want to get ConnectionString in appsettings.js,you can use constructor to get Configuration,And use _configuration.GetSection("xxx").Value or _configuration["xxx"].
Code:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IConfiguration _configuration;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
public IActionResult Index()
{
string RedirectUrl = _configuration.GetSection("RedirectUrl").Value;
return View();
}
}
result:
There is no issue in my code.
There is issue in appsetitngs.json file. According to me it is not an issue.
I just change the connection string order and make it to up and after it i am getting the connection string value in application.
Below is the place where i changed the order:
{
"ConnectionStrings": {
"ConnectionString1": "data source=192.xxx.x.xxx; database=MyDatabase; user id=myuser; password=myuser; Pooling=false; Connection Lifetime=10000;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Previously i used this connectionString section just after Logging section.
This might help someone who is struggling for the same.
I have tried to implement Entity Framework Core to my Application I have some trouble.
Following error message occurs when I try to debug:
System.ArgumentException: "AddDbContext was called with configuration,
but the context type 'DatenbankKontext' only declares a parameterless
constructor. This means that the configuration passed to AddDbContext
will never be used. If configuration is passed to AddDbContext, then
'DatenbankKontext' should declare a constructor that accepts a
DbContextOptions and must pass it to the base
constructor for DbContext."
That's my DatabaseContext:
using Microsoft.EntityFrameworkCore;
namespace PlaudertischSoftware.Models
{
public class DatenbankKontext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(
#"Server=(localdb)\mssqllocaldb;Database=PlaudertischSoftwareDatenbankCore;Integrated Security=True");
}
public virtual DbSet<ObstSpielDaten> ObstSpielDaten { get; set; }
public virtual DbSet<AutoGaugeDaten> AutoGaugeDaten { get; set; }
}
}
That's my Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using PlaudertischSoftware.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.HttpOverrides;
using System.Net;
namespace PlaudertischSoftware
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddMvc(option => option.EnableEndpointRouting = false);
services.AddControllersWithViews()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
services.Configure<ForwardedHeadersOptions>(options =>
{
options.KnownProxies.Add(IPAddress.Parse("0.0.0.0"));
});
services.AddDbContext<DatenbankKontext>(options => {
options.UseSqlite(Configuration.GetConnectionString("PlaudertischSoftwareDatenbank"));
});
}
[System.Obsolete]
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseAuthentication();
}
}
}
And that's my appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"PlaudertischSoftwareDatenbank": "Server=(localdb)\\mssqllocaldb;Database=PlaudertischSoftwareDatenbank;Integrated Security=True"
}
}
The error is telling you that you need to add a constructor that takes options.
'DatenbankKontext' should declare a constructor that accepts a DbContextOptions and must pass it to the base constructor for DbContext.
So add one:
public class DatenbankKontext : DbContext
{
public DatenbankKontext(DbContextOptions options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(
#"Server=(localdb)\mssqllocaldb;Database=PlaudertischSoftwareDatenbankCore;Integrated Security=True");
}
public virtual DbSet<ObstSpielDaten> ObstSpielDaten { get; set; }
public virtual DbSet<AutoGaugeDaten> AutoGaugeDaten { get; set; }
}
In ASP.NET Core 3.1 a feature was added where unhandled exceptions can be passed onto an instance of ILogger as noted here:
Logging in .NET Core and ASP.NET Core
I have a Server side Blazor website where I want to be able to process these exceptions in a function where I can log them to a database or perhaps send an email. However I am unable to come up with code to do this based on the provided documentation. Could someone provide sample code to trap unhandled exceptions?
I recommand to use Serilog with sinks you need.
In your project add packages :
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Settings.Configuration
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.Debug
dotnet add package Serilog.Sinks.Seq
Serilog.AspNetCore to integrate Serilog with ASP.Net core.
Serilog.Settings.Configuration to read serilog config from .Net core configuration.
Serilog.Sinks.Console to write logs in the console.
Serilog.Sinks.Debug to write logs in Visual Studio output pane.
Serilog.Sinks.Seq to write logs in a Seq server, which is much more powerful than a DB.
Setup login in your Program.cs with :
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration))
.Build();
And in Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Admin/Error");
}
app.UseSerilogRequestLogging()
And configure your logs in appsettings.json with:
"Serilog": {
"LevelSwitches": {
"$controlSwitch": "Information"
},
"MinimumLevel": {
"ControlledBy": "$controlSwitch"
},
"WriteTo": [
{
"Name": "Seq",
"Args": {
"serverUrl": "http://localhost:5341/",
"controlLevelSwitch": "$controlSwitch",
"apiKey": "{SeqApiKey}"
}
},
{
"Name": "Console"
},
{
"Name": "Debug"
}
],
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithThreadId"
]
}
This tell Serilog to use the log level configure in Seq for your app.
"LevelSwitches": {
"$controlSwitch": "Information"
}
...
"Args": {
"serverUrl": "http://localhost:5341/",
"controlLevelSwitch": "$controlSwitch",
"apiKey": "{SeqApiKey}"
}
{SeqApiKey} is an Api key configure in your Seq server for your app.
If you want to use a DB, Serilog has long list of sinks you can use.
Doing some playing around, I found a way to do error logging without having to use a third party tool. Here is my code:
public class ExceptionLogger : ILogger
{
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return logLevel == LogLevel.Error;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (logLevel == LogLevel.Error)
{
while (exception.InnerException != null)
exception = exception.InnerException;
LogException(exception);
}
}
private void LogException(Exception error)
{
...
}
public sealed class ExceptionLoggerProvider : ILoggerProvider
{
public ILogger CreateLogger(string categoryName)
{
return new ExceptionLogger();
}
public void Dispose()
{
}
}
And in Startup.cs add this:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddProvider(new ExceptionLoggerProvider());
...
If you want to do it by yourself, on top of your middleware pipe add:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Admin/Error");
}
app.Use(async (context, next) =>
{
try
{
await next().ConfigureAwait(false);
}
catch(Exception e)
{
LogException(e);
throw;
}
})