You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4net-dev@logging.apache.org by "Curt Arnold (JIRA)" <ji...@apache.org> on 2011/08/30 05:52:37 UTC

[jira] [Commented] (LOG4NET-306) Allowing the MethodLocationPatternConverter to skip certain methods from the call stack when trying to log the caller method (Solution for Log4Net wrappers)

    [ https://issues.apache.org/jira/browse/LOG4NET-306?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13093421#comment-13093421 ] 

Curt Arnold commented on LOG4NET-306:
-------------------------------------

FYI: log4j handles this by providing a special method specifically designed for wrappers to call and provide the class name for the wrapper. An equivalent 


public void log(String callerFQCN,
                Priority level,
                Object message,
                Throwable t)
This is the most generic printing method. It is intended to be invoked by wrapper classes.
Parameters:
callerFQCN - The wrapper class' fully qualified class name.
level - The level of the logging request.
message - The message of the logging request.
t - The throwable of the logging request, may be null.



The same approach seems to be available in log4net.Core.ILogger:


/// <summary>
		/// This generic form is intended to be used by wrappers.
		/// </summary>
		/// <param name="callerStackBoundaryDeclaringType">The declaring type of the method that is
		/// the stack boundary into the logging system for this call.</param>
		/// <param name="level">The level of the message to be logged.</param>
		/// <param name="message">The message object to log.</param>
		/// <param name="exception">the exception to log, including its stack trace. Pass <c>null</c> to not log an exception.</param>
		/// <remarks>
		/// <para>
		/// Generates a logging event for the specified <paramref name="level"/> using
		/// the <paramref name="message"/> and <paramref name="exception"/>.
		/// </para>
		/// </remarks>
		void Log(Type callerStackBoundaryDeclaringType, Level level, object message, Exception exception);

The log4net.Core.LogImpl makes extensive use of this call passing it is class name to mark the boundary between the logging code and the application code.

Is there a reason why this facility is inadequate?


> Allowing the MethodLocationPatternConverter to skip certain methods from the call stack when trying to log the caller method (Solution for Log4Net wrappers)
> ------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: LOG4NET-306
>                 URL: https://issues.apache.org/jira/browse/LOG4NET-306
>             Project: Log4net
>          Issue Type: New Feature
>          Components: Other
>    Affects Versions: 1.2.9, 1.2.10
>            Reporter: Jon Abaunza
>            Priority: Blocker
>
> There are several environments where it is required to develop a wrapper around Log4Net for backward compatibility with previous logging systems.
> The problem comes when it is required to obtain the method name. The system goes up in the StackTrace and identifies the wrapper method as the first user method and therefore logs the name of the wrapper instead of the real caller method.
> The solution would be as simple as creating a new MethodAtribute (lets say log4Net.SkipMethodForMethodLocation). I would be able to decorate my method with it and the MethodLocationPatternConverter would continue looking up in the stackTrace for the next user method.
> Modifications made (just modified the constructor)
> LocationInfo.cs
> public class LocationInfo
> {
>         	/// <summary>
> 	/// Constructor
> 	/// </summary>
> 	/// <param name="callerStackBoundaryDeclaringType">The declaring type of the method that is
> 	/// the stack boundary into the logging system for this call.</param>
> 	/// <remarks>
> 	/// <para>
> 	/// Initializes a new instance of the <see cref="LocationInfo" />
> 	/// class based on the current thread.
> 	/// </para>
> 	/// </remarks>
> 	public LocationInfo(Type callerStackBoundaryDeclaringType) 
> 	{
> 		// Initialize all fields
> 		m_className = NA;
> 		m_fileName = NA;
> 		m_lineNumber = NA;
> 		m_methodName = NA;
> 		m_fullInfo = NA;
> 		m_assembly = NA;
> #if !NETCF
> 		if (callerStackBoundaryDeclaringType != null)
> 		{
> 			try
> 			{
> 				StackTrace st = new StackTrace(true);
> 				int frameIndex = 0;
> 				// skip frames not from fqnOfCallingClass
> 				while (frameIndex < st.FrameCount)
> 				{
> 					StackFrame frame = st.GetFrame(frameIndex);
> 					if (frame != null && frame.GetMethod().DeclaringType == callerStackBoundaryDeclaringType)
> 					{
> 						break;
> 					}
> 					frameIndex++;
> 				}
> 				// skip frames from fqnOfCallingClass
> 				while (frameIndex < st.FrameCount)
> 				{
> 					StackFrame frame = st.GetFrame(frameIndex);
> 					if (frame != null && frame.GetMethod().DeclaringType != callerStackBoundaryDeclaringType)
> 					{
> 						break;
> 					}
> 					frameIndex++;
> 				}
> 				while (frameIndex < st.FrameCount)
> 				{
> 				             // now frameIndex is the first 'user' caller frame
> 					StackFrame locationFrame = st.GetFrame(frameIndex);
> 					if (locationFrame != null)
> 					{
> 						System.Reflection.MethodBase method = locationFrame.GetMethod();
> 					
> 						if (method != null)
> 						{
> 							bool skip = false;
>                                                                                                                 //NEW CHANGES:
>                                                                                                                 //We look for the new Custom attribute SkipMethodInMethodLocationAttribute
>                                                                                                                 //if the method is decorated with the attribute we will continue looking up in the stack
> 							object[] customAttributes = method.GetCustomAttributes(typeof(SkipMethodInMethodLocationAttribute), false);
> 							foreach (object customAttribute in customAttributes)
> 							{
> 								SkipMethodInMethodLocationAttribute skipMethodAttribute = customAttribute as SkipMethodInMethodLocationAttribute;
> 								if (skipMethodAttribute != null)
> 								{
> 									skip = skipMethodAttribute.Skip;
> 								}
> 							}
> 							//If the method was decorated with the attribute we continue looking up in the stack
> 							if (!skip)
> 							{
> 								m_methodName = method.Name;
> 								if (method.DeclaringType != null)
> 								{
> 									m_className = method.DeclaringType.FullName;
> 								}
> 								m_assembly = method.Module.Assembly.FullName;
> 								m_fileName = locationFrame.GetFileName();
> 								m_lineNumber = locationFrame.GetFileLineNumber().ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
> 								// Combine all location info
> 								m_fullInfo = m_className + '.' + m_methodName + '(' + m_fileName + ':' + m_lineNumber + ')';
> 								break;
> 							}
> 						}							
> 					}
> 					frameIndex++;
> 				}
> 			}
> 			catch(System.Security.SecurityException)
> 			{
> 				// This security exception will occur if the caller does not have 
> 				// some undefined set of SecurityPermission flags.
> 				LogLog.Debug("LocationInfo: Security exception while trying to get caller stack frame. Error Ignored. Location Information Not Available.");
> 			}
> 		}
> #endif
> 	}
> 	//.................End of modifications...............//
> }

--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira