Default values for AdoNetAppender parameter - log4net

I am using log4net with AdoNetAppender. It logs all log info into a table. This table actually has 2 Integer columns (can be null).
Here is the relevant part of my log4net config:
<commandText value="INSERT INTO ActivityLog ([Date],[Thread],[Level],[Logger],[Message],[DealID])
VALUES (#log_date,#thread,#log_level,#logger,#message,#DealID)" />
//other parameters hten DealID
<parameter>
<parameterName value="#DealID" />
<dbType value="Int32" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{DealID}" />
</layout>
</parameter>
What I found out was if I don't explicitly set using something like log4net.ThreadContext.Properties["DealID"] = DealID; it throws me an exception:
System.FormatException occurred
Message="Failed to convert parameter value from a String to a Int32."
Source="System.Data"
StackTrace:
at System.Data.SqlClient.SqlParameter.CoerceValue(Object value, MetaType destinationType)
InnerException: System.FormatException
Message="Input string was not in a correct format."
Source="mscorlib"
StackTrace:
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at System.String.System.IConvertible.ToInt32(IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at System.Data.SqlClient.SqlParameter.CoerceValue(Object value, MetaType destinationType)
InnerException:
I would have to set it like:
log4net.ThreadContext.Properties["DealID"] = 0;
Is there anyway that I can set a default parameter value in my log4net config for this Int32 field so that I don't need to set it explicitly to 0 if no value is supplied? And it makes me wonder why it does not happen to fields which are set as varchar (though no value is supplied to them).

Change your appender:
<parameter>
<parameterName value="#DealID" />
<dbType value="Int32" />
<layout type="log4net.Layout.RawPropertyLayout"> <!-- notice this -->
<key value="DealID" /> <!-- and notice this instead of the pattern layout -->
</layout>
</parameter>
And to give credit, I found it from this thread. (And a bunch of other searching trying to do the same thing.

Related

NLog throws an error when loading an XmlLayout configuration file

I am attempting to configure NLog to output error logs in an XML format, however, NLog is not properly loading the configuration.
Here is the LAYOUT portion of my configuration:
<layout xsi:type="XmlLayout" includeAllProperties="true" propertiesElementKeyAttribute="" propertiesElementName="{0}" elementName="errorlog">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}" />
<element name="processname" value="${processname}" />
<element name="logger" value="${logger}" />
<element name="message" value="${message}" />
</layout>
When calling the LogManager.GetCurrentClassLogger() method, the following error is generated:
Parameter name not supported on XmlLayout
Here are the additional details captured in the NLog error log file:
2019-03-04 16:25:39.9645 Trace Scanning Property Renderers 'System.Collections.ObjectModel.ReadOnlyCollection`1[NLog.LayoutRenderers.LayoutRenderer]' System.Collections.ObjectModel
2019-03-04 16:25:39.9645 Trace Scanning LevelLayoutRenderer 'Layout Renderer: ${level}'
2019-03-04 16:25:39.9645 Debug Setting 'XmlLayout.name' to 'processname'
2019-03-04 16:25:39.9835 Warn Error when setting 'processname' on attibute 'name' Exception: System.NotSupportedException: Parameter name not supported on XmlLayout
at NLog.Internal.PropertyHelper.SetPropertyFromString(Object obj, String propertyName, String value, ConfigurationItemFactory configurationItemFactory)
at NLog.Config.LoggingConfigurationParser.ConfigureObjectFromAttributes(Object targetObject, ILoggingConfigurationElement element, Boolean ignoreType)
2019-03-04 16:25:39.9835 Error Parsing configuration from C:\Users\<user>\Documents\Visual Studio 2017\Projects\TestingNlog\TestingNlog\bin\Debug\NLog.config failed. Exception: NLog.NLogConfigurationException: Exception when parsing C:\Users\<user>\Documents\Visual Studio 2017\Projects\TestingNlog\TestingNlog\bin\Debug\NLog.config. ---> System.NotSupportedException: Parameter name not supported on XmlLayout
If I remove the ELEMENT tags from the configuration file, I am able to generate a partial log file with just the single XML element ERRORLOG with the 2 specified attributes. However, I have not found a way to include the ELEMENT tags in my output log files.
NLog.dll - v4.6.0.9068
C# .NET - v4.7.2
Any ideas or assistance would be greatly appreciated!
-Kasey911
Snakefoot is correct,
this has been changed after NLog 4.6 RC 1
In NLog 4.6 RC 1
<layout xsi:type="XmlLayout" elementName='logevent'>
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<element elementName="message" elementValue="${message}" />
</layout>
In NLog 4.6 RC 2 (not released yet), and probably RTM
<layout xsi:type="XmlLayout" elementName='logevent'>
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<element name="message" value="${message}" />
</layout>

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
}
}

