2011-05-14 1 views
9

나는 Log4net에 대한 래퍼를 만들었으며 (NLog를 선호 할 수도 있지만 아직 결정하지는 못했음) 구조체를 호출하기 위해 기록 된 메시지 결과를 들여 씁니다. 예를 들면 :C# 로깅의 경우 최소한의 오버 헤드로 호출 스택 깊이를 얻으려면 어떻게해야합니까?

2011-04-03 00:20:30,271 [CT] DEBUG -  Merlinia.ProcessManager.CentralThread.ProcessAdminCommand - ProcStart - User Info Repository 
2011-04-03 00:20:30,271 [CT] DEBUG -  Merlinia.ProcessManager.CentralThread.StartOneProcess - User Info Repository 
2011-04-03 00:20:30,411 [CT] DEBUG -  Merlinia.ProcessManager.CentralThread.SetProcessStatus - Process = User Info Repository, status = ProcStarting 
2011-04-03 00:20:30,411 [CT] DEBUG -  Merlinia.ProcessManager.CentralThread.SendProcessStatusInfo 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MhlAdminLayer.SendToAllAdministrators - ProcessTable 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MReflection.CopyToBinary 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MReflection.CopyToBinary - False 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MhlBasicLayer.SendToAllConnections - 228 - True - False 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MmlNonThreaded.SendObject - 228 
2011-04-03 00:20:30,411 [CT] DEBUG -   Merlinia.CommonClasses.MllTcpSocket.SendMessage - 228 - True 
2011-04-03 00:20:32,174 [10] DEBUG - Merlinia.CommonClasses.MReflection.CreateFromBinary 
2011-04-03 00:20:32,174 [10] DEBUG -  Merlinia.CommonClasses.MReflection.CopyFromBinary - Bytes = 71 
2011-04-03 00:20:32,174 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.MessagingCallback - User Info Repository - ProcessInfoAndRequests 
2011-04-03 00:20:32,174 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.ProcessProcessInfoAndRequests - User Info Repository 

I이 System.Diagnostics.StackTrace를 사용하여 계산 StackFrames을한다.

여기에 질문이 있습니다. 더 효율적인 방법이 있습니까? 난 (상대적) 호출 스택 깊이를 결정할 필요가 있습니다. 즉, 현재 로깅 래퍼가 호출 된 마지막 시간을 더한 값 또는 빼기 값입니다. (실제로 StackFrame 객체를 사용하지 않습니다. 그렇지 않으면 메소드 이름을 얻습니다.)

호출 스택 깊이 또는 스택 사용을 쿼리하는 몇 가지 간단한 고성능 방법이 필요합니다.

답변

5

단순히 StackTrace.FrameCount 속성을 사용하고 이전에 기록한 FrameCount과 비교하십시오. FYI FrameCount은 내부 프레임 m_iNumOfFrames 만 돌려주기 때문에 실제 프레임 수를 검색하는 가장 빠른 방법 일 수 있습니다.

+0

답장을 보내 주셔서 감사합니다. 나는 틀릴 수도 있지만 StackTrace 객체를 만들면 모든 StackFrame 객체도 생성된다고 가정합니다. 이 사건이 아니라고 말하는거야? – RenniePet

+3

새로운 StackFrame() 객체는 비싸지 만 사용하지 않으면 성능이 정말로 필요하다면 뒤틀린 Reflection.Emit 자료를 읽으십시오 : http://ayende.com/blog/3879/reducing- Get-a-Stack-Trace의 비용 –

+0

와우, 감동했습니다. 또한 내 능력과 같은 느낌은 당신이 한 일을 이해하지 못하는 것입니다. 나는 나중에 시간이있을 때 그것을 분석하고 이해하려고 노력할 것이다. 그러나 "Func "은 무엇을 의미합니까? (.Net 2를 사용하고 있으며 해당 구성은 정의되지 않은 것으로 플래그 지정됩니다.) – RenniePet

2

Teoman Soygul과 특히 Teoman이 링크를 제공 한 Oren Eini에게 감사드립니다.

