I am following this tutorial: https://andrewlock.net/using-dependency-injection-in-a-net-core-console-application/
and accordingly installed the packages but log is not getting printed anywhere.
This is my code:
var serviceProvider = new ServiceCollection()
.AddLogging()
.AddTransient<IFoo, Foo>(s =>
{
return new Foo()})
.BuildServiceProvider();
//configure console logging
serviceProvider
.GetService<ILoggerFactory>()
.AddConsole(LogLevel.Debug);
var logger = serviceProvider.GetService<ILoggerFactory>().CreateLogger<Program>();
logger.LogError("Starting application");
Turns out the console logging provider doesn't immediately flush the messages to the console like it did in the net-core-1.x versions. It appears to run on a different thread. See this web page for info: https://github.com/aspnet/Logging/issues/631
You can add at the end of the Main function.
serviceProvider.Dispose();
or you can add .AddDebug()
serviceProvider
.GetService<ILoggerFactory>()
.AddConsole(LogLevel.Debug)
.AddDebug();
Creating a new ServiceProvider and HostBuilder may not be worth it if we just want a Logging in Console Application because it's a bit of extra caution to clean it up or dispose of.
Rather, I would suggest just have Logging Factory to use logger and that will solve the logging if that is only what we want.
public static class ApplicationLogging
{
public static ILoggerFactory LogFactory { get; } = LoggerFactory.Create(builder =>
builder.ClearProviders();
// Clear Microsoft's default providers (like eventlogs and others)
builder.AddSimpleConsole(options =>
{
options.IncludeScopes = true;
options.SingleLine = true;
options.TimestampFormat = "hh:mm:ss ";
});
builder.AddApplicationInsights("instrument-key");
});
public static ILogger<T> CreateLogger<T>() => LogFactory.CreateLogger<T>();
}
static void Main(string[] args)
{
var logger = ApplicationLogging.CreateLogger<Program>();
logger.LogInformation("Let's do some work");
logger.LogWarning("I am going Crazy now!!!");
logger.LogInformation("Seems like we are finished our work!");
Console.ReadLine();
}
I landed on this thread trying to troubleshoot why console logging didn't work and this answer documents what I found.
Packages used:
Microsoft.Extensions.Logging
Microsoft.Extensions.Logging.Console
Microsoft.Extensions.Logging.Debug
Application:
.NET Core 2.2 Console (Microsoft.NET.Sdk, netcoreapp2.2)
Using Microsoft.Extensions.Hosting.IHost, this is how I added console logging:
var hostBuilder = new HostBuilder()
// Other Configuration omitted for brevity
.ConfigureLogging((hostBuilderContext, loggingBuilder) =>
{
loggingBuilder.AddConfiguration(hostBuilderContext.Configuration.GetSection("Logging"));
loggingBuilder.AddConsole(options =>
{
options.IncludeScopes = true;
});
loggingBuilder.AddDebug();
});
// Start the application
await hostBuilder.RunConsoleAsync();
Interestingly, if I remove the options parameter in the call to AddConsole, I do not see any logging. I believe this is so because I use an ILogger in my code that emits log statements:
public class ClassThatLogs
{
private readonly ILogger<ClassThatLogs> _logger;
public ClassThatLogs(ILogger<ClassThatLogs> logger)
{
_logger = logger;
}
public void DoWork()
{
_logger.LogInformation("Working");
}
}
If I publish the console app as:
single file _log.LogDebug provide no output
when I publish it as not a single file (so I only uncheck the single file option) _log.LogDebug provides outputs on the console all my debugging statements. No other changes than unchecking single file.
So by only unticking "produce single file" my _logging.LogDebug starts to write stuff to the console.
Seems to be this: https://github.com/serilog/serilog-settings-configuration/issues/244
Related
I am trying to output some log messages from within .NET Core 3.1-based xUnit test cases by means of the NLog logging library. Logging by means of NLog works fine in my main application, but the test cases just won't output any messages.
I think I am doing everything suggested in this related question: NLog works in ASP.NET Core App but not in .NET Core xUnit Test Project
Somehow, though, I cannot figure out what is missing. I have reduced my code into a minimal sample that seems very straightforward, but still does not output anything:
using NLog;
using NLog.Config;
using NLog.Targets;
using System;
using Xunit;
using Xunit.Abstractions;
namespace UnitTests
{
public class LoggingTest
{
public LoggingTest(ITestOutputHelper output)
{
this.output = output;
}
private readonly ITestOutputHelper output;
[Fact]
public void TestLog()
{
var target = new MemoryTarget {Layout = "${message}"};
LogManager.Configuration ??= new LoggingConfiguration();
LogManager.Configuration.AddRuleForAllLevels(target);
LogManager.GetCurrentClassLogger().Info("Hello, World!");
output.WriteLine("{0} line(s) logged:\n{1}", target.Logs.Count, String.Join("\n", target.Logs));
}
}
}
Expected output:
1 line(s) logged:
Hello, World!
Actual output:
0 line(s) logged:
As one further trace, I have read in various places that NLog will only write something in .NET Core 3.1 projects if certain settings are present in a Logging section of the appsettings.json file. I think this section also had to be added to our main application's appsettings.json file.
I am not sure how to transfer this knowledge to the unit tests, though, as they do not appear to come with an appsettings.json file. I have tried copying the main appsettings.json file to the output directory of the unit tests (which is, I think, their execution directory when run from within ReSharper), but to no avail.
What am I missing?
To apply the config, you need to assign LogManager.Configuration, like
LogManager.Configuration = config;
Working example:
[Fact]
public void TestLog()
{
var target = new MemoryTarget { Layout = "${message}" };
var config = new LoggingConfiguration();
config.AddRuleForAllLevels(target);
LogManager.Configuration = config; // <-- assign here
LogManager.GetCurrentClassLogger().Info("Hello, World!");
output.WriteLine("{0} line(s) logged:\n{1}", target.Logs.Count, String.Join("\n", target.Logs));
Assert.Equal(1, target.Logs.Count);
}
Bonus: tests in parallel
Bonus, if you like tests in parallel (who doesn't ;)) - create a new LogFactory instead of assigning the global LogManager.
Like this:
[Fact]
public void TestLogParallelSafe()
{
var logFactory = new LogFactory();
var target = new MemoryTarget { Layout = "${message}" };
var config = new LoggingConfiguration();
config.AddRuleForAllLevels(target);
logFactory.Configuration = config;
logFactory.GetCurrentClassLogger().Info("Hello, World!");
output.WriteLine("{0} line(s) logged:\n{1}", target.Logs.Count, String.Join("\n", target.Logs));
Assert.Equal(1, target.Logs.Count);
}
Of course if other code is using the LogManager, you can't assert those logs.
.NET Core integration
As one further trace, I have read in various places that NLog will only write something in .NET Core 3.1 projects if certain settings are present in a Logging section of the appsettings.json file. I think this section also had to be added to our main application's appsettings.json file.
This is only needed when integrating with ASP.NET Core - e.g. when injection the ILogger<T> from Microsoft. That's is not needed here. For further reference, see Getting started with ASP.NET Core 3 · NLog/NLog Wiki
The approach proposed by Julian can be improved by implementing a specific target which writes the log entries to the output directly, without delay and without buffering all log entries in memory.
internal class TestOutputTarget: TargetWithLayout {
private readonly ITestOutputHelper output;
public TestOutputTarget(ITestOutputHelper output) {
this.output = output;
}
protected override void Write(LogEventInfo logEvent) {
output.WriteLine(RenderLogEvent(Layout, logEvent));
}
}
...
// In the test code
var target = new TestOutputTarget(output) { Layout = "${message}" };
I'd like to have my .Net Core 3.1 app automatically reload its configuration as the file changes on disk, but I'm having trouble getting this to work with the Options pattern. I run my app, save changes to the config file, and it's never called. Why doesn't the IOptionsMonitor instance ever call the OnChange handler? What am I missing?
Program.cs IHostBuilder creation
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(config => configuration = config.Build())
.ConfigureServices((hostContext, services) => {
var separateConfig = new ConfigurationBuilder()
.AddJsonFile("SeparateConfig.json", optional: false, reloadOnChange: true)
.Build();
services
.AddSingleton<MyMainService>()
.Configure<MySeparateConfig>(c => separateConfig.Bind(c));
})
Service that uses MySeparateConfig
public class MyMainService
{
public MyMainService(IOptionsMonitor<MySeparateConfig> config)
{
if (config is null) throw new ArgumentNullException(nameof(config));
ConfigChangeHandle = config.OnChange(UpdateConfiguration);
// Perform initial configuration using config.CurrentValue here
}
private IDisposable ConfigChangeHandle { get; set; }
private void UpdateConfiguration(MySeparateConfig config)
{
// Never called
}
}
As #Nkosi pointed out in comments, this line was the problem:
// Wrong
.Configure<MySeparateConfig>(c => separateConfig.Bind(c));
When I replaced it with the line below, everything started working right:
// Right
.Configure<MySeparateConfig>(separateConfig);
For the reload mechanism to work with IOptionsMonitor<TOptions>, an IOptionsChangeTokenSource<TOptions> needs to be registered in the services.
If you need an OptionsBuilder<TOptions>, for example to add validation, this can be achieved with AddOptions + BindConfiguration (from the Microsoft.Extensions.Options.ConfigurationExtensions NuGet package if you don't have a dependency in ASP.NET Core)
services.AddOptions<MyConfig>().BindConfiguration("").Validate(…)
Note that the BindConfiguration extensions method automatically registers the IOptionsChangeTokenSource<TOptions> which is the magic required for the reload mechanism to work.
In my ASP.NET Core app Iused the following code to initialize and seed database, especially for testing. Example below.
ASP.NET Core example:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
string entityName = $"Data Source=master_data.{typeof(TTestedEntity).Name}.db";
builder.ConfigureServices(services =>
{
ServiceProvider serviceProvider = new ServiceCollection()
.AddEntityFrameworkSqlite()
.BuildServiceProvider();
services.AddDbContext<MasterDataContext>(options =>
{
options.UseSqlite(entityName);
options.UseInternalServiceProvider(serviceProvider);
});
ServiceProvider sp = services.BuildServiceProvider();
using (IServiceScope scope = sp.CreateScope())
{
IServiceProvider scopedServices = scope.ServiceProvider;
MasterDataContext db = scopedServices.GetRequiredService<MasterDataContext>();
ILogger<DiLibMasterDataWebApplicationFactory<TStartup, TTestedEntity>> logger = scopedServices
.GetRequiredService<ILogger<DiLibMasterDataWebApplicationFactory<TStartup, TTestedEntity>>>();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
MasterDataSeed.Seed(db);
}
});
I want to use something similar in Azure Durable Functions on my local machine for testing, playing purposes. But it cases this error.
The documentation says the following.
Is the quoted text refers to that I want to do? I mean does it says that, hey, coder, don't do that because there are other stuff you are not aware of and you might find yourself in a crazy situation!
The startup class is meant for only setup and registration. Avoid
using services registered at startup during the startup process. For
instance, don't try to log a message in a logger that is being
registered during startup. This point of the registration process is
too early for your services to be available for use. After the
Configure method is run, the Functions runtime continues to register
additional dependencies, which can affect how your services operate.
If no then what is the solution for this?
The error message linked above mention two types which aren't there. I haven't found these types anywhere.
private void ConfigureControlPanel(IFunctionsHostBuilder builder)
{
builder.Services.AddDbContext<ControlPanelContext>(options =>
{
options.UseSqlite("Data Source=test.db");
});
builder.Services.AddTransient<ModuleValidator>();
builder.Services.AddTransient<IControlPanelBusinessLogic, ControlPanelBusinessLogic>();
ServiceProvider sp = builder.Services.BuildServiceProvider();
using (IServiceScope serviceScope = sp.CreateScope())
{
IServiceProvider scopedServices = serviceScope.ServiceProvider;
ControlPanelContext controlPanelContext = scopedServices.GetRequiredService<ControlPanelContext>();
controlPanelContext.Database.EnsureDeleted();
controlPanelContext.Database.EnsureCreated();
Module m1 = new Module { Name = "asd", Description = "asdd", ModuleRoute = "asd"};
Module m2 = new Module { Name = "asd2", Description = "asdd2", ModuleRoute = "asd2"};
controlPanelContext.Modules.Add(m1);
controlPanelContext.Modules.Add(m2);
}
}
I have an ASP.NET 5 Web API that I host in Azure as a Web App. I want to log messages from my code using Azure Diagnostics. There are multiple article including Azure docs that suggest that it should be as easy as System.Diagnostics.Trace.WriteLine once enabled. The logs should show up under LogsFiles/Application and in log stream in Azure.
I enabled application logging for the web app:
But the following calls produces no logs:
System.Diagnostics.Trace.TraceError("TEST");
System.Diagnostics.Trace.TraceInformation("TEST");
System.Diagnostics.Trace.TraceWarning("TEST");
System.Diagnostics.Trace.WriteLine("TEST");
I tried to manually define TRACE symbol, but with no luck:
I also tried to use the new Microsoft.Extensions.Logging framework and use ILogger.Log API, but without any results again:
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
loggerFactory.MinimumLevel = LogLevel.Debug;
var sourceSwitch = new SourceSwitch("Sandbox.AspNet5.ApiApp-Demo");
sourceSwitch.Level = SourceLevels.All;
loggerFactory.AddTraceSource(sourceSwitch,
new ConsoleTraceListener(false));
loggerFactory.AddTraceSource(sourceSwitch,
new EventLogTraceListener("Application"));
}
Any ideas on what am I doing wrong?
If you look at your web.config, it probably has stdoutLogEnabled="false". If you set that to true, then anything that is written to standard output will be written to a file. And the stdoutLogFile determines where it goes, which by default is under d:\home\logfiles.
Now you need to make sure that your logging actually goes to stdout. Doing Console.WriteLine would definitely work. I think it's probably possible to also configure things via ILoggerFactory in your startup.cs to make logs go to stdout.
I've found an simple trick for azure app ,see https://github.com/aspnet/Logging/tree/dev/src/Microsoft.Extensions.Logging.AzureAppServices,
please add the package "Microsoft.Extensions.Logging.AzureAppServices": "1.0.0-preview1-final" and update related dependencies,
add the azure diagnostics in startup.cs like this :
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddAzureWebAppDiagnostics(); // for default setting.
or for custom setting:
loggerFactory.AddAzureWebAppDiagnostics(new AzureAppServicesDiagnosticsSettings( ...)); // add custom setting.
// see here for detailed member properties: https://github.com/aspnet/Logging/blob/dev/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesDiagnosticsSettings.cs
And enable the diagnostics log on azure, both logging on blob and file are work well. No need for any
extra configuration. :)
I got Streaming Log output working for an ASP.NET 5 web app by writing the following class (based on David Ebbo's example):
public class AzureApplicationLogTraceListener : TraceListener
{
private readonly string _logPath;
private readonly object _lock = new object();
public AzureApplicationLogTraceListener()
{
string instanceId = Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID");
if (instanceId != null)
{
string logFolder = Environment.ExpandEnvironmentVariables(#"%HOME%\LogFiles\application");
Directory.CreateDirectory(logFolder);
instanceId = instanceId.Substring(0, 6);
_logPath = Path.Combine(logFolder, $"logs_{instanceId}.txt");
}
}
public override void Write(string message)
{
if (_logPath != null)
{
lock (this)
{
File.AppendAllText(_logPath, message);
}
}
}
public override void WriteLine(string message)
{
Write(message + Environment.NewLine);
}
}
and then putting this in my Startup.Configure:
Trace.Listeners.Add(new AzureApplicationLogTraceListener());
This only supports filesystem-based logging (which is sufficient for live log streams).
I am currently using Hangfire to run some jobs in a windows service. The jobs are fired from a WebAPI.
My system is currently working fine when all jobs succeed, but I am getting no logging for when exceptions occur. Has anyone got any experience in using a custom logger to receive messages from Hangfire?
My logger is a basic NLog interface:
public class Logger : ILogger
{
public readonly NLog.Logger logger;
public Logger(string name)
{
if (LogManager.Configuration == null)
{
FallbackInitialisation();
}
logger = LogManager.GetLogger(name);
}
public Trace(string message)
//etc.
}
I am configuring my Hangfire job server in my windows service like so:
SqlServerStorage storage = new SqlServerStorage("myConnectionString");
BackgroundJobServerOptions options = new BackgroundJobServerOptions();
m_server = new BackgroundJobServer(options, storage);
GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 0 });
Based on the hangfire documentation, I simply tried following it by adding the required references, and adding the following after my job server setup, however there were still no logs produced:
var properties = new NameValueCollection();
properties["configType"] = "INLINE";
LogManager.Adapter = new NLogLoggerFactoryAdapter(properties);
I am simply simulating an exception by throw new Exception() in the method called by the job. The job is created in the webApi call like so:
[HttpGet]
public void TestStartJob()
{
m_logger.Trace("TestStartJob");
BackgroundJob.Enqueue(() => m_service.TestStartJob());
}
What I am looking for, is if anyone has any experience in getting logging properly configured for use with Hangfire.
I have managed to solve this issue by using the following versions of nuget packages:
NLog - 3.1
Common.Logging - 2.2.0
JVW.Logging.CommonLoggingNLogAdapter - 1.0.0.1
and then configuring the logging as such
var properties = new NameValueCollection();
properties["configType"] = "INLINE";
Common.Logging.LogManager.Adapter = new NLogFactoryAdapter(properties);