We're using NLog for our application. I have set up the logger in the code using mostly the documentation. Yet the logger object gets disposed of while the app is about to write to it causing an error. I use the logger in .NET CORE 3.1 in the Middlewares that handles exceptions globally and authentication. The funniest part is that the error itself is logged to the file by NLog. Error from the logs:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'LoggerFactory'.
at Microsoft.Extensions.Logging.LoggerFactory.CreateLogger(String categoryName)
at Microsoft.Extensions.Logging.Logger`1..ctor(ILoggerFactory factory)
at Microsoft.Extensions.Logging.LoggerFactoryExtensions.CreateLogger[T](ILoggerFactory factory)
Configuration in the code:
public static class NLogConfig
{
public static void CreateLogger(IConfiguration configuration)
{
var logName = configuration.GetSection("LoggerConfig").GetSection("LogName").Get<string>();
var logPath = configuration.GetSection("LoggerConfig").GetSection("Directory").Get<string>();
var infoLogLayout = configuration.GetSection("LoggerConfig").GetSection("InfoLayout").Get<string>();
var errorLogLayout = configuration.GetSection("LoggerConfig").GetSection("ErrorLayout").Get<string>();
var config = new NLog.Config.LoggingConfiguration();
var infoLog = new NLog.Targets.FileTarget("FileLog") { FileName = logPath + logName, Layout = infoLogLayout };
var errorLog = new NLog.Targets.FileTarget("FileLog") { FileName = logPath + logName, Layout = errorLogLayout };
config.AddRule(LogLevel.Trace, LogLevel.Info, infoLog);
config.AddRule(LogLevel.Warn, LogLevel.Fatal, errorLog);
NLog.LogManager.Configuration = config;
}
}
JSON configuration file:
"LoggerConfig": {
"LogName": "MY_API_${shortdate}.log",
"Directory": "C:\\Logs\\MY_API\\",
"InfoLayout": "${longdate} | INFO | ${logger} | ${message}",
"ErrorLayout": "------------------\n${longdate} | ERROR | ${logger} | ${message:withexception=true} \n------------------"
}
In the classes that use the log I do this as it is in documentation:
private static readonly NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
The "disposed" error happens randomly and not frequently, but often enough to be a slight concern. How can I avoid this problem? I've read that NLog should be thread-safe so calls to the API shouldn't cause such a problem. Especially when it was only one person using the API at the time
Related
I am trying to read the custom-defined configuration file from the library project that I have created in c#.
To read the configuration file, There is a ConfigManager i have created as below,
static Configuration ConfigManager
{
get
{
if (_configuration == null)
{
//Map the new configuration file.
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileMap.ExeConfigFilename = _assemblyLocation;
// Get the mapped configuration file
_configuration = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
}
return _configuration;
}
}
I am getting the exception at OpenMappedExeConfiguration() Method.
Any help would be appreciated.
Thanks in advance.
I have a project which uses the below code to create a NLog instance.
public FileTarget CreateNLogFileTarget(string layout, FileArchivePeriod archiveMode, int maxArchiveFiles,
bool keepFileOpen, bool enableConcurrentWrites, ArchiveNumberingMode archiveNumberingMode, string fileName)
{
FileTarget fileTarget = new FileTarget();
fileTarget.Layout = layout;
fileTarget.ArchiveEvery = archiveMode;
fileTarget.MaxArchiveFiles = maxArchiveFiles;
fileTarget.KeepFileOpen = keepFileOpen;
fileTarget.ConcurrentWrites = enableConcurrentWrites;
fileTarget.ArchiveNumbering = archiveNumberingMode;
fileTarget.FileName = fileName;
return fileTarget;
}
FileTarget infoLogFileTarget = CreateNLogFileTarget(#"${longdate} ${message}",
FileArchivePeriod.Hour, 70, false, true, ArchiveNumberingMode.Rolling, "${basedir}/Logs/" + infoLogName + "/${shortdate}{#}.log");
I am using this project in another project and I need to use this NLog utility class to create my loggers. But I need to override these configurations. How can I override these configurations through the xml file? Any help would be much appreciated.
To use the FileTarget from CreateNLogFileTarget in your XML config, you should first find out the target name of the FileTarget it's probably in other parts of the code. Then you could use the target in your config:
<logger name='*' minlevel="Trace" writeTo='theTarget' />
Maybe by using NLog-variables. Change your CreateNLogFileTarget to setup the parameters to get their value from NLog-variables.
Then on startup check if these NLog variables already exists in the loaded NLog-configuration. If not then they are set by the runtime, before calling CreateNLogFileTarget.
https://github.com/NLog/NLog/wiki/Configuration-file#variables
I've created a static class to use my configurations, but when I try to add the JSON file to the configuration I got an exception:
MyConfigurations.json:
{ "ConnectionString": "my connection string", ...}
My static class constructor:
static MyConfigurations() {
var configuration = new Configuration()
.AddJsonFile("MyConfigurations.json")
.AddEnvironmentVariables();
...
...
My exception occurs when the .AddJsonFile is executed.
Exception: Object reference not set to an instance of an object."
StackTrace:
at
Microsoft.Framework.ConfigurationModel.PathResolver.get_ApplicationBaseDirectory()
at
Microsoft.Framework.ConfigurationModel.JsonConfigurationExtension.AddJsonFile(IConfigurationSourceRoot
configuration, String path, Boolean optional) at
Microsoft.Framework.ConfigurationModel.JsonConfigurationExtension.AddJsonFile(IConfigurationSourceRoot
configuration, String path) at
Project.SharedKernel.MyConfigurations..cctor() in
C:\Project\Project.SharedKernel\MyConfigurations.cs:line 86
You have not set the application base path which the configuration API needs to determine where the physical config files live. You can set it using the SetBasePath extension method. A typical implementation looks like this:
public Startup(IApplicationEnvironment appEnv)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("MyConfigurations.json")
.AddEnvironmentVariables()
.Build();
}
Note: this only counts for beta8, see this question. You don't have to specify the default base path anymore in RC1: https://github.com/aspnet/Announcements/issues/88.
I need to write event log for every instance of class to separate file. Historically project uses NLog for logging, so I want to try resolving this issue using NLog (I've found similar topic Unique log file for each instance of class , but it's suggests using log4net)
Currently I'm getting instance of logger like this:
public static Logger GetInstanceLogger(string name, bool init = false)
{
if (!LogManager.Configuration.AllTargets.Any(t => t.Name == name))
{
var target = new FileTarget();
target.Name = name;
target.FileName = string.Format("logs/{0}.${{shortdate}}.log", name);
target.Layout =
"${date:format=dd.MM.yyyy HH\\:mm\\:ss.fff} thread[${threadid}] ${logger} (${level:uppercase=true}): ${message}. ${exception:format=ToString}";
var config = init ? new LoggingConfiguration() : LogManager.Configuration;
config.AddTarget(name, target);
var ruleInfo = new LoggingRule("*", LogLevel.Trace, target);
config.LoggingRules.Add(ruleInfo);
LogManager.Configuration = config;
LogManager.ReconfigExistingLoggers();
}
var logger = LogManager.GetLogger(name);
return logger;
}
Right now it's writing same log into all files (I suppose it's caused by the log level). Is there a way to accomplish this task using NLog?
Thanks.
I came out with solution using event properties of layout renderer in the filename. When I'm writing new message to log, I'm adding filename as a property for LogEventInfo
protected virtual void SendEvent(LogLevel level, string message, Exception exception, string memberName = null)
{
var logEvent = new LogEventInfo(level, _name, message);
logEvent.Exception = exception;
foreach (String key in _properties.Keys)
{
logEvent.Properties[key] = _properties[key];
}
_logger.Log(logEvent);
}
And in configuration file in NLog targets section:
<targets async="true">
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}_${event-properties:item=Name}.log"
layout="${detailedLayout}" />
<target xsi:type="File" name="errorLogFile" fileName="${basedir}/logs/${shortdate}.ERROR_${event-properties:item=Name}.log"
layout="${detailedLayout}" />
</targets>
try var ruleInfo = new LoggingRule(name, LogLevel.Trace, target);
There is some code for logging to file. I dont using app.config
class Program
{
static void Main(string[] args)
{
MyLogger.Write("This is message error", "My Category");
Console.ReadKey();
}
}
public static class MyLogger
{
static readonly LogWriterImpl _writer;
static MyLogger()
{
TextFormatter formatter = new TextFormatter
("Timestamp: {timestamp}{newline}" +
"Message: {message}{newline}" +
"Category: {category}{newline}");
var logFileListener = new Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener
(
"c:\\messages.log", "----------", "----------", formatter
);
LogSource mainLogSource = new LogSource("MainLogSource", SourceLevels.All);
mainLogSource.Listeners.Add(logFileListener);
LogSource nonExistantLogSource = new LogSource("Empty");
IDictionary<string, LogSource> traceSources = new Dictionary<string, LogSource>();
traceSources.Add("Error", mainLogSource);
traceSources.Add("Debug", mainLogSource);
_writer = new LogWriterImpl
(
new Microsoft.Practices.EnterpriseLibrary.Logging.Filters.ILogFilter[0],
traceSources,
nonExistantLogSource,
nonExistantLogSource,
mainLogSource,
"Error",
false,
true
);
}
public static void Write(string message)
{
Write(message, "Error");
}
public static void Write(string message, string category)
{
LogEntry entry = new LogEntry();
entry.Categories.Add(category);
entry.Message = message;
_writer.Write(entry);
}
}
This program work without errors but it don't create log file c:\messages.log and don't write log entity. Where is the error? I don't want using application config file in my project
There could be a couple of reasons (at least!) why you are not seeing any logging:
The categories that are configured for logging are "Error" and "Debug" but when you call MyLogger.Write you are passing a category of "My Category"
There could be a permission problem. Writing to the root of the drive is frequently restricted
As an aside, you should probably store the reference to LogWriterImpl as the base class LogWriter.
As another aside, instead of using the logging classes directly it is preferable to use the Fluent Configuration API which was released as part of version 5.0. It makes this type of configuration much simpler. As an example:
var builder = new ConfigurationSourceBuilder();
builder.ConfigureLogging()
.WithOptions
.DoNotRevertImpersonation()
.LogToCategoryNamed("My Category")
.SendTo.FlatFile("MyMessages")
.FormatWith(new FormatterBuilder()
.TextFormatterNamed("Text Formatter")
.UsingTemplate("Timestamp: {timestamp}...{newline})}"))
.ToFile("c:\\messages.log");
var configSource = new DictionaryConfigurationSource();
builder.UpdateConfigurationWithReplace(configSource);
EnterpriseLibraryContainer.Current
= EnterpriseLibraryContainer.CreateDefaultContainer(configSource);
It's also more maintainable and supportable. E.g. there is less chance that there won't be breaking implementation changes like when LogWriter was made abstract as part of version 5.