다음은 내가 사용하게 될 해결책이라고 생각되는 몇 가지 개념 증명 코드입니다.하지만 타이밍 테스트를 아직하지 않았 음을 인정해야합니다.

class TestProgram 
    { 
     static void Main(string[] args) 
     { 
     OneTimeSetup(); 

     int i = GetCallStackDepth(); // i = 10 on my test machine 
     i = AddOneToNesting();   // Now i = 11 
     } 


     private delegate object DGetStackFrameHelper(); 

     private static DGetStackFrameHelper _getStackFrameHelper; 

     private static FieldInfo _frameCount; 


     private static void OneTimeSetup() 
     { 
     Type stackFrameHelperType = 
      typeof(object).Assembly.GetType("System.Diagnostics.StackFrameHelper"); 


     MethodInfo getStackFramesInternal = 
      Type.GetType("System.Diagnostics.StackTrace, mscorlib").GetMethod(
          "GetStackFramesInternal", BindingFlags.Static | BindingFlags.NonPublic); 


     DynamicMethod dynamicMethod = new DynamicMethod(
         "GetStackFrameHelper", typeof(object), new Type[0], typeof(StackTrace), true); 

     ILGenerator generator = dynamicMethod.GetILGenerator(); 
     generator.DeclareLocal(stackFrameHelperType); 
     generator.Emit(OpCodes.Ldc_I4_0); 
     generator.Emit(OpCodes.Ldnull); 
     generator.Emit(OpCodes.Newobj, 
        stackFrameHelperType.GetConstructor(new Type[] { typeof(bool), typeof(Thread) })); 
     generator.Emit(OpCodes.Stloc_0); 
     generator.Emit(OpCodes.Ldloc_0); 
     generator.Emit(OpCodes.Ldc_I4_0); 
     generator.Emit(OpCodes.Ldnull); 
     generator.Emit(OpCodes.Call, getStackFramesInternal); 
     generator.Emit(OpCodes.Ldloc_0); 
     generator.Emit(OpCodes.Ret); 


     _getStackFrameHelper = 
        (DGetStackFrameHelper)dynamicMethod.CreateDelegate(typeof(DGetStackFrameHelper)); 


     _frameCount = stackFrameHelperType.GetField(
            "iFrameCount", BindingFlags.NonPublic | BindingFlags.Instance); 
     } 


     private static int GetCallStackDepth() 
     { 
     return (int)_frameCount.GetValue(_getStackFrameHelper()); 
     } 


     private static int AddOneToNesting() 
     { 
     return GetCallStackDepth(); 
     } 
    } 

편집 :이 버전은 내가 새로운 버전을 게시 한 다른 답변을 참조 늦은 2017 년에 마이크로 소프트가 mscorlib.dll의 업데이트 후 닷넷 프레임 워크 4.5이 작동하지 않습니다. (나는이 대답을 후세를 위해서 남겨두고있다. 닷넷 프레임 워크 2.0과 3.5에서도 여전히 작동한다.)

+0

+1, 좋은 직장이 있습니다. –

+0

사실, 거의 100 % Oren Eini의 코드입니다. 닷넷 2로 회귀했습니다. – RenniePet

2

6 년 반 동안의 믿을만한 서비스 후에 갑자기 많은 프로그램이 .Net Framework 4.5의 변경 사항을 포함하는 Microsoft의 업데이트를 적용한 후 2017 년 말에 충돌했습니다. 이것이 mscorlib.dll의 내부적으로 문서화되지 않은 데이터 구조에 의존하는 코드 작성을위한 것입니다.

이 코드 버전은 다시 작동하며 mscorlib.dll에 대한 향후 업데이트가있을 때 약간 더 강력하도록 설계되었습니다. 정상적으로 실패하고 항상 0을 반환합니다. 그러나 mscorlib.dll의 향후 변경에 대비하여이 코드에서 향후 충돌이 발생할 수 있다는 보장은 없습니다.

