You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by dp...@apache.org on 2017/05/25 21:27:59 UTC

[1/4] logging-log4net git commit: First version of ProcMonAppender

Repository: logging-log4net
Updated Branches:
  refs/heads/develop [created] f5fdf89fd
  refs/heads/pr/old/10 [created] b4bc43eed
  refs/heads/pr/old/22 [created] 675e7cba2
  refs/heads/pr/old/7 [created] 3c930d8d9
  refs/heads/pr/old/9 [created] f33118151
Updated Tags:  refs/tags/1.2.13RC1 [created] 6dc3da926


First version of ProcMonAppender


Project: http://git-wip-us.apache.org/repos/asf/logging-log4net/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4net/commit/f3311815
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4net/tree/f3311815
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4net/diff/f3311815

Branch: refs/heads/pr/old/9
Commit: f331181519b36aebf9203bead1cce6e57c58b373
Parents: 610157a
Author: Justin Dearing <zi...@gmail.com>
Authored: Thu Mar 20 19:12:19 2014 -0400
Committer: Dominik Psenner <dp...@apache.org>
Committed: Wed May 24 23:25:47 2017 +0200

----------------------------------------------------------------------
 src/log4net/Appender/ProcMonAppender.cs | 170 +++++++++++++++++++++++++++
 src/log4net/log4net.vs2010.csproj       |   5 +-
 2 files changed, 173 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4net/blob/f3311815/src/log4net/Appender/ProcMonAppender.cs
