how to implement log4net in c# and what is need of log4net? - log4net

i just want to know that, what is use of log4net in application and how i`m gonna implement in c#.
i am done with some of the research that i am put under it.
public static class Logger
{
private static log4net.ILog Log { get; set; }
static Logger()
{
Log = log4net.LogManager.GetLogger(typeof(Logger));
}
public static void Error(object msg)
{
Log.Error(msg);
}
public static void Error(object msg, Exception ex)
{
Log.Error(msg, ex);
}
public static void Error(Exception ex)
{
Log.Error(ex.Message, ex);
}
public static void Info(object msg)
{
Log.Info(msg);
}
}
}
above is just a method that maintain log in separate file i just want to know in advance.

1.First you have to add a reference for log4net.dll if you havn't done that yet.
2.Then you have to add that statement above your stating class declaration. You can put it right bellow your using statements and outside of your class declaration:
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
3.The class that you already have created.
4.Add that config to your configSections tag on your web.config/app.config:
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
That will allow you to add tags to configure your log.
5.Add that to your configuration tag on your web.config/app.config:
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="RollingFileAppender"/>
</root>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="./Log/log.log" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<datePattern value="'.'yyyy_MM_dd" />
<maxSizeRollBackups value="30" />
<maximumFileSize value="100MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date{yyyy/mm/dd HH:mm:ss,fff} %-5level %logger [%thread] %message %exception%newline"/>
</layout>
</appender>
</log4net>
6.Then you just have to call the methods on your Logger class.
If you want more information i'll sugest you to read the blog of that guy Log4Net Tutorials and Resources

Related

How to write log4net config into appsettings.json?

I have implemented log4net into .NET core 2.0, to log into a text file. As log4net have a config file, which is having XML configuration in it. So, I have created a separate file log4net.config to set its configuration and it is working fine. But I want to set its configuration into appsettings.json. Is it possible to write the log4net configuration into appsettings.json.
<appSettings>
<add key="IsLog" value="True" />
<add key="MaxThreads" value="3" />
</appSettings>
<log4net debug="false">
<root>
<level value="ALL" />
<appender-ref ref="RollingLogFileAppender" />
</root>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="./logs/logfile.txt" />
<appendToFile value="false" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="-1" />
<maximumFileSize value="50GB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
</log4net>
this is my XML configuration.
I have found this question in which it is mentioned that log4net don't support Json projects. Is it possible to write its configuration into appsettings.json.
Maybe it is not the right way but, anyhow I managed to use my log4net.config in appSettings.json. I am putting my answer here so it can help someone if they don't want to use an extra config file for there project.
So what I have done is like by converting my XML into JSON and used the JSON as a string on my appSettings.json after that I use the following code to read the string.
appSettings.json
{
"ConnectionStrings": {
"LoggerConfig": "Config string"
}
}
Json to XML Conversion using Newtonsoft.Json
string xmlElement = _configuration["ConnectionStrings:LoggerConfig"];
XmlDocument doc = (XmlDocument)JsonConvert.DeserializeXmlNode(xmlElement);
XmlConfigurator.Configure(logRepository, GenerateStreamFromString(doc.InnerXml));
This code is used to convert the JSON into XML but it will not provide the XML content as Element so, I used it as a stream.
For converting the string into a stream I used the following code.
public static Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
and it works fine.
Here I used to convert first my XML to JSON and again JSON to XML to
use it as a log4net config, but if anyone wants then use XML
directly as a string, So it will reduce some code.
If you using Microsoft.Extensions.Logging.Log4Net.AspNetCore nuget package, there is a way keep log4net config in appsettings.json (but honestly not very usable).
You can write into appsettings.json (or appsettings.Environment.json for different environments) rules overriding nodes from log4net config file.
Documentation
Example of setting the logging level from appsettings.json.
appsettings.json:
{
"Log4NetCore": {
"PropertyOverrides": [
{
"XPath": "/log4net/root/level",
"Attributes": {
//"value": "ALL"
//"value": "DEBUG"
//"value": "INFO"
"value": "WARN"
//"value": "ERROR"
//"value": "FATAL"
//"value": "OFF"
}
}
]
}
}
You still needs log4net config file with nodes which you want override from appsettings.json:
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="DebugAppender" type="log4net.Appender.DebugAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<file value="example.log" />
<appendToFile value="true" />
<maximumFileSize value="100KB" />
<maxSizeRollBackups value="2" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %5level %logger.%method [%line] - MESSAGE: %message%newline %exception" />
</layout>
</appender>
<root>
<level value="ALL"/>
<appender-ref ref="DebugAppender" />
<appender-ref ref="RollingFile" />
</root>
</log4net>
Registration in Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
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.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Add these lines
var loggingOptions = this.Configuration.GetSection("Log4NetCore")
.Get<Log4NetProviderOptions>();
loggerFactory.AddLog4Net(loggingOptions);
app.UseMvc();
}
}

Change subject of SmtpAppender programmatically

I have an SmtpAppender like this:
<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="MyEmail#example.org" />
<from value="SenderEmail#example.org" />
<subject type="log4net.Util.PatternString" value="This is a subject" />
<smtpHost value="smtp.host.value" />
<authentication value="Basic" />
<port value="587" />
<username value="Username" />
<password value="Password" />
<bufferSize value="1" />
<EnableSsl value="true"/>
<lossy value="false" />
<threshold value="ERROR" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%3thread] %-5level [%class].[%method] - %message%newline" />
</layout>
</appender>
I would like to change the subject of the log mail in my code to a string I would specify so it suits my needs better than a standard subject.
Right now I achieve it like this. First, I find the SmtpAppender by name:
public static IAppender FindAppenderByName(string name)
{
ILoggerRepository rootRep = LogManager.GetRepository();
foreach (IAppender iApp in rootRep.GetAppenders()) {
if (string.Compare(name, iApp.Name, true) == 0) {
return iApp;
}
}
return null;
}
and then I can change the subject like this:
IAppender appender = FindAppenderByName("SmtpAppender");
SmtpAppender smtpAppender = (SmtpAppender)appender;
smtpAppender.Subject = "An error occured trying to convert the values";
log.Error("Error message");
But this seems to be a bit too complicated since every time I send an error mail I first have to find the appender by name and I don't want to use a global IAppender object as well as that seems like bad practice to me.
Is there a way to use the user-specified error message as a subject or is there an extension method I could use?
My proposal is as follows :
Create a new class extending SmtpAppender
Override SendBuffer
Based on your LoggingEvent, dynamically change the base.Subject
public class CustomSmtpAppender : SmtpAppender
{
protected override void SendBuffer(LoggingEvent[] events)
{
PrepareSubject(events);
base.SendBuffer(events);
}
protected virtual void PrepareSubject(IEnumerable<LoggingEvent> events)
{
//here you can eval events and set base.Subject
}
}

RemotingAppender problems. Conn created but nothing appended

I have been learning log4net and wish to use the remoting appender to log messages onto a server sometime in the future. To do this, I first tried creating a local .Net remoting server and appending to it. It seems to me that the server has been created but I cannot receive these messages. (To check this, I try accessing the server by entering localhost:portnumber in my browser, before and after running my program. It fails before and accepts the connection later. Any better way to debug this?)
Anyway, here is the code. I would appreciate any help.
PS: I can see the File & Console appenders work.
Client code
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.onfig.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<appender name="Console" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="DEBUG"/>
<foreColor value="Red, HighIntensity"/>
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%class %date [%level] - %message%newline"/>
</layout>
</appender>
<appender name="File" type="log4net.Appender.FileAppender">
<file value="logfile.txt"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%class %date [%level] - %message%newline"/>
</layout>
</appender>
<appender name="RemotingAppender" type="log4net.Appender.RemotingAppender">
<sink value="tcp://localhost:8086/RemoteLogger"/>
<lossy value="false"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%class %date [%level] - %message%newline"/>
</layout>
<bufferSize value="1"/>
<onlyFixPartialEventData value="true"/>
</appender>
<root>
<level value="ALL"/>
<appender-ref ref="Console"/>
<appender-ref ref="RemotingAppender"/>
<appender-ref ref="File"/>
</root>
</log4net>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
Server code Appconfig
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application name="RemoteLogger">
<channels>
<channel name="logging sink" ref="tcp server" port="8086"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Server Code
namespace RemoteAPP
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Listening");
var _sink = new RemoteSink();
_sink.EventsReached += (s,a)=> AddLog(a.LoggingEvents);
RemotingConfiguration.Configure("RemoteAPP.exe.config", false);
//RemotingConfiguration.RegisterWellKnownServiceType(new WellKnownServiceTypeEntry(typeof(RemoteSink), "RemoteLogger", WellKnownObjectMode.SingleCall));
RemotingServices.Marshal(_sink, "RemoteLogger");
Console.ReadLine();
}
private static void AddLog(IEnumerable<LoggingEvent> enumerable)
{
var Logevents = enumerable.ToList();
foreach(var logevent in Logevents)
{
Console.WriteLine(logevent);
}
}
}
public class RemoteSink:MarshalByRefObject,RemotingAppender.IRemoteLoggingSink
{
public class LoggingArgs:EventArgs
{
public IEnumerable<LoggingEvent> LoggingEvents;
}
public EventHandler<LoggingArgs> EventsReached;
void RemotingAppender.IRemoteLoggingSink.LogEvents(LoggingEvent[] events)
{
var ev = EventsReached;
if(ev==null)
{
ev.Invoke(this, new LoggingArgs{LoggingEvents = events});
}
}
}
}
Client Code
class Program
{
static void Main(string[] args)
{
Thread.Sleep(10000);
log4net.ILog log = log4net.LogManager.GetLogger(typeof(Program));
for (int i = 0; i < 1000;i++ )
{
log.Info("Hello world");
log.Debug("This is Debug");
log.Warn("This is Warn");
}
Well,
I found the answer. The code above is correct. Turns out there was some incompatibility between the client and server side of Log4net. Although I installed both from the same source (Nuget), something changed between the two references. After setting log4net's internal debugger property to true, I stumbled across this log message.
log4net:ERROR [RemotingAppender] ErrorCode: GenericFailure. Failed in SendBufferCallback
Googling it led me to here and this is where I suspected something was amiss. Long story short, removed all references to log4net in RemoteAPP and reinstalled via Nuget.
It works now. If someone knows how this occurred, please continue the discussion.

log4j isolating certain level from a class

I want to ask something on log4j. I have this config file for log4j on activemq. My problem is that I want to log all INFO level messages from every class I have, but I want to log all DEBUG level messages from "TransportConnection" class to a different file and, at the same time, log all only messages that are greater or equal to WARN level, to the rootLogger.
The problem with this configuration is that I log INFO level messages from "TransportConnection" class in the rootLogger. I want to pass only the WARN and above level to the rootLogger.
I dont want to set a Threshold to the "out" appender, because I want INFO level messages from other classes.
log4j.rootLogger=INFO,out
# Log these warnings
log4j.logger.org.apache.activemq.broker.BrokerRegistry=INFO
log4j.logger.org.apache.activemq.broker.TransportConnection=DEBUG,tc
# Standard logging
log4j.appender.out=org.apache.log4j.RollingFileAppender
log4j.appender.out.file=/var/lib/activemq/log/activemq.log
log4j.appender.out.maxFileSize=10240KB
log4j.appender.out.maxBackupIndex=100
log4j.appender.out.append=true
log4j.appender.out.layout=org.apache.log4j.PatternLayout
log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
# Transport Connections logging
log4j.appender.tc=org.apache.log4j.RollingFileAppender
log4j.appender.tc.file=/var/lib/activemq/log/tc.log
log4j.appender.tc.maxFileSize=10240KB
log4j.appender.tc.maxBackupIndex=100
log4j.appender.tc.append=true
log4j.appender.tc.layout=org.apache.log4j.PatternLayout
log4j.appender.tc.layout.ConversionPattern=%d [%t] %-5p %-30.30c{1} - %m%n
You could write your own custom filter. I suggest that implementation could be something like that:
public class MinLevelForParticularClassFilter extends Filter {
private boolean acceptOnMatch = false;
private Level level;
private String className;
#Override
public int decide(LoggingEvent event) {
if (this.className != null && this.level != null) {
if (event.getLocationInformation().getClassName().startsWith(className)) {
// this is event for specified class
if (!event.getLevel().isGreaterOrEqual(this.level)) {
// level of event is less than specified level
return Filter.DENY;
}
}
}
if (acceptOnMatch) {
return Filter.ACCEPT;
} else {
return Filter.NEUTRAL;
}
}
public boolean isAcceptOnMatch() { return acceptOnMatch; }
public void setAcceptOnMatch(boolean acceptOnMatch) { this.acceptOnMatch = acceptOnMatch; }
public Level getLevel() { return level; }
public void setLevel(Level level) { this.level = level; }
public String getClassName() { return className; }
public void setClassName(String className) { this.className = className; }
}
Note that if you change "className" variable to "packageName" variable, the implementation will be the same for filter by particular package.
The log4j configuration in XML (since filters are unsupported by configuration in property files):
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="out" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="activemq.log" />
<param name="MaxFileSize" value="10240KB" />
<param name="MaxBackupIndex" value="100" />
<param name="Append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%-15.15t] %-5p %-30.30c{1} - %m%n"/>
</layout>
<!-- Apply filter to appender that's destined for root logger -->
<filter class="com.foo.log4j.filters.MinLevelForParticularClassFilter">
<param name="Level" value="WARN" />
<param name="ClassName" value="org.apache.activemq.broker.TransportConnection" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
<appender name="tc" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="tc.log" />
<param name="MaxFileSize" value="10240KB" />
<param name="MaxBackupIndex" value="100" />
<param name="Append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %-30.30c{1} - %m%n"/>
</layout>
</appender>
<logger name="org.apache.activemq.broker.BrokerRegistry">
<level value="INFO" />
</logger>
<logger name="org.apache.activemq.broker.TransportConnection">
<level value="DEBUG" />
<appender-ref ref="tc" />
</logger>
<root>
<priority value ="INFO" />
<appender-ref ref="out" />
</root>
</log4j:configuration>

Is it possible to use Log4Net to write to log file within a CLR Stored Procedure?

Issue: Unable to debug (write) to a Log File using Log4net inside of a CLR Stored Procedure. Possibly a problem with the way I'm building the CLR project? I'm only importing the DLL's into sql server (create assembly....). Do I need to import the App.Config as well?
DLL Name:
CLRTest.dll
Source Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;
using log4net;
using log4net.Config;
public class MyClass
{
private readonly static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string sContextConn = "Context Connection=true";
[Microsoft.SqlServer.Server.SqlProcedure]
public static void Select1()
{
XmlConfigurator.Configure();
log.Debug("Begin Select1()...");
using (SqlConnection connection = new SqlConnection(sContextConn))
{
connection.Open();
SqlCommand command = new SqlCommand("select 1", connection);
SqlDataReader r = command.ExecuteReader();
SqlContext.Pipe.Send(r);
}
log.Debug("End Select1()...");
}
}//end MyClass
Log4Net XML Config (App.Config):
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="C:\log\clrsql.log" />
<param name="AppendToFile" value="true" />
<datePattern value="yyyyMMdd-HHmm" />
<param name="rollingStyle" value="Size" />
<param name="maxSizeRollBackups" value="50" />
<param name="maximumFileSize" value="25MB" />
<param name="staticLogFileName" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [t-%t] [%c.%M(%L)] %m%n" />
</layout>
</appender>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d %-5p [%c.%M(%L)] %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="RollingFileAppender" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
Permissions on SQL Server:
C:\log\ --
NETWORK SERVICE and MyDomain\sqlserveraccount have Full Control on the "log" folder.
SQL Scripts:
drop procedure clr_Select1
go
drop assembly CLRTest
go
create ASSEMBLY CLRTest FROM 'C:\Share\ClrSql\TEST\CLRTest.dll' WITH PERMISSION_SET = unsafe
go
CREATE PROCEDURE clr_Select1
AS EXTERNAL NAME CLRTest.MyClass.Select1
go
exec clr_Select1
SQL Output:
(No column name)
1
The log4net configurator call that you're making actually tries to find the AppDomain.CurrentDomain.SetupInformation.ConfigurationFile. I don't know what the AppDomain is for a stored procedure.
You might want to try a standalone configuration file and use the overload of that configurator.
log4net.Config.XmlConfigurator.Configure(new FileInfo("config.log4net"));

Resources