I have been given a dll named MYLogger.dll which is apparently a wrapper around log4net and has a MyLogger and a DefaultLogger classes defined in it and both these classes inherit ILogger. And I have to log exceptions and info for an Asp.Net Web Api service which has several projects defined within one solution in .net.
And these the signature for MyLogger class
public MyLogger(string name, string configFilePath = "",
string newLogFileFolderName = "");
public MyLogger(Type t, string configFilePath = "",
string newLogFileFolderName = "");
public event EventHandler<EventArgs<string>> MessageLogged;
public void Log(LogLevel logLevel, string message, object[] args = null);
public void LogException(Exception ex, string customMessage = "",
LogLevel logLevel = LogLevel.Error);
And signature for DefaultLogger class is
public DefaultLog();
public static DefaultLog Logger { get; }
public event EventHandler<EventArgs<string>> MessageLogged;
public void Log(LogLevel logLevel, string message, object[] args = null);
public void LogException(Exception ex, string customMessage = "",
LogLevel logLevel = LogLevel.Error);
And my config file named MyLog.Config looks like
<configuration>
<!-- Register a section handler for the log4net section -->
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
</configSections>
<log4net>
<appender name="ErrorFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="C\Data\Log\MyErrorLog.txt" />
<appendToFile value="true" />
<maxSizeRollBackups value="-1" />
<maximumFileSize value="10MB" />
<rollingStyle value="Size" />
<layout type="MyLogger.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] <%property{auth}> - %message%newline" />
</layout>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="ERROR" />
<levelMax value="FATAL" />
</filter>
</appender>
<appender name="InfoFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="C\Data\Log\MyInfoLog.txt" />
<appendToFile value="true" />
<maxSizeRollBackups value="-1" />
<maximumFileSize value="10MB" />
<rollingStyle value="Size" />
<layout type="MyLogger.MyPatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] <%property{auth}> - %message%newline" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="INFO" />
<levelMax value="INFO" />
</filter>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="ErrorFileAppender" />
<appender-ref ref="InfoFileAppender" />
</root>
</log4net>
</configuration>
And I'm trying to use both the logger class viz MyLogger for both exception logging and info logging as below.
private static ILogger log = new Logger(MethodBase.GetCurrentMethod()
.DeclaringType, #"~\MyLog.config"," ");
Even when i give path to second parameter(for either info or exception text file) rather being empty, still nothing is written to either of them. I tried to throw the exception but still no success for exception logging.
And with the help of log object I'm trying to achieve logging for info as below
string message = string.Format("The feature class name is {0} and the type is {1}",Name,Type);
//DefaultLog.Logger.Log(LogLevel.Info, message);
log.Log(LogLevel.Info, message);
as you can see I tried both with DefaultLog and log object(which is an implementation of ILog interface with MyLogger class) and the same way I'm trying to achieve exceptions using following code at the time some exception is thrown inside by a controller action
catch (HttpResponseException ex)
{
string errorMessage = string.Format("Getting {0} data gives error because either fcollection is empty or soemthing is null Error:{1}",
Name, ex.Message);
//DefaultLog.Logger.LogException(ex, errorMessage, LogLevel.Error);
log.LogException(ex, errorMessage, LogLevel.Error);
}
For both info and exception nothing gets logged/written into text files mentioned along with their respective paths inside my Xml config file.
And the weirdest thing about the two constructors of MyLogger class is that both constructors have string as a parameter for configfile path and the string as a parameter for newLogFileFolderName, how could I figure out that this newLogFileFolderName is asking for path for text file where info logs are written or text file where exception logs are written. And I'm assuming that inside the constructors of MyLogger class something like
XmlConfigurator.ConfigureAndWatch(new FileInfo(MyLog.config))
has been used that's why 1st parameter for config file has been substituted for ConfigureAndWatch ( ) method's argument but I have no idea what is the use of second parameter inside constructor code of MyLogger i.e do I need to instantiate MyLogger class twice once with path for info text file and second time path for exception text file as a second parameter in the constructor. That doesn't make sense to me.Its highly confusing. All suggestion will be highly appreciated.
Related
I'd like to change the name of my log at runtime without changing the appender. Basically, once it gets to a certain set time of day I'd like to be able to use a different file.
This is what my appender looks like:
<appender name="info" type="log4net.Appender.RollingFileAppender">
<file value="logs\" />
<datePattern value="yyyyMMdd'_INFO.log'" />
<staticLogFileName value="false" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%newline[%date]-%level-%logger[%M]- Linea:%L - %message%newline"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="INFO"/>
<param name="LevelMax" value="INFO"/>
</filter>
</appender>
This is the function where I iterate through the appenders:
public void SetLogFile(string fileName)
{
foreach (var appender in log.Logger.Repository.GetAppenders())
{
try
{
((log4net.Appender.FileAppender)appender).File = fileName;
((log4net.Appender.FileAppender)appender).ActivateOptions();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
SetLogFile("log1.txt");
Which creates the following file for this specific example:
log1.txt20171221_INFO.log
I'd like for my log file to be named log1.txt and not have the datePattern set in the log4net.config file concatenated with the name, but I can't figure out how to do it without changing the log4net.config appender configuration.
As you are using RollFileAppender, you have to change the RollingStyle and set the path accordingly.
foreach (var appender in log.Logger.Repository.GetAppenders())
{
try
{
string file = Path.GetDirectoryName(((log4net.Appender.RollingFileAppender)appender).File);
string filename = Path.Combine(file, fileName);
switch (((log4net.Appender.RollingFileAppender)appender).RollingStyle)
{
case log4net.Appender.RollingFileAppender.RollingMode.Date:
((log4net.Appender.RollingFileAppender)appender).RollingStyle = log4net.Appender.RollingFileAppender.RollingMode.Once;
break;
case log4net.Appender.RollingFileAppender.RollingMode.Composite:
((log4net.Appender.RollingFileAppender)appender).RollingStyle = log4net.Appender.RollingFileAppender.RollingMode.Size;
break;
}
((log4net.Appender.FileAppender)appender).File = filename;
((log4net.Appender.FileAppender)appender).ActivateOptions();
}
catch (Exception ex) {}
}
I am using log4net together with ElasticSearch and Kibana.
By now my web.config looks like this:
<log4net>
<appender name="ElasticSearchAppender" type="log4net.ElasticSearch.ElasticSearchAppender, log4net.ElasticSearch">
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%date - %level - %message %property{location} %property{label} %property{mstimeload} %property{applicationid} %property{page}
%property{ipclient} %property{browser} %property{browsersignature} %property{appversion} %property{sessionuniquecodetag} %property{globalcountertailsloaded}
%property{ipserveraddress} %newline" />
</layout>
<connectionString value="Server=myip;Index=logstash;Port=9200;rolling=true"/>
<lossy value="true" />
<bufferSize value="100" />
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="ERROR"/>
</evaluator>
</appender>
<root>
<level value="ALL"/>
<appender-ref ref="ElasticSearchAppender" />
</root>
I have some custom parameters like location, label, mstimeload, applicationid, page, ipclient, ...
Everything works fine, but all of these parameters are of string type, instead I would like to have also integer or geo_point type, but I don't know how to tell log4net of which type is my parameter.
Then in c# I write my log like this:
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
log4net.ThreadContext.Properties["label"] = label;
log4net.ThreadContext.Properties["ipclient"] = ipaddress;
log4net.ThreadContext.Properties["browser"] = browserType;
log4net.ThreadContext.Properties["browsersignature"] = browserHashSignature;
log4net.ThreadContext.Properties["appversion"] = ASSettings.ApplicationVersion;
log4net.ThreadContext.Properties["mstimeload"] = msTime == null ? null : Convert.ToString(Convert.ToInt32(msTime.Value), CultureInfo.InvariantCulture);
log4net.ThreadContext.Properties["globalcountertailsloaded"] = globalCounter_tilesloaded == null ? null : Convert.ToString(globalCounter_tilesloaded.Value, CultureInfo.InvariantCulture);
log4net.ThreadContext.Properties["ipserveraddress"] = ipserveraddress;
log4net.ThreadContext.Properties["page"] = page;
log4net.ThreadContext.Properties["sessionuniquecodetag"] = sessionuniquecodetag;
log4net.ThreadContext.Properties["applicationid"] = applicationid;
log4net.ThreadContext.Properties["location"] = ASSecurity.GetLatLongFromIp(ipaddress);
log.Error(description, ex);
But I am not sure if this is the best way to do it or if there are also other ways.
Here is how log4net is been setup
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="..\AppLogs\%property{LogName}.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="5MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{yyyy/MM/dd HH:mm:ss} [%thread] %-5level %logger - %message%newline" />
</layout>
............
</log4net>
I know I can get the file name of log file using something like below.
var fileAppender = log4net.LogManager.GetRepository().GetAppenders().First(appender => appender is RollingFileAppender);
However , I want to get the folder name where the log file will be created. Is there anyway of getting this?
var fileAppender = _logger.Logger.Repository.GetAppenders().OfType<RollingFileAppender>().FirstOrDefault();
assuming that you are declaring your instance of the _logger like this
private static readonly ILog _logger = LogManager.GetLogger(typeof(YourClass));
The File property contains the full path to the log file. Once you have that, use Path.GetDirectoryName to get the folder.
This MSTest unit test shows that:
[TestInitialize]
public void Setup()
{
log4net.GlobalContext.Properties["LogName"] = "testlogger";
var fileInfo = new FileInfo("log4net.config.xml");
if (fileInfo.Exists == false)
{
throw new InvalidOperationException("Can't locate the log4net config file");
}
LogManager.ResetConfiguration();
log4net.Config.XmlConfigurator.Configure(fileInfo);
}
[TestMethod]
[DeploymentItem(#"log4net.config.xml")]
public void log4net_RollingFileAppender_Is_Configured_Correctly()
{
var appender = log4net.LogManager.GetRepository()
.GetAppenders()
.OfType<RollingFileAppender>()
.First();
Console.WriteLine(appender.File);
Console.WriteLine(Path.GetDirectoryName(appender.File));
}
And in the config:
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="..\AppLogs\%property{LogName}.txt" />
Test result:
I wrote a log4net wrapper
public class Log4NetWrapper : ILogger
{
private readonly log4net.ILog logger;
public Log4NetWrapper(string loggerName)
{
logger = log4net.LogManager.GetLogger(loggerName);
}
public void Debug(string message, params object[] values)
{
logger.DebugFormat(message, values);
}
public bool IsDebugEnabled {get { return logger.IsDebugEnabled; } }
...
}
the problem is that the line and file I get when logging is of the wrapper and not the actual location of the message.
this is by design, if you are interested in line and file where an exception is thrown in the first place you should log the exception object or its stacktrace member, if you have configured the log4net file appender to show the file and line where the message is been written from, it is normal that you find your wrapper, but when logging exceptions and stacktrace you will find the correct content.
in our appenders we have the following, so we do not even show the wrapper class name or file/line...
<log4net>
<appender name="FileAppender" type="log4net.Appender.RollingFileAppender">
<threshold value="ALL"/>
<immediateFlush>true</immediateFlush>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<encoding value="utf-8"/>
<file value="D:\Axis\RPP\Logs\myLogFile.log" />
<appendToFile value="true"/>
<rollingStyle value="Date" />
<maxSizeRollBackups value="30" />
<maximumFileSize value="25MB" />
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%property{log4net:HostName}] - %username%newline%utcdate - %-5level - %message%newline"/>
</layout>
</appender>
<root>
<priority value="ALL"/>
<appender-ref ref="FileAppender"/>
</root>
This is my configuration for log4net:
<log4net>
<appender name="MyLogger" type="log4net.Appender.RollingFileAppender">
<file value="MyLog.log" />
<appendToFile value="true" />
<rollingStyle value="Size"/>
<maxSizeRollBackups value="20"/>
<maximumFileSize value="1000KB"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss},%p,%m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="MyLogger" />
</root>
</log4net>
In C# I'm trying to get the name of the log file (which is MyLog.log). I googled and tried many things but failed to do so. Any help?
Thanks!
Solution is quite easy in your situation; just use this code:
var rootAppender = ((Hierarchy)LogManager.GetRepository())
.Root.Appenders.OfType<FileAppender>()
.FirstOrDefault();
string filename = rootAppender != null ? rootAppender.File : string.Empty;
When having multiple file appenders, you might want to get them by name. Also to make sure to get the appender even if it is not referenced by the root node, the following code helps:
public static string GetLogFileName(string name)
{
var rootAppender = LogManager.GetRepository()
.GetAppenders()
.OfType<FileAppender>()
.FirstOrDefault(fa => fa.Name == name);
return rootAppender != null ? rootAppender.File : string.Empty;
}
Since I already had a logger defined in the class I just used it. One thing to be aware of is that there may be more than one appender and often the first one is the console (which doesn't have a file). Here is my solution for what its worth.
using log4net;
using log4net.Appender;
using log4net.Repository;
namespace MyNameSpace {
public class MyClass {
private static readonly ILog logger = LogManager.GetLogger(typeof(MyClass));
public String GetLogFileName() {
String filename = null;
IAppender[] appenders = logger.Logger.Repository.GetAppenders();
// Check each appender this logger has
foreach (IAppender appender in appenders) {
Type t = appender.GetType();
// Get the file name from the first FileAppender found and return
if (t.Equals(typeof(FileAppender)) || t.Equals(typeof(RollingFileAppender))) {
filename = ((FileAppender)appender).File;
break;
}
}
return filename;
}
}
}
String filename = null;
Hierarchy hierarchy = LogManager.GetRepository() as Hierarchy;
Logger logger = hierarchy.Root;
IAppender[] appenders = logger.Repository.GetAppenders();
// Check each appender this logger has
foreach (IAppender appender in appenders)
{
Type t = appender.GetType();
// Get the file name from the first FileAppender found and return
if (t.Equals(typeof(FileAppender)) || t.Equals(typeof(RollingFileAppender)))
{
filename = ((FileAppender)appender).File;
break;
}
}
System.Diagnostics.Process.Start(filename); //for example, open file in notepad
((log4net.Appender.FileAppender)(_log.Logger.Repository.GetAppenders())[0]).File
If your config does not have a <root> node then the above solution will not work for you. Read on.
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="${LOCALAPPDATA}\Anonymous.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="2000KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<logger name="AnonymousLog">
<level value="All" />
<appender-ref ref="RollingFileAppender" />
</logger>
</log4net>
This retrieves the log file:
string path = (LogManager.GetCurrentLoggers()[0].Logger.Repository.GetAppenders()[0] as FileAppender).File;
The (hopefully) crash-proof version:
string path = null;
if (LogManager.GetCurrentLoggers().Length > 0 && LogManager.GetCurrentLoggers()[0].Logger.Repository.GetAppenders().Length > 0)
{
path = (LogManager.GetCurrentLoggers()[0].Logger.Repository.GetAppenders()[0] as FileAppender).File;
}
Finally, if you get stuck with log4net add this to your <appSettings> section:
<add key="log4net.Internal.Debug" value="true"/>
I didn't find the above code working.
This worked for me
var filename= ((log4net.Appender.FileAppender)(((log4net.Appender.IAppender[])((((((log4net.Repository.Hierarchy.Hierarchy)((((log4net.Core.LoggerWrapperImpl)(log)).Logger).Repository)).Root).Hierarchy.Root).Appenders).SyncRoot))[0])).File