I'm currently working on console application that should be able to log to both console and database using Serilog and built-in Dependency injection to pass Ilogger via constructor to other services. Im running the latest Net Core.
Now my problem with the code below is that in RobotService I'm trying to log all the levels of logs, but I can only see the logs in console. The autocreated table in database called "Logs" is empty.
I'm not quite sure what am I doing wrong. Have anybody dealt with somethins similar?
Thanks a lot for any input.
public class Program
{
static void Main(string[] args)
{
var host = AppStartup();
var robotService = ActivatorUtilities.CreateInstance<RobotService>(host.Services);
robotService.Run();
static IConfigurationRoot ConfigSetup()
{
return new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
}
static IHost AppStartup()
{
var appSettings = ConfigSetup();
var host = Host.CreateDefaultBuilder()
.UseSerilog()
.ConfigureServices((context, services) =>
{
services.AddTransient<IRobotService, RobotService>();
services.AddTransient<IStrategyService, StrategyService>();
})
.Build();
Serilog.Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.MSSqlServer(
connectionString: appSettings.GetConnectionString("DevConnection"),
sinkOptions: new MSSqlServerSinkOptions
{
TableName = "Logs",
AutoCreateSqlTable = true
},
columnOptions: new ColumnOptions()
).CreateLogger();
return host;
}
}
}
Related
I have written custom ConfigurationProvider so that the application ( .net Core 6 ) is able to read configuration from the database. This works as expected when running from Visual Studio or running the output .exe file from command line, however when I try to run it as a Windows Service the config from database is not added. See code below with the appropriate comments
private static IHost CreateHost(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureHostConfiguration(configBuilder =>
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json", optional: true, reloadOnChange: true);
IConfigurationRoot configuration = builder.Build();
var dbConfig = configuration.GetSection("DatabaseConfig");
// Get connection string from appSettings.json and add SqlConfigurationProvider
configBuilder.AddSqlDatabase(config =>
{
config.ConnectionString = dbConfig["ConnectionString"];
config.RefreshInterval = TimeSpan.FromMinutes(1);
});
})
.ConfigureServices((host, services) =>
{
var myConfigSection = host.Configuration.GetSection("MyConfigSection");
var myConfigValue = myConfigSection["MyConfig"];
// When running from VS or CMD myConfigValue is properly taken from the database
// When running from Windows Service myConfigValue will be null suggesting SqlConfigurationProvider has not been added
if (string.IsNullOrEmpty(myConfigValue))
throw new ArgumentException("Config is missing");
services.AddHostedService<WorkerProcess>();
})
.UseWindowsService(options =>
{
options.ServiceName = "MyService";
})
.Build();
}
I suppose that when we call UseWindowsService the host configuration is ignored and that's the reason but that's just the assumption.
What is the proper way of registering Custom Configuration Provider when running the app as Windows Service ?
Edit
I have tried using ConfigureAppConfiguration instead of ConfigureHostConfiguration but the result is unfortunately the same
I have a Blazor wasm .net 6 hosted project.
I have configured serilog to log to file and mssqlserver. I works fine for the log from serilog.aspnetcore but when I try Log.Debug(...) in my api controller nothing is logged to the file or sql server.
Program.cs in Blazor.Server
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
Serilog.Events.LogEventLevel logEventLevelFile = (Serilog.Events.LogEventLevel)Enum.Parse(typeof(Serilog.Events.LogEventLevel), configuration.GetSection("Serilog:MinimumLevelFile").Value);
Serilog.Events.LogEventLevel logEventLevelDB = (Serilog.Events.LogEventLevel)Enum.Parse(typeof(Serilog.Events.LogEventLevel), configuration.GetSection("Serilog:MinimumLevelDB").Value);
Log.Logger = new LoggerConfiguration()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Month, restrictedToMinimumLevel: logEventLevelFile)
.WriteTo.MSSqlServer(connectionString: configuration.GetSection("Serilog:ConnectionStrings:LogDatabase").Value,
restrictedToMinimumLevel: logEventLevelDB,
sinkOptions: new MSSqlServerSinkOptions
{
TableName = configuration.GetSection("Serilog:TableName").Value,
SchemaName = configuration.GetSection("Serilog:SchemaName").Value
},
appConfiguration: configuration
, columnOptionsSection: configuration.GetSection("Serilog:ColumnOptions"))
.CreateBootstrapLogger();
Serilog.Debugging.SelfLog.Enable(msg =>
{
Debug.Print(msg);
Debugger.Break();
});
try
{
Log.Information("Application starting up.");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "The application failed to start up correctly.");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
var builder = Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).UseSerilog();
return builder;
}
}
appsettings.json
"Serilog": {
"Using": [ "Serilog.Sinks.MSSqlServer" ],
"MinimumLevelFile": "Debug",
"MinimumLevelDB": "Debug",
"ConnectionStrings": {
"LogDatabase": "...valid connection string ..."
},
"SchemaName": "dbo",
"TableName": "Logs"
},
UserController.cs
public UserController(){}
// GET: api/user
[AllowAnonymous]
[HttpGet]
public IActionResult Get(string empId)
{
Log.Debug($"{HttpContext.Request.Path} => Loading current user from Query String: {empId}");
....
I have a very similar project in .net 6 (Angular instead of Blazor wasm) and everything works fine
The user controller is the same.
I have tried to move the configuration around, like moving it in the .UseSerilog() of CreateHostBuilder but not luck.
Why is serilog not logging what is in my api controller?
Looks like the default logging level of 'Information' was applied even if I was setting the level for each sinks.
Log.Logger = new LoggerConfiguration()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Month, restrictedToMinimumLevel: logEventLevelFile)
.WriteTo.MSSqlServer(connectionString: configuration.GetSection("Serilog:ConnectionStrings:LogDatabase").Value,
restrictedToMinimumLevel: logEventLevelDB,
sinkOptions: new MSSqlServerSinkOptions
{
TableName = configuration.GetSection("Serilog:TableName").Value,
SchemaName = configuration.GetSection("Serilog:SchemaName").Value
},
appConfiguration: configuration
, columnOptionsSection: configuration.GetSection("Serilog:ColumnOptions"))
.CreateBootstrapLogger();
Forcing the minimum level to Verbose fixed the issue
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Month, restrictedToMinimumLevel: logEventLevelFile)
.WriteTo.MSSqlServer(connectionString: configuration.GetSection("Serilog:ConnectionStrings:LogDatabase").Value,
restrictedToMinimumLevel: logEventLevelDB,
sinkOptions: new MSSqlServerSinkOptions
{
TableName = configuration.GetSection("Serilog:TableName").Value,
SchemaName = configuration.GetSection("Serilog:SchemaName").Value
},
appConfiguration: configuration
, columnOptionsSection: configuration.GetSection("Serilog:ColumnOptions"))
.CreateBootstrapLogger();
I am working on .NET Core 5 Console App and I was trying load appsettings.[environment].json at runtime based on the "ASPNETCORE_ENVIRONMENT = Development" which I setup in debug under project properties.
In the BuildConfig method I can see that
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
is returning the "Development" text correctly and also loading the 2 file (appsettings.json, appsettings.development.json).
but when I pass the config to different class via constructor and then inspecting config in that class I seeing the (appsettings.json, appsettings.production.json) file not the development file why?
I don't have appsettings.production.json file in my project yet.
static void Main(string[] args)
{
try
{
//setting for SeriLog
var builder = new ConfigurationBuilder();
BuildConfig(builder);
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.ReadFrom.Configuration(builder.Build())
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File(#"Log\Log.txt")
.CreateLogger();
Log.Logger.Information("Application Starting");
var host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.AddTransient<IRIUProcess, RIUProcess>();
})
.UseSerilog()
.Build();
var svc = ActivatorUtilities.CreateInstance<RIUProcess>(host.Services);
svc.Run(args);
}
catch (Exception ex)
{
Log.Logger.Error(ex.ToString());
}
}
static void BuildConfig(IConfigurationBuilder builder)
{
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
}
Configuration builder return self instance configurated like entityframework core
//setting for SeriLog
var builder = new ConfigurationBuilder();
BuildConfig(builder);
should be
//setting for SeriLog
var builder = new ConfigurationBuilder();
builder = BuildConfig(builder);
Without that, your configuration builder remain unchanged.
Adapt also the BuildConfig method for that.
Then you can remove the CreateDefaultBuilder (with use default AspNetCore Configuration) and directly use your instance of configuration :
new HostBuilder().ConfigureHostConfiguration(a => a.AddConfiguration(builder.Build()))
https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.hostbuilder.configurehostconfiguration?view=dotnet-plat-ext-5.0
You will have then your host with the same configuration for SeriLog and your host
I managed to implement logging into Elasticsearch in localhost. However It logs any kind of activity from ASP.net Core. A simple program start logs around 20 entries. How could I log only my log entries I place into the code. Would need all the severities, so change min log level is not the solution.
private static void ConfigureLogging()
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile(
$"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json",
optional: true)
.Build();
Log.Logger = new LoggerConfiguration()
.WriteTo.Elasticsearch(ConfigureElasticSink(configuration, environment))
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
private static ElasticsearchSinkOptions ConfigureElasticSink(IConfigurationRoot configuration, string environment)
{
return new ElasticsearchSinkOptions(new Uri(configuration["ElasticConfiguration:Uri"]))
{
AutoRegisterTemplate = true,
IndexFormat = $"{Assembly.GetExecutingAssembly().GetName().Name.ToLower().Replace(".", "-")}-{environment?.ToLower().Replace(".", "-")}-{DateTime.UtcNow:yyyy-MM}"
};
}
Loggly has the similar solutions I would need: https://github.com/neutmute/loggly-csharp
You can override the logging level of libraries outside of your code. This can be done in code or in your serilog config settings in your appsettings file. Here is an article that explains how. https://nblumhardt.com/2019/10/serilog-in-aspnetcore-3/
I have an ASP.NET Core 2.1 REST API app. I want to be able to run it as a windows service as well as on IIS so I have created a startup project for each and they use a shared Core library for the "guts" of the app. I am using CORS which I think may be coming into play.
The Service project runs normally under dotnet.exe from visual studio and the IIS project runs using IIS Express from visual studio. Their startup code differs very little and is at the end of this post.
Like most rest API's, I want to rely on Http status codes to convey information. On one endpoint, I want to use the RequestSizeLimitAttribute to return a 413 status if the request exceeds a certain limit.
The windows service project behaves correctly, returning the correct status codes to the client. But when I run as IIS Express, the client first sends an OPTIONS request and gets a 204 back (as expected), but then the response after that is always a 502-Bad Gateway.
Does IIS mess with things if an exception happens because of the RequestSizeLimitAttribute?
//IIS Express Project Startup Code
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IConfiguration config => new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile("appsettings.Development.json", optional: true)
.Build();
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
AND
//Windows Service startup code
public static IConfiguration config => new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile("appsettings.Development.json", optional: true)
.Build();
public static void Main(string[] args)
{
RunWebHost(args, logger);
}
public static void RunWebHost(string[] args)
{
var isService = true;
if (Debugger.IsAttached || args.Contains("--console"))
{
isService = false;
}
var pathToContentRoot = Directory.GetCurrentDirectory();
if (isService)
{
var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
pathToContentRoot = Path.GetDirectoryName(pathToExe);
}
var webHostArgs = args.Where(arg => arg != "--console").ToArray();
var host = WebHost.CreateDefaultBuilder(webHostArgs)
.UseContentRoot(pathToContentRoot)
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
if (isService)
{
host.RunAsService();
}
else
{
host.Run();
}
}