Questions regarding log4net's AdoNetAppender

I have log4net logging to my database after following a few examples and tutorials. A couple of things are unclear to me.
In the example on the log4net page it lists the following node in the configuration area.
<parameter>
<parameterName value="#exception" />
<dbType value="String" />
<size value="2000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
This parameter appears to pass an empty string to the database even when I log from within an exception.
Can someone clarify what should be logged to this field and when?
Also that example also has 'thread' being logged to the database as a varchar(255). What I get in that field is an integer. I'm not familiar with threads. Can I just store this as an int? Would this end up being a string in some cases?
<parameter>
<parameterName value="#thread" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%thread" />
</layout>
</parameter>
In order for exception to be populated, you need to use the correct overload
for example, if you use
catch (Exception ex)
{
log.Error("Error saving details" , ex);
...
}
then the exception column of your database will be set to ex.ToString()
If you use
log.Error("Error saving details " + ex.ToString());
then exception is not filled it
As for the thread identifier, this should be a string in your database. The default thread id is an integer, but you can explicitly name the thread,
eg
var ts = new System.Threading.ThreadStart(InitialiseDatabase);
var thread = new System.Threading.Thread(ts);
thread.Name = "Initialising Database";
It is a good idea to name your threads, as it makes filtering/indentifying much easier.

Log4Net Date and Time Without Leading Zeros (Conversion Pattern)