----------------------------------------------------------------------
diff --git a/src/log4net/Appender/ProcMonAppender.cs b/src/log4net/Appender/ProcMonAppender.cs
new file mode 100644
index 0000000..cfd8af6
--- /dev/null
+++ b/src/log4net/Appender/ProcMonAppender.cs
@@ -0,0 +1,170 @@
+#region Apache License
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more 
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership. 
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with 
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using log4net.Core;
+using Microsoft.Win32.SafeHandles;
+
+namespace log4net.Appender
+{
+    /// <summary>
+    /// Appends log events to Sysinternals Process Monitor.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// The application configuration file can be used to control what listeners 
+    /// are actually used.
+    /// </para>
+    /// <para>
+    /// Events are written using a write only file handle that procmon listens for. Process Monitor will then display these messages amongst the IO data.
+    /// </para>
+    /// </remarks>
+    /// <author>Justin Dearing</author>
+    /// <seealso cref="http://www.wintellect.com/blogs/jrobbins/see-the-i-o-you-caused-by-getting-your-diagnostic-tracing-into-process-monitor"/>
+    /// <seealso cref="http://www.wintellect.com/blogs/jrobbins/procmondebugoutput-now-on-github"/>
+    /// <seealso cref="https://github.com/Wintellect/ProcMonDebugOutput"/>
+    public class ProcMonAppender : AppenderSkeleton
+    {
+        // Constants to represent C preprocessor macros for PInvokes
+        const uint GENERIC_WRITE = 0x40000000;
+        const uint OPEN_EXISTING = 3;
+        const uint FILE_WRITE_ACCESS = 0x0002;
+        const uint FILE_SHARE_WRITE = 0x00000002;
+        const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
+        const uint METHOD_BUFFERED = 0;
+
+        // Procmon Constants 
+        const uint FILE_DEVICE_PROCMON_LOG = 0x00009535;
+        const string PROCMON_DEBUGGER_HANDLER = "\\\\.\\Global\\ProcmonDebugLogger";
+
+        /// <summary>
+        /// The handle to the procmon log device.
+        /// </summary>
+        private static SafeFileHandle hProcMon;
+
+        /// <summary>
+        /// Get the IO Control code for the ProcMon log.
+        /// </summary>
+        private static uint IOCTL_EXTERNAL_LOG_DEBUGOUT { get { return CTL_CODE(); } }
+
+        /// <seealso href="http://msdn.microsoft.com/en-us/library/windows/hardware/ff543023(v=vs.85).aspx"/>
+        private static uint CTL_CODE(
+            uint DeviceType = FILE_DEVICE_PROCMON_LOG,
+            uint Function = 0x81,
+            uint Method = METHOD_BUFFERED,
+            uint Access = FILE_WRITE_ACCESS)
+        {
+            return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method);
+        }
+
+        /// <remarks>This is only used for opening the procmon log handle, hence the default parameters.</remarks>
+        /// <seealso href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx"/>
+        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+        private static extern SafeFileHandle CreateFile(
+            string lpFileName = PROCMON_DEBUGGER_HANDLER,
+            uint dwDesiredAccess = GENERIC_WRITE,
+            uint dwShareMode = FILE_SHARE_WRITE,
+            IntPtr lpSecurityAttributes = default(IntPtr),
+            uint dwCreationDisposition = OPEN_EXISTING,
+            uint dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL,
+            IntPtr hTemplateFile = default(IntPtr));
+
+        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
+        private static extern bool DeviceIoControl(
+            SafeFileHandle hDevice, uint dwIoControlCode,
+            StringBuilder lpInBuffer, uint nInBufferSize,
+            IntPtr lpOutBuffer, uint nOutBufferSize,
+            out uint lpBytesReturned, IntPtr lpOverlapped);
+
+
+        static ProcMonAppender()
+        {
+            AppDomain.CurrentDomain.ProcessExit += (sender, args) =>
+            {
+                if (!hProcMon.IsInvalid) hProcMon.Close();
+            };
+        }
+
+        /// <summary>
+        /// Does the actual tracing to Process Monitor.
+        /// </summary>
+        /// <param name="message">
+        /// The message to display.
+        /// </param>
+        /// <param name="args">
+        /// The formatting arguments for the message
+        /// </param>
+        /// <returns>
+        /// True if the trace succeeded, false otherwise.
+        /// </returns>
+        private static bool ProcMonDebugOutput(string message, params object[] args)
+        {
+            bool returnValue = false;
+            try
+            {
+                StringBuilder renderedMessage = new StringBuilder(); 
+                renderedMessage.AppendFormat(message, args);
+                uint outLen;
+                if (hProcMon == null || hProcMon.IsInvalid)
+                {
+                    hProcMon = CreateFile();
+                }
+                DeviceIoControl(
+                    hProcMon, IOCTL_EXTERNAL_LOG_DEBUGOUT,
+                    renderedMessage, (uint)(message.Length * Marshal.SizeOf(typeof(char))),
+                    IntPtr.Zero, 0, out outLen, IntPtr.Zero);
+            }
+            catch (EntryPointNotFoundException notFoundException)
+            {
+                // This means the appropriate ProcMonDebugOutput[Win32|x64].DLL
+                // file could not be found. I'll eat this exception so it does
+                // not take down the application.
+                Debug.WriteLine(notFoundException.Message);
+            }
+
+            return returnValue;
+        }
+
+        /// <summary>
+        /// This appender requires a <see cref="AppenderSkeleton.Layout"/> to be set.
+        /// </summary>
+        /// <value><c>true</c></value>
+        override protected bool RequiresLayout
+        {
+            get { return true; }
+        }
+
+        /// <summary>
+        /// Writes the logging event to Sysinternals Process Monitor.
+        /// </summary>
+        /// <param name="loggingEvent">The event to log.</param>
+        /// <remarks>
+        /// <para>
+        /// Writes the logging event to Sysinternals Process Monitor.
+        /// </para>
+        /// </remarks>
+        protected override void Append(LoggingEvent loggingEvent)
+        {
+            throw new System.NotImplementedException();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4net/blob/f3311815/src/log4net/log4net.vs2010.csproj
----------------------------------------------------------------------
diff --git a/src/log4net/log4net.vs2010.csproj b/src/log4net/log4net.vs2010.csproj
index 45e2c58..8c6a7a5 100644
--- a/src/log4net/log4net.vs2010.csproj
+++ b/src/log4net/log4net.vs2010.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <!--
 
  Licensed to the Apache Software Foundation (ASF) under one
@@ -179,6 +179,7 @@
     <Compile Include="Appender\OutputDebugStringAppender.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="Appender\ProcMonAppender.cs" />
     <Compile Include="Appender\RemoteSyslogAppender.cs">
       <SubType>Code</SubType>
     </Compile>
@@ -793,4 +794,4 @@
     <PostBuildEvent>
     </PostBuildEvent>
   </PropertyGroup>
-</Project>
+</Project>
\ No newline at end of file


[4/4] logging-log4net git commit: Only look at relevant log files for backup size at initialization time.

Posted by dp...@apache.org.
Only look at relevant log files for backup size at initialization time.


Project: http://git-wip-us.apache.org/repos/asf/logging-log4net/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4net/commit/675e7cba
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4net/tree/675e7cba
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4net/diff/675e7cba

Branch: refs/heads/pr/old/22
Commit: 675e7cba232433f99e3ab0800e59c6740d6ecb37
Parents: 655233d
Author: Sotirios Delimanolis <se...@yahoo-inc.com>
Authored: Fri Jan 29 10:06:25 2016 -0800
Committer: Dominik Psenner <dp...@apache.org>
Committed: Thu May 25 23:16:48 2017 +0200

----------------------------------------------------------------------
 src/Appender/RollingFileAppender.cs | 149 +++++++++++++------------------
 1 file changed, 62 insertions(+), 87 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4net/blob/675e7cba/src/Appender/RollingFileAppender.cs
----------------------------------------------------------------------
diff --git a/src/Appender/RollingFileAppender.cs b/src/Appender/RollingFileAppender.cs
index c1b7705..3068ce7 100644
--- a/src/Appender/RollingFileAppender.cs
+++ b/src/Appender/RollingFileAppender.cs
@@ -867,100 +867,75 @@ namespace log4net.Appender
 			}
 		}
 