/// <summary> 
    /// This test program demonstrates a faster way of getting call stack depth by avoiding getting a 
    /// StackTrace object. But you can't get the calling method names this way. 
    /// 
    /// See http://stackoverflow.com/questions/5999177/for-c-logging-how-to-obtain-call-stack-depth-with-minimal-overhead 
    /// and http://ayende.com/blog/3879/reducing-the-cost-of-getting-a-stack-trace 
    /// 
    /// Update, late 2017, .Net mscorlib.dll has been changed for .Net 4.5. In the code below the two 
    /// possibilities are called "old .Net" and "new .Net". The two versions can be tested by setting 
    /// the target for this project to either .Net Framework 2.0 or .Net Framework 4.5. 
    /// </summary> 
    class TestProgram 
    { 
     static void Main() 
     { 
     OneTimeSetup(); 

     int i = GetCallStackDepth(); // i = 10 on my test machine for old .Net, 12 for new .Net 
     int j = AddOneToNesting(); 
     Console.WriteLine(j == i + 1 ? "Test succeeded!" : "Test failed!!!!!!!!"); 
     Console.ReadKey(); 
     } 


     private delegate object DGetStackFrameHelper(); 

     private static DGetStackFrameHelper _getStackFrameHelper = null; 

     private static FieldInfo _frameCount = null; 


     private static void OneTimeSetup() 
     { 
     try 
     { 
      Type stackFrameHelperType = 
          typeof(object).Assembly.GetType("System.Diagnostics.StackFrameHelper"); 

      // ReSharper disable once PossibleNullReferenceException 
      MethodInfo getStackFramesInternal = 
       Type.GetType("System.Diagnostics.StackTrace, mscorlib").GetMethod(
          "GetStackFramesInternal", BindingFlags.Static | BindingFlags.NonPublic); 
      if (getStackFramesInternal == null) 
       return; // Unknown mscorlib implementation 

      DynamicMethod dynamicMethod = new DynamicMethod(
         "GetStackFrameHelper", typeof(object), new Type[0], typeof(StackTrace), true); 

      ILGenerator generator = dynamicMethod.GetILGenerator(); 
      generator.DeclareLocal(stackFrameHelperType); 

      bool newDotNet = false; 

      ConstructorInfo constructorInfo = 
        stackFrameHelperType.GetConstructor(new Type[] {typeof(bool), typeof(Thread)}); 
      if (constructorInfo != null) 
       generator.Emit(OpCodes.Ldc_I4_0); 
      else 
      { 
       constructorInfo = stackFrameHelperType.GetConstructor(new Type[] {typeof(Thread)}); 
       if (constructorInfo == null) 
        return; // Unknown mscorlib implementation 
       newDotNet = true; 
      } 

      generator.Emit(OpCodes.Ldnull); 
      generator.Emit(OpCodes.Newobj, constructorInfo); 
      generator.Emit(OpCodes.Stloc_0); 
      generator.Emit(OpCodes.Ldloc_0); 
      generator.Emit(OpCodes.Ldc_I4_0); 

      if (newDotNet) 
       generator.Emit(OpCodes.Ldc_I4_0); // Extra parameter 

      generator.Emit(OpCodes.Ldnull); 
      generator.Emit(OpCodes.Call, getStackFramesInternal); 
      generator.Emit(OpCodes.Ldloc_0); 
      generator.Emit(OpCodes.Ret); 

      _getStackFrameHelper = 
        (DGetStackFrameHelper) dynamicMethod.CreateDelegate(typeof(DGetStackFrameHelper)); 

      _frameCount = stackFrameHelperType.GetField("iFrameCount", 
                BindingFlags.NonPublic | BindingFlags.Instance); 
     } 
     catch 
     {} // _frameCount remains null, indicating unknown mscorlib implementation 
     } 


     private static int GetCallStackDepth() 
     { 
     if (_frameCount == null) 
      return 0; // Unknown mscorlib implementation 
     return (int)_frameCount.GetValue(_getStackFrameHelper()); 
     } 


     private static int AddOneToNesting() 
     { 
     return GetCallStackDepth(); 
     } 
    } 
관련 문제