LocationInfo.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. #region Apache License
  2. //
  3. // Licensed to the Apache Software Foundation (ASF) under one or more
  4. // contributor license agreements. See the NOTICE file distributed with
  5. // this work for additional information regarding copyright ownership.
  6. // The ASF licenses this file to you under the Apache License, Version 2.0
  7. // (the "License"); you may not use this file except in compliance with
  8. // the License. You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. #endregion
  19. using System;
  20. using System.Collections;
  21. using System.Diagnostics;
  22. using log4net.Util;
  23. namespace log4net.Core
  24. {
  25. /// <summary>
  26. /// The internal representation of caller location information.
  27. /// </summary>
  28. /// <remarks>
  29. /// <para>
  30. /// This class uses the <c>System.Diagnostics.StackTrace</c> class to generate
  31. /// a call stack. The caller's information is then extracted from this stack.
  32. /// </para>
  33. /// <para>
  34. /// The <c>System.Diagnostics.StackTrace</c> class is not supported on the
  35. /// .NET Compact Framework 1.0 therefore caller location information is not
  36. /// available on that framework.
  37. /// </para>
  38. /// <para>
  39. /// The <c>System.Diagnostics.StackTrace</c> class has this to say about Release builds:
  40. /// </para>
  41. /// <para>
  42. /// "StackTrace information will be most informative with Debug build configurations.
  43. /// By default, Debug builds include debug symbols, while Release builds do not. The
  44. /// debug symbols contain most of the file, method name, line number, and column
  45. /// information used in constructing StackFrame and StackTrace objects. StackTrace
  46. /// might not report as many method calls as expected, due to code transformations
  47. /// that occur during optimization."
  48. /// </para>
  49. /// <para>
  50. /// This means that in a Release build the caller information may be incomplete or may
  51. /// not exist at all! Therefore caller location information cannot be relied upon in a Release build.
  52. /// </para>
  53. /// </remarks>
  54. /// <author>Nicko Cadell</author>
  55. /// <author>Gert Driesen</author>
  56. #if !NETCF
  57. [Serializable]
  58. #endif
  59. public class LocationInfo
  60. {
  61. #region Public Instance Constructors
  62. /// <summary>
  63. /// Constructor
  64. /// </summary>
  65. /// <param name="callerStackBoundaryDeclaringType">The declaring type of the method that is
  66. /// the stack boundary into the logging system for this call.</param>
  67. /// <remarks>
  68. /// <para>
  69. /// Initializes a new instance of the <see cref="LocationInfo" />
  70. /// class based on the current thread.
  71. /// </para>
  72. /// </remarks>
  73. public LocationInfo(Type callerStackBoundaryDeclaringType)
  74. {
  75. // Initialize all fields
  76. m_className = NA;
  77. m_fileName = NA;
  78. m_lineNumber = NA;
  79. m_methodName = NA;
  80. m_fullInfo = NA;
  81. #if !(NETCF || NETSTANDARD1_3) // StackTrace isn't fully implemented for NETSTANDARD1_3 https://github.com/dotnet/corefx/issues/1797
  82. if (callerStackBoundaryDeclaringType != null)
  83. {
  84. try
  85. {
  86. StackTrace st = new StackTrace(true);
  87. int frameIndex = 0;
  88. // skip frames not from fqnOfCallingClass
  89. while (frameIndex < st.FrameCount)
  90. {
  91. StackFrame frame = st.GetFrame(frameIndex);
  92. if (frame != null && frame.GetMethod().DeclaringType == callerStackBoundaryDeclaringType)
  93. {
  94. break;
  95. }
  96. frameIndex++;
  97. }
  98. // skip frames from fqnOfCallingClass
  99. while (frameIndex < st.FrameCount)
  100. {
  101. StackFrame frame = st.GetFrame(frameIndex);
  102. if (frame != null && frame.GetMethod().DeclaringType != callerStackBoundaryDeclaringType)
  103. {
  104. break;
  105. }
  106. frameIndex++;
  107. }
  108. if (frameIndex < st.FrameCount)
  109. {
  110. // take into account the frames we skip above
  111. int adjustedFrameCount = st.FrameCount - frameIndex;
  112. ArrayList stackFramesList = new ArrayList(adjustedFrameCount);
  113. m_stackFrames = new StackFrameItem[adjustedFrameCount];
  114. for (int i=frameIndex; i < st.FrameCount; i++)
  115. {
  116. stackFramesList.Add(new StackFrameItem(st.GetFrame(i)));
  117. }
  118. stackFramesList.CopyTo(m_stackFrames, 0);
  119. // now frameIndex is the first 'user' caller frame
  120. StackFrame locationFrame = st.GetFrame(frameIndex);
  121. if (locationFrame != null)
  122. {
  123. System.Reflection.MethodBase method = locationFrame.GetMethod();
  124. if (method != null)
  125. {
  126. m_methodName = method.Name;
  127. if (method.DeclaringType != null)
  128. {
  129. m_className = method.DeclaringType.FullName;
  130. }
  131. }
  132. m_fileName = locationFrame.GetFileName();
  133. m_lineNumber = locationFrame.GetFileLineNumber().ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
  134. // Combine all location info
  135. m_fullInfo = m_className + '.' + m_methodName + '(' + m_fileName + ':' + m_lineNumber + ')';
  136. }
  137. }
  138. }
  139. catch(System.Security.SecurityException)
  140. {
  141. // This security exception will occur if the caller does not have
  142. // some undefined set of SecurityPermission flags.
  143. LogLog.Debug(declaringType, "Security exception while trying to get caller stack frame. Error Ignored. Location Information Not Available.");
  144. }
  145. }
  146. #endif
  147. }
  148. /// <summary>
  149. /// Constructor
  150. /// </summary>
  151. /// <param name="className">The fully qualified class name.</param>
  152. /// <param name="methodName">The method name.</param>
  153. /// <param name="fileName">The file name.</param>
  154. /// <param name="lineNumber">The line number of the method within the file.</param>
  155. /// <remarks>
  156. /// <para>
  157. /// Initializes a new instance of the <see cref="LocationInfo" />
  158. /// class with the specified data.
  159. /// </para>
  160. /// </remarks>
  161. public LocationInfo(string className, string methodName, string fileName, string lineNumber)
  162. {
  163. m_className = className;
  164. m_fileName = fileName;
  165. m_lineNumber = lineNumber;
  166. m_methodName = methodName;
  167. m_fullInfo = m_className + '.' + m_methodName + '(' + m_fileName +
  168. ':' + m_lineNumber + ')';
  169. }
  170. #endregion Public Instance Constructors
  171. #region Public Instance Properties
  172. /// <summary>
  173. /// Gets the fully qualified class name of the caller making the logging
  174. /// request.
  175. /// </summary>
  176. /// <value>
  177. /// The fully qualified class name of the caller making the logging
  178. /// request.
  179. /// </value>
  180. /// <remarks>
  181. /// <para>
  182. /// Gets the fully qualified class name of the caller making the logging
  183. /// request.
  184. /// </para>
  185. /// </remarks>
  186. public string ClassName
  187. {
  188. get { return m_className; }
  189. }
  190. /// <summary>
  191. /// Gets the file name of the caller.
  192. /// </summary>
  193. /// <value>
  194. /// The file name of the caller.
  195. /// </value>
  196. /// <remarks>
  197. /// <para>
  198. /// Gets the file name of the caller.
  199. /// </para>
  200. /// </remarks>
  201. public string FileName
  202. {
  203. get { return m_fileName; }
  204. }
  205. /// <summary>
  206. /// Gets the line number of the caller.
  207. /// </summary>
  208. /// <value>
  209. /// The line number of the caller.
  210. /// </value>
  211. /// <remarks>
  212. /// <para>
  213. /// Gets the line number of the caller.
  214. /// </para>
  215. /// </remarks>
  216. public string LineNumber
  217. {
  218. get { return m_lineNumber; }
  219. }
  220. /// <summary>
  221. /// Gets the method name of the caller.
  222. /// </summary>
  223. /// <value>
  224. /// The method name of the caller.
  225. /// </value>
  226. /// <remarks>
  227. /// <para>
  228. /// Gets the method name of the caller.
  229. /// </para>
  230. /// </remarks>
  231. public string MethodName
  232. {
  233. get { return m_methodName; }
  234. }
  235. /// <summary>
  236. /// Gets all available caller information
  237. /// </summary>
  238. /// <value>
  239. /// All available caller information, in the format
  240. /// <c>fully.qualified.classname.of.caller.methodName(Filename:line)</c>
  241. /// </value>
  242. /// <remarks>
  243. /// <para>
  244. /// Gets all available caller information, in the format
  245. /// <c>fully.qualified.classname.of.caller.methodName(Filename:line)</c>
  246. /// </para>
  247. /// </remarks>
  248. public string FullInfo
  249. {
  250. get { return m_fullInfo; }
  251. }
  252. #if !(NETCF || NETSTANDARD1_3)
  253. /// <summary>
  254. /// Gets the stack frames from the stack trace of the caller making the log request
  255. /// </summary>
  256. public StackFrameItem[] StackFrames
  257. {
  258. get { return m_stackFrames; }
  259. }
  260. #endif
  261. #endregion Public Instance Properties
  262. #region Private Instance Fields
  263. private readonly string m_className;
  264. private readonly string m_fileName;
  265. private readonly string m_lineNumber;
  266. private readonly string m_methodName;
  267. private readonly string m_fullInfo;
  268. #if !(NETCF || NETSTANDARD1_3)
  269. private readonly StackFrameItem[] m_stackFrames;
  270. #endif
  271. #endregion Private Instance Fields
  272. #region Private Static Fields
  273. /// <summary>
  274. /// The fully qualified type of the LocationInfo class.
  275. /// </summary>
  276. /// <remarks>
  277. /// Used by the internal logger to record the Type of the
  278. /// log message.
  279. /// </remarks>
  280. private readonly static Type declaringType = typeof(LocationInfo);
  281. /// <summary>
  282. /// When location information is not available the constant
  283. /// <c>NA</c> is returned. Current value of this string
  284. /// constant is <b>?</b>.
  285. /// </summary>
  286. private const string NA = "?";
  287. #endregion Private Static Fields
  288. }
  289. }