-		/// <summary>
-		/// Does the work of bumping the 'current' file counter higher
-		/// to the highest count when an incremental file name is seen.
-		/// The highest count is either the first file (when count direction
-		/// is greater than 0) or the last file (when count direction less than 0).
-		/// In either case, we want to know the highest count that is present.
-		/// </summary>
-		/// <param name="baseFile"></param>
-		/// <param name="curFileName"></param>
-		private void InitializeFromOneFile(string baseFile, string curFileName)
-		{
-            if (curFileName.StartsWith(Path.GetFileNameWithoutExtension(baseFile)) == false)
-			{
-				// This is not a log file, so ignore
-				return;
-			}
-			if (curFileName.Equals(baseFile)) 
-			{
-				// Base log file is not an incremented logfile (.1 or .2, etc)
-				return;
-			}
-	
-            /*
-			if (m_staticLogFileName) 
-			{
-				int endLength = curFileName.Length - index;
-				if (baseFile.Length + endLength != curFileName.Length) 
-				{
-					// file is probably scheduledFilename + .x so I don't care
-					return;
-				}
-			}
-            */
-	
-			// Only look for files in the current roll point
-			if (m_rollDate && !m_staticLogFileName)
-			{
-				string date = m_dateTime.Now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo);
-				string prefix = m_preserveLogFileNameExtension ? Path.GetFileNameWithoutExtension(baseFile) + date : baseFile + date;
-				string suffix = m_preserveLogFileNameExtension ? Path.GetExtension(baseFile) : "";
-				if (!curFileName.StartsWith(prefix) || !curFileName.EndsWith(suffix))
-				{
-					LogLog.Debug(declaringType, "Ignoring file ["+curFileName+"] because it is from a different date period");
-					return;
-				}
-			}
-            
-			try 
-			{
-				// Bump the counter up to the highest count seen so far
-                int backup = GetBackUpIndex(curFileName);
-                
-                // caution: we might get a false positive when certain
-                // date patterns such as yyyyMMdd are used...those are
-                // valid number but aren't the kind of back up index
-                // we're looking for
-                if (backup > m_curSizeRollBackups)
-                {
-                    if (0 == m_maxSizeRollBackups)
-                    {
-                        // Stay at zero when zero backups are desired
-                    }
-                    else if (-1 == m_maxSizeRollBackups)
-                    {
-                        // Infinite backups, so go as high as the highest value
-                        m_curSizeRollBackups = backup;
+        /// <summary>
+        /// Does the work of bumping the 'current' file counter higher
+        /// to the highest count when an incremental file name is seen.
+        /// The highest count is either the first file (when count direction
+        /// is greater than 0) or the last file (when count direction less than 0).
+        /// In either case, we want to know the highest count that is present.
+        /// </summary>
+        /// <param name="origBaseFile"></param>
+        /// <param name="origCurFileName"></param>
+        private void InitializeFromOneFile(string origBaseFile, string origCurFileName)
+        {
+            // keep originals for logging
+            string baseFile = origBaseFile;
+            string curFileName = origCurFileName;
+
+            if (m_preserveLogFileNameExtension) {
+                curFileName = Path.GetFileNameWithoutExtension (curFileName);
+                baseFile = Path.GetFileNameWithoutExtension (baseFile);
+            }
+
+            if (!m_staticLogFileName) {
+                // Generate expected target file name
+                string date = m_dateTime.Now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo);
+                baseFile = baseFile + date;
+
+                // Only look for files in the current roll point
+                if (m_rollDate) {
+                    if (!origCurFileName.StartsWith (baseFile) || (m_preserveLogFileNameExtension && !origCurFileName.EndsWith (Path.GetExtension (origBaseFile)))) {
+                        LogLog.Debug (declaringType, "Ignoring file [" + origCurFileName + "] because it is from a different date period");
+                        return;
                     }
-                    else
-                    {
-                        // Backups limited to a finite number
-                        if (m_countDirection >= 0)
-                        {
-                            // Go with the highest file when counting up
+                }
+            }
+
+            // remove baseFile prefix
+            string datePatternOrBackup = curFileName.Substring (baseFile.Length);
+
+            if (datePatternOrBackup.Length == 0) {
+                LogLog.Debug (declaringType, "Encountered the base file [" + origCurFileName + "]");
+            } else if (datePatternOrBackup [0] != '.') {
+                LogLog.Debug (declaringType, "Encountered a file that definitely isn't a size backup for the current log file [" + origCurFileName + "]");
+            } else {
+                int backup;
+                if (!int.TryParse (datePatternOrBackup.Substring (1), out backup)) {
+                    LogLog.Debug (declaringType, "Encountered a backup file not ending in .N [" + origCurFileName + "]");
+                } else {
+                    if (backup > m_curSizeRollBackups) {
+                        if (0 == m_maxSizeRollBackups) {
+                            // Stay at zero when zero backups are desired
+                        } else if (-1 == m_maxSizeRollBackups) {
+                            // Infinite backups, so go as high as the highest value
                             m_curSizeRollBackups = backup;
-                        }
-                        else
-                        {
-                            // Clip to the limit when counting down
-                            if (backup <= m_maxSizeRollBackups)
-                            {
+                        } else {
+                            // Backups limited to a finite number
+                            if (m_countDirection >= 0) {
+                                // Go with the highest file when counting up
                                 m_curSizeRollBackups = backup;
+                            } else {
+                                // Clip to the limit when counting down
+                                if (backup <= m_maxSizeRollBackups) {
+                                    m_curSizeRollBackups = backup;
+                                }
                             }
                         }
+                        LogLog.Debug (declaringType, "File name [" + origCurFileName + "] moves current count to [" + m_curSizeRollBackups + "]");
                     }
-                    LogLog.Debug(declaringType, "File name [" + curFileName + "] moves current count to [" + m_curSizeRollBackups + "]");
                 }
-			} 
-			catch(FormatException) 
-			{
-				//this happens when file.log -> file.log.yyyy-MM-dd which is normal
-				//when staticLogFileName == false
-				LogLog.Debug(declaringType, "Encountered a backup file not ending in .x ["+curFileName+"]");
-			}
-		}
+            }
+        }
 
         /// <summary>
         /// Attempts to extract a number from the end of the file name that indicates


[2/4] logging-log4net git commit: add ColoredSmtpAppender. allow user send colorful log email.

Posted by dp...@apache.org.
add ColoredSmtpAppender. allow user send colorful log email.


Project: http://git-wip-us.apache.org/repos/asf/logging-log4net/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4net/commit/3c930d8d
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4net/tree/3c930d8d
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4net/diff/3c930d8d

Branch: refs/heads/pr/old/7
Commit: 3c930d8d9b35cbfe1ba3e7ad93ff888d71f94f01
Parents: b584368
Author: Devin <st...@gmail.com>
Authored: Fri Dec 13 09:35:41 2013 -0800
Committer: Dominik Psenner <dp...@apache.org>
Committed: Wed May 24 23:26:39 2017 +0200

----------------------------------------------------------------------
 src/log4net/Appender/ColoredSmtpAppender.cs | 315 +++++++++++++++++++++++
 src/log4net/log4net.vs2010.csproj           |   7 +-
 2 files changed, 320 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4net/blob/3c930d8d/src/log4net/Appender/ColoredSmtpAppender.cs
----------------------------------------------------------------------
diff --git a/src/log4net/Appender/ColoredSmtpAppender.cs b/src/log4net/Appender/ColoredSmtpAppender.cs
new file mode 100644
index 0000000..d9a8573
--- /dev/null
+++ b/src/log4net/Appender/ColoredSmtpAppender.cs
@@ -0,0 +1,315 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.Mail;
+using System.Text;
+using System.Web;
+using log4net.Appender;
+using log4net.Core;
+using log4net.Util;
+
+namespace log4net.Appender
+{
+    /// <summary>
+    /// Appends colorful logging events to the email html content
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// When configuring the colored console appender, mappings should be
+    /// specified to map logging levels to colors and css styles. For example:
+    /// </para>
+    /// <code lang="XML" escaped="true">
+    ///	<mapping>
+    ///		<level value="ERROR" />
+    ///		<foreColor value="Red" />
+    ///		<backColor value="White" />
+    ///     <style value="font-size: larger;font-weight: bold" />
+    ///	</mapping>
+    ///	<mapping>
+    ///		<level value="WARN" />
+    ///		<foreColor value="#FFFF00" />
+    ///	</mapping>
+    ///	<mapping>
+    ///		<level value="INFO" />
+    ///		<foreColor value="#000" />
+    ///	</mapping>
+    ///	<mapping>
+    ///		<level value="DEBUG" />
+    ///		<foreColor value="#eee" />
+    ///	</mapping>
+    /// </code>
+    /// <para>
+    /// The Level is the standard log4net logging level while
+    /// ForeColor and BackColor are the values of CSS color value
+    /// Style is the CSS style.
+    /// </para>
+    /// </remarks>
+    /// <author>Devin Zeng</author>
+    public class ColoredSmtpAppender : SmtpAppender
+    {
+        private LevelMapping m_levelMapping = new LevelMapping();
+
+        protected override void SendBuffer(LoggingEvent[] events)
+        {
+            // Note: this code already owns the monitor for this
+            // appender. This frees us from needing to synchronize again.
+            try
+            {
+                StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
+
+                string t = Layout.Header;
+                if (t != null)
+                {
+                    writer.Write(t);
+                }
+
+                for (int i = 0; i < events.Length; i++)
+                {
+                    // Render the event and append the text to the buffer
+                    RenderColoredLoggingEvent(writer, events[i]);
+                }
+
+                t = Layout.Footer;
+                if (t != null)
+                {
+                    writer.Write(t);
+                }
+
+                SendEmail(writer.ToString());
+            }
+            catch (Exception e)
+            {
+                ErrorHandler.Error("Error occurred while sending e-mail notification.", e);
+            }
+        }
+        /// <summary>
+        /// Based on loggingEvent configration, render the html formatted log content.
+        /// </summary>
+        /// <param name="writer"></param>
+        /// <param name="loggingEvent"></param>
+        protected void RenderColoredLoggingEvent(TextWriter writer, LoggingEvent loggingEvent)
+        {
+            if (Layout == null)
+            {
+                throw new InvalidOperationException("A layout must be set");
+            }
+            if (Layout.IgnoresException)
+            {
+                string exceptionStr = loggingEvent.GetExceptionString();
+                if (exceptionStr != null && exceptionStr.Length > 0)
+                {
+                    writer.WriteLine(HtmlMessage(exceptionStr));
+                }
+            }
+            writer.WriteLine(BuildHtmlLoggingMessage(loggingEvent));
+
+        }
+        /// <summary>
+        /// Build loggingEvent html format message
+        /// </summary>
+        /// <param name="loggingEvent"></param>
+        /// <returns>Log content</returns>
+        private string BuildHtmlLoggingMessage(LoggingEvent loggingEvent)
+        {
+            StringBuilder style = new StringBuilder();
+            LevelColors levelColors = m_levelMapping.Lookup(loggingEvent.Level) as LevelColors;
+            if (levelColors != null)
+            {
+                // if the backColor has been explicitly set
+                if (levelColors.HasBackColor)
+                {
+                    style.Append("background-color:" + levelColors.BackColor + ";");
+                }
+                // if the foreColor has been explicitly set
+                if (levelColors.HasForeColor)
+                {
+                    style.Append("color:" + levelColors.ForeColor + ";");
+                }
+                if (!String.IsNullOrEmpty(levelColors.Style))
+                {
+                    style.Append(levelColors.Style);
+                }
+            }
+            var css = style.ToString();
+            string strLoggingMessage = RenderLoggingEvent(loggingEvent);
+            return HtmlMessage(strLoggingMessage, css);
+        }
+
+        private string HtmlMessage(string strLoggingMessage, string css = "")
+        {
+            return "<span style='white-space:pre;" + css + "'>" + HttpUtility.HtmlEncode(strLoggingMessage) + "</span>";
+        }
+
+        /// <summary>
+        /// Allow HTML content
+        /// </summary>
+        /// <param name="messageBody"></param>
+        protected override void SendEmail(string messageBody)
+        {
+            // .NET 2.0 has a new API for SMTP email System.Net.Mail
+            // This API supports credentials and multiple hosts correctly.
+            // The old API is deprecated.
+
+            // Create and configure the smtp client
+            SmtpClient smtpClient = new SmtpClient();
+            if (!String.IsNullOrEmpty(SmtpHost))
+            {
+                smtpClient.Host = SmtpHost;
+            }
+            smtpClient.Port = Port;
+            smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
+            smtpClient.EnableSsl = EnableSsl;
+
+            if (Authentication == SmtpAuthentication.Basic)
+            {
+                // Perform basic authentication
+                smtpClient.Credentials = new System.Net.NetworkCredential(Username, Password);
+            }
+            else if (Authentication == SmtpAuthentication.Ntlm)
+            {
+                // Perform integrated authentication (NTLM)
+                smtpClient.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
+            }
+
+            using (MailMessage mailMessage = new MailMessage())
+            {
+                mailMessage.Body = messageBody;
+                mailMessage.IsBodyHtml = true;
+                mailMessage.BodyEncoding = BodyEncoding;
+                mailMessage.From = new MailAddress(From);
+                mailMessage.To.Add(To);
+                if (!String.IsNullOrEmpty(Cc))
+                {
+                    mailMessage.CC.Add(Cc);
+                }
+                if (!String.IsNullOrEmpty(Bcc))
+                {
+                    mailMessage.Bcc.Add(Bcc);
+                }
+                if (!String.IsNullOrEmpty(ReplyTo))
+                {
+                    // .NET 4.0 warning CS0618: 'System.Net.Mail.MailMessage.ReplyTo' is obsolete:
+                    // 'ReplyTo is obsoleted for this type.  Please use ReplyToList instead which can accept multiple addresses. http://go.microsoft.com/fwlink/?linkid=14202'
+#if !FRAMEWORK_4_0_OR_ABOVE
+                    mailMessage.ReplyTo = new MailAddress(ReplyTo);
+#else
+                    mailMessage.ReplyToList.Add(new MailAddress(ReplyTo));
+#endif
+                }
+                mailMessage.Subject = Subject;
+                mailMessage.SubjectEncoding = SubjectEncoding;
+                mailMessage.Priority = Priority;
+
+                // TODO: Consider using SendAsync to send the message without blocking. This would be a change in
+                // behaviour compared to .NET 1.x. We would need a SendCompletedCallback to log errors.
+                smtpClient.Send(mailMessage);
+            }
+        }
+
+        /// <summary>
+        /// Add a mapping of level to color - done by the config file
+        /// </summary>
+        /// <param name="mapping">The mapping to add</param>
+        /// <remarks>
+        /// <para>
+        /// Add a <see cref="LevelColors"/> mapping to this appender.
+        /// Each mapping defines the foreground and background colors
+        /// for a level.
+        /// </para>
+        /// </remarks>
+        public void AddMapping(LevelColors mapping)
+        {
+            m_levelMapping.Add(mapping);
+        }
+
+        /// <summary>
+        /// Initialize the options for this appender
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// Initialize the level to color mappings set on this appender.
+        /// </para>
+        /// </remarks>
+        public override void ActivateOptions()
+        {
+            base.ActivateOptions();
+            m_levelMapping.ActivateOptions();
+        }
+
+        /// <summary>
+        /// Copy from ManagedColoredConsoleAppender, add Style propertity
+        /// </summary>
+        public class LevelColors : LevelMappingEntry
+        {
+            /// <summary>
+            /// The mapped foreground color for the specified level
+            /// </summary>
+            /// <remarks>
+            /// <para>
+            /// Required property.
+            /// The mapped foreground color for the specified level.
+            /// </para>
+            /// </remarks>
+            public string ForeColor
+            {
+                get { return (this.foreColor); }
+                // Keep a flag that the color has been set
+                // and is no longer the default.
+                set
+                {
+                    this.foreColor = value;
+                    this.hasForeColor = true;
+                }
+            }
+
+            private string foreColor;
+            private bool hasForeColor;
+
+            internal bool HasForeColor
+            {
+                get
+                {
+                    return hasForeColor;
+                }
+            }
+            /// <summary>
+            /// CSS style
+            /// </summary>
+            public string Style
+            { get; set; }
+
+            /// <summary>
+            /// The mapped background color for the specified level
+            /// </summary>
+            /// <remarks>
+            /// <para>
+            /// Required property.
+            /// The mapped background color for the specified level.
+            /// </para>
+            /// </remarks>
+            public string BackColor
+            {
+                get { return (this.backColor); }
+                // Keep a flag that the color has been set
+                // and is no longer the default.
+                set
+                {
+                    this.backColor = value;
+                    this.hasBackColor = true;
+                }
+            }
+
+            private string backColor;
+            private bool hasBackColor;
+
+            internal bool HasBackColor
+            {
+                get
+                {
+                    return hasBackColor;
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4net/blob/3c930d8d/src/log4net/log4net.vs2010.csproj
----------------------------------------------------------------------
diff --git a/src/log4net/log4net.vs2010.csproj b/src/log4net/log4net.vs2010.csproj
index 45e2c58..569ec4c 100644
--- a/src/log4net/log4net.vs2010.csproj
+++ b/src/log4net/log4net.vs2010.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <!--
 
  Licensed to the Apache Software Foundation (ASF) under one
@@ -145,6 +145,9 @@
     <Compile Include="Appender\ColoredConsoleAppender.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="Appender\ColoredSmtpAppender.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="Appender\ConsoleAppender.cs">
       <SubType>Code</SubType>
     </Compile>
@@ -793,4 +796,4 @@
     <PostBuildEvent>
     </PostBuildEvent>
   </PropertyGroup>
-</Project>
+</Project>
\ No newline at end of file


[3/4] logging-log4net git commit: Support for types of non-parameterless constructors in XmlConfigurator.

Posted by dp...@apache.org.
Support for types of non-parameterless constructors in XmlConfigurator.

The constructor parameters are taken from the corresponding values of
the child nodes.

You can add for instance

<standardunit type="Amazon.CloudWatch.StandardUnit">
<value value="Kilobytes"/>
</standardunit>

where StandardUnit has only one constructor that has a string parameter
named "value".


Project: http://git-wip-us.apache.org/repos/asf/logging-log4net/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4net/commit/b4bc43ee
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4net/tree/b4bc43ee
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4net/diff/b4bc43ee

Branch: refs/heads/pr/old/10
Commit: b4bc43eed24d1152772141652d68002f396c615d
Parents: 610157a
Author: Martin Camitz <ma...@gmail.com>
Authored: Tue Apr 22 15:36:29 2014 +0200
Committer: Dominik Psenner <dp...@apache.org>
Committed: Thu May 25 00:06:48 2017 +0200

----------------------------------------------------------------------
 .../Hierarchy/XmlHierarchyConfigurator.cs       | 54 ++++++++++++++++++--
 1 file changed, 50 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4net/blob/b4bc43ee/src/log4net/Repository/Hierarchy/XmlHierarchyConfigurator.cs
----------------------------------------------------------------------
diff --git a/src/log4net/Repository/Hierarchy/XmlHierarchyConfigurator.cs b/src/log4net/Repository/Hierarchy/XmlHierarchyConfigurator.cs
index ae415d7..a251b47 100644
--- a/src/log4net/Repository/Hierarchy/XmlHierarchyConfigurator.cs
+++ b/src/log4net/Repository/Hierarchy/XmlHierarchyConfigurator.cs
@@ -19,7 +19,9 @@
 
 using System;
 using System.Collections;
+using System.Collections.Generic;
 using System.Globalization;
+using System.Linq;
 using System.Reflection;
 using System.Xml;
 
@@ -986,13 +988,57 @@ namespace log4net.Repository.Hierarchy
 
 			// Create using the default constructor
 			object createdObject = null;
-			try
+
+			//Try parameterless constructor
+			if (objectType.GetConstructor(Type.EmptyTypes) != null)
 			{
-				createdObject = Activator.CreateInstance(objectType);
+				try
+				{
+					createdObject = Activator.CreateInstance(objectType);
+				}
+				catch (Exception createInstanceEx)
+				{
+					LogLog.Error(declaringType,
+						"XmlHierarchyConfigurator: Failed to construct object of type [" + objectType.FullName +
+						"] Exception: " + createInstanceEx.ToString());
+				}
+
 			}
-			catch(Exception createInstanceEx)
+			else
 			{
-				LogLog.Error(declaringType, "XmlHierarchyConfigurator: Failed to construct object of type [" + objectType.FullName + "] Exception: "+createInstanceEx.ToString());
+				//See if there is a constructor matching supplied values, most specific first.
+				Exception lastException = null;
+				foreach (var constructorInfo in objectType.GetConstructors().OrderByDescending(x => x.GetParameters().Count()))
+				{
+
+					try
+					{
+						var nodes = new List<XmlNode>(element.ChildNodes.Cast<XmlNode>());
+						var parameterInfos = constructorInfo.GetParameters();
+						LogLog.Debug(declaringType, "Trying constructor with parameters: " + string.Join(",", parameterInfos.Select(p => p.Name)));
+
+						var args = parameterInfos.Select(p => nodes.SingleOrDefault(n => n.Name == p.Name));
+						LogLog.Debug(declaringType, "Matching args from config: " + string.Join(",", args.Select(n => n.Name)));
+						LogLog.Debug(declaringType, "Matching arg values from config: " + string.Join(",", args.Select(a => a.Attributes["value"].Value)));
+						if (args.All(a => a != null))
+						{
+							createdObject = constructorInfo.Invoke(args.Select(a => a.Attributes["value"].Value).ToArray());
+							break;
+						}
+					}
+					catch (Exception e)
+					{
+						lastException = e;
+					}
+				}
+
+				if (createdObject == null)
+				{
+					LogLog.Error(declaringType,
+					   "XmlHierarchyConfigurator: Failed to construct object of type [" + objectType.FullName +
+					   "] No matching constructor found. LastException: " +
+					   (lastException != null ? lastException.ToString() : "None caught."));
+				}
 			}
 
 			// Set any params on object