What would be the equivalent code of web host builder for generic host builder for console application in 'Microsoft.Extensions.Hosting':
var hostBuilder = WebHost.CreateDefaultBuilder(args).UseConfiguration(config).UseStartup<TStartup>();
I couldn't find exactly analog. I use the next code for that. For my background services.
public static async Task Main(string[] args)
{
var host = new HostBuilder()
.ConfigureHostConfiguration(configHost => configHost.AddEnvironmentVariables("ASPNETCORE_"))
.ConfigureAppConfiguration((hostContext, configApp) =>
{
configApp.SetBasePath(GetConfigDirectoryPath());
configApp.AddJsonFile("appsettings.json", optional: false);
configApp.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", optional: true);
})
.ConfigureServices(ConfigureServices)
.UseHostedService<MyBackgroundService>()
.Build();
await host.RunAsync();
}
private static void ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options => options.UseSqlServer(hostBuilderContext.Configuration.GetConnectionString("Default")));
services.AddTransient<IDbContextProvider<AppDbContext>, DefaultDbContextProvider>();
services.AddTransient<IUnitOfWorkManager, UnitOfWorkManager>();
services.AddTransient(typeof(IUnitOfWork), typeof(EfCoreUnitOfWork));
services.AddTransient(typeof(IReadOnlyRepository<>), typeof(EfCoreReadOnlyRepositoryBase<>));
services.AddTransient(typeof(IReadOnlyRepository<,>), typeof(EfCoreReadOnlyRepositoryBase<,>));
services.AddTransient(typeof(IRepository<>), typeof(EfCoreRepositoryBase<>));
services.AddTransient(typeof(IRepository<,>), typeof(EfCoreRepositoryBase<,>));
services.AddDistributedRedisCache(options => options.Configuration = hostBuilderContext.Configuration.GetSection("Redis:Configuration").Value);
//Register AutoMapper profiles
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
services.AddAutoMapper(assemblies);
}
Is implementing background service
public class MyBackgroundService : BackgroundService
public static class Extensions
{
public static IHostBuilder UseHostedService<T>(this IHostBuilder hostBuilder)
where T : class, IHostedService, IDisposable
{
return hostBuilder.ConfigureServices(services =>
services.AddHostedService<T>());
}
}
Would have been nice if you could elaborate what exactly you are looking for but following is from my sample console application written in .net6. wrote this to show-case dependency injection and use of NLog with Microsoft.Extensions.Logging
Skip following and Get the source from Github
Program.cs
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using RedBeans.Examples.Console.DependencyInjectionLoggingExample;
Console.WriteLine("Hello, World!");
await Host.CreateDefaultBuilder(args)
.UseContentRoot(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
.ConfigureServices((context, services) =>
{
services.AddHostedService<MyApplication>()
.AddTransient<AnotherClass>()
.AddOptions<ConfigSection1Settings>().Bind(context.Configuration.GetSection("ConfigSection1"));
})
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
logging.AddConfiguration(context.Configuration.GetSection("NLog"));
logging.AddNLog();
})
.RunConsoleAsync();
MyApplication.cs
namespace RedBeans.Examples.Console.DependencyInjectionLoggingExample;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
internal class MyApplication : IHostedService
{
private int? _exitCode;
private readonly ILogger<MyApplication> _logger;
private readonly AnotherClass _another;
private readonly IHostApplicationLifetime _applicationLifetime;
public MyApplication(IHostApplicationLifetime applicationLifetime, AnotherClass another, ILogger<MyApplication> logger)
{
_applicationLifetime = applicationLifetime;
_another = another;
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation($"Starting Application with arguments: {string.Join(";", Environment.GetCommandLineArgs())}");
_applicationLifetime.ApplicationStarted.Register(() =>
{
Task.Run(async () =>
{
try
{
_logger.LogInformation("My Application Business logic starts here!!!");
_another.AnotherFunction();
await Task.Delay(1000, cancellationToken);
_exitCode = 0;
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled exception");
_exitCode = 1;
}
finally
{
_applicationLifetime.StopApplication();
}
});
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogDebug($"Exiting with return code: {_exitCode}");
Environment.ExitCode = _exitCode.GetValueOrDefault(-1);
return Task.CompletedTask;
}
}
AnotherClass.cs
namespace RedBeans.Examples.Console.DependencyInjectionLoggingExample;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
internal class AnotherClass
{
private readonly ILogger _logger;
private readonly IOptions<ConfigSection1Settings> _configSection1;
public AnotherClass(IOptions<ConfigSection1Settings> configSection1, ILogger<AnotherClass> logger)
{
_logger = logger;
_configSection1 = configSection1;
}
public void AnotherFunction()
{
_logger.LogInformation($"I can tell you the value of configSection1.Config1 is {_configSection1.Value.Config1}");
}
}
ConfigSection1Settings.cs
namespace RedBeans.Examples.Console.DependencyInjectionLoggingExample;
internal sealed class ConfigSection1Settings
{
public string? Config1 { get; set; }
}
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft.Hosting.Lifetime": "Warning",
"Microsoft.Extensions.Hosting.Internal.Host": "Warning"
}
},
"ConfigSection1": {
"Config1": "Value1"
},
"NLog": {
"throwConfigExceptions": true,
"targets": {
"async": true,
"logfile": {
"type": "File",
"fileName": "c:/temp/nlog-${shortdate}.log"
},
"logconsole": {
"type": "Console"
}
},
"rules": [
{
"logger": "Microsoft.*",
"maxLevel": "Error",
"final": true
},
{
"logger": "*",
"minLevel": "Info",
"writeTo": "logconsole"
},
{
"logger": "*",
"minLevel": "Debug",
"writeTo": "logfile"
}
]
}
}
Project File
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.1" />
</ItemGroup>
</Project>
Related
I'm trying to use serligo as my main logging utilitie but I can't seem to get it to work correctly.
In the Program.cs file, I configure the logger and also log a startup message, this message gets outputted to the console.
I also have, in one of my controllers, an ILogger<HomeController> instance that logs information when a certain action is called. These logs are not outputted to the console, and I cannot understand why.
Startup.cs
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
try
{
Log.Information("Starting up version {version}", Defaults.Version); // WORKS
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application start-up failed");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Controller
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Home/Index"); //Does not work
return View();
}
}
appsettings.json
"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"
}
}
]
}
}
]
},
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration))
The issue relates the above code, in this part of code, you should also configure it to write to the console.
Modify the code as below:
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration).WriteTo.Console())
Then, the output like this:
Searching I've not found the answer I'm looking for. I will only put code snippets here to help ask my question, supplying ALL of the code would not be executable without the entire system.
I'm using the built-in logging in C# to log to Windows Event Viewer and/or the Console. I also wanted to write to a file but not third-party logging, so I wrote my own simple logger that logs the same data to files and works.
My appsettings.json as a second for some configuration parameters like a working folder. I also have a folder path for the logger. What I would like to be able to do is use the same holder path from the setting and not have 2.
appsettings.json
{
"AppSettings": {
"WorkingFolderPath": "C:\\mypath\\",
"HeartbeatIntervalMinutes": 0, // This is seconds when in debug mode.
"FileRetryDelayMinutes": 1,
"PingTimeoutMilliseconds": 200
},
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Debug"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Debug"
}
},
"EventLog": {
"LogLevel": {
"Default": "Warning"
}
},
"FileLog": {
"Options": {
"FolderPath": "C:\\mypath\\",
"RetentionDays": 5
},
"LogLevel": {
"Default": "Debug"
}
}
}
}
As you see in this file I have a "WorkingFolderPath" setting I'd like to use that as my path in my "FileLog" logger and not have to specify a path there as well.
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
logging.AddConsole();
logging.AddDebug();
logging.AddEventLog(new EventLogSettings()
{
SourceName = "FreshIQAppMessagingService"
});
logging.AddFileLogger(options =>
{
context.Configuration.GetSection("Logging").GetSection("FileLog").GetSection("Options").Bind(options);
});
})
.ConfigureAppConfiguration((hostContext, config) =>
{
config
.SetBasePath(ApplicationPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureServices((hostContext, services) =>
{
services.Configure<Configuration>(hostContext.Configuration.GetSection("AppSettings"));
services.AddHostedService<Service1>();
services.AddHostedService<Service2>();
services.AddHostedService<Service3>();
services.AddHostedService<Service4>();
});
Here it's getting the options and passing them in. What I've not been able to figure out is how to change my classes so that I can send in the "WorkingFolderPath" from the top of the appsettings instead of the one with the options with the FileLog logger.
logging.AddFileLogger(options =>
{
context.Configuration.GetSection("Logging").GetSection("FileLog").GetSection("Options").Bind(options);
});
FileLoggerExtensions.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace FreshIQAppMessaging.Logging
{
public static class FileLoggerExtensions
{
public static ILoggingBuilder AddFileLogger(this ILoggingBuilder loggingBuilder, Action<FileLoggerOptions> configure)
{
loggingBuilder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
loggingBuilder.Services.Configure(configure);
return loggingBuilder;
}
}
}
FileLoggerProvider.cs
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.IO;
namespace FreshIQAppMessaging.Logging
{
[ProviderAlias("FileLog")]
public class FileLoggerProvider : ILoggerProvider
{
public readonly FileLoggerOptions Options;
public FileLoggerProvider(IOptions<FileLoggerOptions> options)
{
Options = options.Value;
if (!Directory.Exists(Options.FolderPath))
{
Directory.CreateDirectory(Options.FolderPath);
}
}
public ILogger CreateLogger(string categoryName)
{
return new FileLogger(this);
}
public void Dispose() { }
}
}
FileLogger.cs
using Microsoft.Extensions.Logging;
using System;
using System.Globalization;
using System.IO;
namespace FreshIQAppMessaging.Logging
{
public class FileLogger : ILogger
{
protected readonly FileLoggerProvider _fileLoggerProvider;
public FileLogger(FileLoggerProvider fileLoggerProvider)
{
_fileLoggerProvider = fileLoggerProvider;
}
public IDisposable? BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return logLevel != LogLevel.None;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
// Clean up old log files
var logFiles = Directory.GetFiles(_fileLoggerProvider.Options.FolderPath, "*-MyApp.log");
foreach (var logFilePath in logFiles)
{
var logFileName = new FileInfo(logFilePath).Name;
if (DateTime.TryParseExact(logFileName.Substring(0, 8), "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var logFileDate))
{
if (logFileDate.AddDays(_fileLoggerProvider.Options.RetentionDays) < DateTime.Now)
{
File.Delete(logFilePath);
}
}
}
var fullFilePath = $"{_fileLoggerProvider.Options.FolderPath}\\{DateTime.Now.ToString("yyyyMMdd")}-MyApp.log";
var logRecord = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} [{logLevel.ToString()}] {formatter(state, exception)} {(exception != null ? exception.StackTrace : "")}";
using (var streamWriter = new StreamWriter(fullFilePath, true))
{
streamWriter.WriteLine(logRecord);
}
}
}
}
FileLoggerOptions.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace FreshIQAppMessaging.Logging
{
public class FileLoggerOptions
{
public virtual string? FolderPath { get; set; }
public virtual int RetentionDays { get; set; } = 5;
}
}
You typically don't want to include custom options for your logger inside the Logging section. Most people do something like this instead:
{
"AppSettings": {
"WorkingFolderPath": "C:\\mypath\\",
...
},
"FileLog": {
"FolderPath": "C:\\mypath\\",
"RetentionDays": 5
},
"Logging": {
...
"FileLog": {
"LogLevel": {
"Default": "Debug"
}
}
}
}
You didn't share your options class but I assume you have a property on it named FolderPath. You could then configure your file logger like this:
logging.Services.Configure<FileLoggerOptions>(builder.Configuration.GetSection("FileLog"));
logging.AddFileLogger(options =>
{
options.FolderPath = builder.Configuration["AppSettings:WorkingFolderPath"];
});
You can now remove the FolderPath property from the FileLog object in the appsettings.json file since that value will be set using this line when adding the file logger:
options.FolderPath = builder.Configuration["AppSettings:WorkingFolderPath"];
The options would then look like this:
{
"AppSettings": {
"WorkingFolderPath": "C:\\mypath\\",
...
},
"FileLog": {
"RetentionDays": 5
},
"Logging": {
...
"FileLog": {
"LogLevel": {
"Default": "Debug"
}
}
}
}
I've written a similar logging provider in the Elmah.Io.Extensions.Logging repository and I copied some of the code from there. I tried mapping it to your original code in the question, but there might be something that doesn't match exactly. I hope that you still see the intent behind what I'm trying. You can look inside this repository for inspiration.
I have a small ASP.NET Core 3.1 project that should execute the Odata request and then log either info or exception into the Loggly. I'm using Serilog for this.
But for some reason, it only sends the very first message from the Program.cs only (Log.Debug($"DEBUG: Starting at:{DateTime.Now}");), but does not send anything other than that. While it works fine for the Console or File logging.
I have tried to fake an exception to trigger the Error logging. But nothing works (in the Loggly).
Any suggestions on why is that and how to make it work properly?
Here is my code:
Program.cs:
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try
{
Log.Debug($"DEBUG: Starting at:{DateTime.Now}");
CreateHostBuilder(args).Build().Run();
Log.Debug($"DEBUG: Host created");
}
catch (Exception ex)
{
Log.Error("Oops something failed!");
throw new Exception(ex.Message);
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog();
}
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.AddControllers();
services.AddDbContext<OdataTestObjectContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionStrings:Database"]);
});
services.AddControllers().AddOData(opt =>
opt.Filter().Expand().Select().OrderBy().Count().SetMaxTop(100)
.AddRouteComponents("odata", GetEdmModel()));
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "OdataTest", Version = "v1" });
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
});
var appSettings = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
var serilogConfig = new LoggerConfiguration().ReadFrom.Configuration(appSettings);
services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(serilogConfig.CreateLogger(), true));
services.AddMvc(opts => { opts.Filters.Add(new AutoLogAttribute()); });
}
// 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(c =>
{
c.RouteTemplate = "/swagger/{documentName}/swagger.json";
});
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "OdataTest v1"));
}
app.UseHttpsRedirection();
////Send "~/$odata" to debug routing if enable the following middleware
app.UseODataRouteDebug();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
var entitySet = builder.EntitySet<TestObject>("TestObjects");
entitySet.EntityType.HasKey(entity => entity.Id);
return builder.GetEdmModel();
}
}
Controller.cs
[ApiController]
[Route("OData")]
public class TestController : ODataController
{
private readonly OdataTestObjectContext _context;
private readonly ILogger<TestController> _logger;
public TestController(ILogger<TestController> logger, OdataTestObjectContext context)
{
_context = context;
_logger = logger;
}
[ApiExplorerSettings(IgnoreApi = true)]
[EnableQuery]
public IActionResult Get()
{
try
{
return Ok(_context.TestObjects);
}
catch (Exception ex)
{
_logger.LogError("Error happened for Default query! - {0}", ex.Message);
throw new InvalidOperationException(ex.Message);
}
}
[HttpGet("Top({top})")]
[EnableQuery]
public IEnumerable<TestObject> GetTop(int top)
{
return _context.TestObjects.OrderByDescending(x => x.Id).Take(top);
}
[HttpGet("Enumerable")]
[EnableQuery]
public IEnumerable<TestObject> Enumerable()
{
try
{
_logger.LogDebug("Executing ENUMERABLE now.");
//string a = "asd";
//int s = Convert.ToInt32(a);
var results = _context.SaleInvoicesAndProducts.AsEnumerable();
int z = 1;
if (results.Any())
z = 0;
int i = 10 / z;
return results;
}
catch (Exception ex)
{
_logger.LogError("Error happened for Enumerable query! - {0}", ex.Message);
throw new Exception();
}
}
AutoLogAttribute.cs (this just outputs logs with some request's parameters):
public class AutoLogAttribute : TypeFilterAttribute
{
public AutoLogAttribute() : base(typeof(AutoLogActionFilterImpl))
{
}
private class AutoLogActionFilterImpl : IActionFilter
{
private readonly ILogger<AutoLogAttribute> _logger;
public AutoLogActionFilterImpl(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<AutoLogAttribute>();
}
public void OnActionExecuting(ActionExecutingContext context)
{
// perform some business logic work
_logger.LogInformation($"queryString: {context.HttpContext.Request.QueryString}");
_logger.LogDebug($"queryString (Debug): {context.HttpContext.Request.QueryString}");
}
public void OnActionExecuted(ActionExecutedContext context)
{
//TODO: log body content and response as well
_logger.LogInformation($"host: {context.HttpContext.Request.Host}");
_logger.LogInformation($"patheAndQuery: {context.HttpContext.Request.GetEncodedPathAndQuery()}");
_logger.LogDebug($"path (Debug): {context.HttpContext.Request.Path}");
_logger.LogDebug($"host (Debug): {context.HttpContext.Request.Host}");
_logger.LogDebug($"patheAndQuery (Debug): {context.HttpContext.Request.GetEncodedPathAndQuery()}");
}
}
}
appsetting.json
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Sinks.Loggly" ],
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Debug",
"System": "Debug"
}
},
"Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
"WriteTo": [
{ "Name": "Console" },
{
"Name": "File",
"Args": { "path": "C:\\ODATA\\log.json" }
},
{
"Name": "Loggly",
"Args": {
"customerToken": "mytoken",
"tags": "mytest"
}
}
],
},
"ConnectionStrings": {
"Database": "Server=(localdb)\\MSSQLLocalDB;Initial Catalog=MyTestDB;Integrated Security=true;MultiSubnetFailover=True;MultipleActiveResultSets=True;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
You can try to clear providers:
Host
****
.ConfigureLogging(logging =>
{
logging.ClearProviders();
})
.UseSerilog();
Also, if you have configured logging on Host, you don't need to put the additional configuration into Startup.cs
I am making .Net core application and use Serilog for logging. Now I want to use Serilog to write a log for each clients who already logged in.
I expected the application gonna have seperate logger
Global logger: write server log
Client loggers: each client gonna have their own logger to write log file.
From what I tried to do the application do generate the txt file. However, it doesn't write anything to it.
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseIISIntegration()
.UseSerilog((hostingcontext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingcontext.Configuration))
.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<LoggerManager>();
}
...MORE CODE...
}
LoggerManager.cs
public class LoggerManager
{
public static Dictionary<string, ILogger> loggerDict = new Dictionary<string, ILogger>();
public void CreateUserLogger(string username, EnumType userType)
{
var logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.Async(a => a.File(#$"Log/{userType}/{username}.txt",
buffered:true,
rollingInterval:RollingInterval.Day,
retainedFileCountLimit:90))
.CreateLogger();
loggerDict.TryAdd(cheID, logger);
}
public void WriteInfoLog (string username, string message)
{
loggerDict.TryGetValue(username, out ILogger logger);
logger.Information(message);
}
}
HomeController.cs
class HomeController {
private readonly LoggerManager _loggerManager;
public HomeController(LoggerManager loggerManager)
{
_loggerManager = loggerManager;
}
public IActionResult Index() {
_loggerManager.WriteInfoLog(authenticatedUser.CHEID, "HELLO HOW ARE U TODAY");
return View();
}
}
appsettings.json
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"System": "Warning",
"Microsoft": "Warning"
}
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"path": "Logs/Server/serverlog.txt",
"rollingInterval": "Day",
"retainedFileCountLimit": 7,
"buffered": true
}
}
]
}
}
]
}
I randomly found the "sinks-map". This sinks definitely satisfy my requirement.
https://github.com/serilog/serilog-sinks-map
I'm having trouble getting the logger to work like i want it to. I've set the loglevel to warning, but the console window is still bloated with info logs.
I've provided some examples below, nothing extra is configured in Startup.cs or Program.cs.
I'm happy to provide more information if needed.
appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "ConnectionString"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning"
}
}
}
Logging example:
public class DishRepository : IDishRepository
{
private readonly ApplicationDbContext _context;
private readonly ILogger<DishRepository> _logger;
public DishRepository(ApplicationDbContext context, ILogger<DishRepository> logger)
{
_context = context;
_logger = logger;
}
public IEnumerable<Dish> GetAll()
{
try
{
_logger.LogInformation("GetAll was called");
return _context.Dishes
.Include(d => d.Category)
.Include(d => d.DishIngredients)
.ThenInclude(di => di.Ingredient)
.Include(d => d.PizzaType).ToList();
}
catch (Exception e)
{
_logger.LogError($"Failed to get all dishes: {e}");
return Enumerable.Empty<Dish>();
}
}
}
When i run my program via VisualStudio i get this:
--------This Works--------
I found the example below at https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?tabs=aspnetcore2x it works, but I don't understand why this works and not the appsettings.json example above.
appsettings.json
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"PizzeriaAngular": "Warning",
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
Program.cs still looks like this:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
There are two config files appsettings.json and appsettings.Development.json. And system use it in development mode.
This code work for me (NetCore 2.x)
in class Startup.cs in method ConfigureServices(IServiceCollection services)
services.AddLogging(builder =>
{
builder.SetMinimumLevel(LogLevel.Trace);
builder.AddFilter("Microsoft", LogLevel.Warning);
builder.AddFilter("System", LogLevel.Error);
builder.AddFilter("Engine", LogLevel.Warning);
});
If you are using another logging provider, like NLog, then the "Logging" appsettings for Microsoft will not work.
For NLog, you have to set the minimum logging level in the nlog.config:
<logger name="*" minlevel="Info" writeTo="default" />