Does anyone know how to specify the date format so that leading zeros do not appear in the log? I know it sounds very picky, but another process is reading my logs and seems not to like leading zeros.
I am using:
<appender name="UdpAppender" type="log4net.Appender.UdpAppender">
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="NamespaceName.ClassName" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<param name="RemoteAddress" value="192.168.1.123" />
<param name="RemotePort" value="514" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date{MM/dd/yyyy hh:mm:ss tt} %message %newline" />
</layout>
</appender>
And I get this:
07/10/2012 09:20:39 AM ...
If possible, I'd like to get:
7/10/2012 9:20:39 AM
I tried simply using: %date{M/dd/yyyy h:mm:ss tt}
But it didn't work (and also seems odd for when two chars are needed)
Edit on 07/15/12:
I am not yet back at the office so I haven't checked on the UDP bug yet, but in case anyone else out there wants to extend an appender simply to add a property to pass into your app:
public class SyslogAppender : UdpAppender
{
public string UpdateIp { get; set; }
// ReSharper disable RedundantOverridenMember
override protected void Append(LoggingEvent loggingEvent)
{
base.Append(loggingEvent);
}
// ReSharper restore RedundantOverridenMember
}
<appender name="UdpAppender" type="Namespace.Log4Net.SyslogAppender">
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="Namespace.LogGenFileReparser" />
</filter>
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="Namespace.Log4Net.SyslogAppender" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<param name="RemoteAddress" value="192.168.73.133" />
<param name="RemotePort" value="514" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date{M/dd/yyyy h:mm:ss tt} %message %newline" />
</layout>
<param name="UpdateIp" value="false" />
</appender>
I check the new param before calling configure:
var log4FileConfigFile = XDocument.Parse(File.ReadAllText("log4net.config"));
var firstOrDefault = log4FileConfigFile.Descendants("log4net").Descendants("appender").
Select(appender => appender.Attributes().
FirstOrDefault(attrib => attrib.Name == "name" && attrib.Value == "UdpAppender")).
FirstOrDefault(check => null != check);
if (firstOrDefault != null)
if (firstOrDefault.Document != null)
{
var xAttribute2 = firstOrDefault.Document.Descendants("param").
Attributes().FirstOrDefault(attrib => attrib.Value == "UpdateIp");
if (xAttribute2 != null && xAttribute2.Value.ToLower().Equals("true"))
{
var xAttribute = firstOrDefault.Document.Descendants("param").
Attributes().FirstOrDefault(attrib => attrib.Value == "RemoteAddress");
if (xAttribute != null)
xAttribute.NextAttribute.SetValue(
IPAddresses.GetLocalIp().ToString());
}
}
XmlConfigurator.Configure(...
Edit 7/17/12
I had no other log4net error (with UDP appender). The problem was in the receiver.
Works for me but I'm logging to a file.
Are you sure whatever you are using the view the log isn't re-formatting the output?

AdoNetAppender and Clob field

I have a asp.net 3.5 app that is using log4Net and AdoNetAppender. Currently the app uses a message field just like the log4net documentation http://logging.apache.org/log4net/release/config-examples.html. I would like to convert the field from varchar2 to Clob. I was trying to find some documentation on how to do this. All I could find is:
http://old.nabble.com/DbType-for-CLOB-column-using-AdoNetAppender-td1214036.html#a1214036
which wasn't too useful. Does anyone know a link or some samples on how to use a Clob file with a AdoNetAppender?
Thanks, Bill N
Did you try this:
http://marc.info/?l=log4net-user&m=110874200319166
basically you need to set the DbType to string and remove the Size parameter. Apparently this does not work correctly for nvarchar(max) (see here) but that does not mean it will not work for Clob.
I know this is an old question, but I recently needed to pass a CLOB parameter to a package procedure, using log4net. I was not able to do that using the suggestions I found online, including the one with setting the DbType to String and removing Size.
I am using an Oracle package procedure that takes a parameter of type CLOB. Using a custom AdoNetAppenderParameter I am able to pass long strings (270k+ characters) to the procedure and store them in the DB (Oracle 9i).
First of all, I had to use Oracle's Data Access Provider (after all, Microsoft's System.Data.OracleClient has been deprecated). Your project must reference Oracle.DataAccess.dll. I got the NuGet package by searching for "oracle.dataaccess" in NuGet package manager.
The library has an implementation of DbParameter, OracleParameter, that has a OracleDbType property. The type of the property is OracleDbType which is an enumeration that has the Clob value.
After adding the reference, I changed the appender's connection type to:
<connectionType value="Oracle.DataAccess.Client.OracleConnection, Oracle.DataAccess, Version=2.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342" />
I then created a custom AdoNetAppenderParameter that creates a new OracleParameter and sets its type to Clob:
public class OracleAdoNetAppenderParameter : AdoNetAppenderParameter
{
public OracleDbType OracleDbType { get; set; }
public override void Prepare(System.Data.IDbCommand command)
{
if (!(command is OracleCommand))
{
string message = string.Format("The log4net parameter of type {0} can only be used with an appender connection of type {1}. The expected command type, {2}, cannot be supplied. Please check the parent appender's connectionType property.",
this.GetType(), typeof(OracleConnection), typeof(OracleCommand));
throw new System.ArgumentException(message, "command");
}
var parameter = command.CreateParameter() as OracleParameter;
parameter.ParameterName = base.ParameterName;
parameter.OracleDbType = this.OracleDbType;
command.Parameters.Add(parameter);
}
}
I exposed a property, OracleDbType, so I would be able to specify it through configuration.
I initially did not expose the property, named the class OracleClobAdoNetAppenderParameter and set the OracleDbType property to Clob inside the Prepare method.
After I created the class, I added the parameter to the appender's configuration, like this:
<parameter type="YourNamespace.OracleAdoNetAppenderParameter, YourAssembly">
<OracleDbType value="Clob" />
<parameterName value=":yourProcedureClobParam"/>
<layout type="..."></layout>
</parameter>
You can use your own layout. I am passing my large string through log4net's context as a custom parameter.
Here's the final configuration I'm using:
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<connectionType value="Oracle.DataAccess.Client.OracleConnection, Oracle.DataAccess, Version=2.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342" />
<connectionString value="data source=xxx;User ID=xxx;Password=xxx"/>
<commandText value="MY_PKG.LogMessage"/>
<commandType value="StoredProcedure" />
<!-- SERVICE_MESSAGE -->
<parameter type="MyNamespace.OracleAdoNetAppenderParameter, MyAssembly">
<OracleDbType value="Clob" />
<parameterName value=":service_message"/>
<layout type="log4net.Layout.RawPropertyLayout">
<key value="service_message"/>
</layout>
</parameter>
<!-- LOG_LEVEL -->
<parameter>
<parameterName value=":type"/>
<dbType value="String"/>
<size value="20"/>
<layout type="log4net.Layout.PatternLayout" value="%level"/>
</parameter>
<!-- LOG_DATE -->
<parameter>
<parameterName value=":timestamp"/>
<dbType value="DateTime"/>
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<!-- MESSAGE -->
<parameter>
<parameterName value=":message"/>
<dbType value="String"/>
<size value="1000"/>
<layout type="log4net.Layout.PatternLayout" value="%message"/>
</parameter>
<!-- EXCEPTION -->
<parameter>
<parameterName value=":error"/>
<dbType value="String"/>
<size value="4000"/>
<layout type="log4net.Layout.ExceptionLayout"/>
</parameter>
......
</appender>
Hope it helps.

Resources