2011-05-14 19 views
9

Log4net için bir paket oluşturdum (NLog'un lehine düşebilirim; henüz karar vermedim) ve oturum açtıkları mesajların yapısını çağırmasıyla ilgili bir fikir vermesini sağlarım. Örneğin:C# günlüğe kaydetme için, çağrı yığını derinliğini minimum yük ile nasıl edinebilirim?

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 

Bu System.Diagnostics.StackTrace kullanarak ve sayma StackFrames yapmak.

Şimdi soru şu: Bunu yapmanın daha verimli bir yolu var mı? Sadece (göreceli) çağrı yığını derinliğini belirlemeye ihtiyacım var, yani, mevcut derinlik artı veya eksi, günlüğe kaydetme sargımın çağrıldığı son kez. (Aslında StackFrame nesnelerini kullanmadığımı unutmayın - aksi halde yöntem isimlerini alırım.)

Arama yığını derinliğini veya yığın kullanımını sorgulamanın basit ve yüksek performanslı bir yolunu umuyorum.

cevap

5

StackTrace.FrameCount özelliğini kullanın ve daha önce kaydedilmiş olan FrameCount ile karşılaştırın. FYI, FrameCount, muhtemelen gerçek çerçeve sayısını almak için en hızlı yöntemdir, çünkü yalnızca iç m_iNumOfFrames alanını size geri döndürür.

+0

Cevabınız için teşekkür ederiz. Yanlış olabilir, ancak StackFrame nesnesinin tümünün de oluşturulduğu bir StackTrace nesnesi oluşturduğunuzda farz ediyorum. Bunun böyle olmadığını mı söylüyorsun? – RenniePet

+3

yeni StackFrame() nesnesi, onu kullanmıyorsanız o kadar pahalı değil, ancak gerçekten en iyi performansa ihtiyacınız varsa, bazı Twisted Reflection.Emit öğelerini okuyun: http://ayende.com/blog/3879/reducing- -kullanıma-a-stack-trace –

+0

Vay, etkilendim. Benim yeteneklerim gibi hissetmek de, neler yaptığınızı anlamaya yetmiyor. Daha fazla zamanım olduğunda daha sonra analiz etmeye ve anlamaya çalışacağım. Ama "Func " ne anlama geliyor? (.Net 2 kullanıyorum ve bu yapı tanımsız olarak işaretlendi.) – RenniePet

2

Teoman Soygul'a ve özellikle Teoman'a bir link verdiği Oren Eini'ye teşekkür ederiz. Aşağıdakiler, kullanacağım çözüm olduğunu düşündüğüm bazı "kavram kanıtı" kodudur - itiraf etmeliyim ki, herhangi bir zamanlama testi yapmadım.

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(); 
     } 
    } 

DÜZENLEME: Bu sürüm ben yeni bir sürüm için gönderdiniz başka yanıta bakın sonlarında 2017 yılında Microsoft tarafından mscorlib.dll bir güncellemeden sonra .Net Framework 4.5 için çalışmaz. (Bu cevabı hakikat uğruna bırakıyorum - ve hala .NET Framework 2.0 ve 3.5 için çalışıyor.)

+0

+1, güzel iş var. –

+0

Gerçekten değil, neredeyse% 100 Oren Eini kodu, sadece .Net 2'ye geriledi. – RenniePet

2

Altı buçuk yıllık güvenilir hizmetten sonra, birden fazla programımın olduğunu anladım. Microsoft'un, .NET Framework 4.5'deki değişiklikleri içeren bir güncelleştirmeyi uyguladıktan sonra 2017'nin sonlarında kilitleniyor. Bu, mscorlib.dll dosyasında dahili belgesiz veri yapılarına bağlı olan kod yazarken aldığınız şeydir.

Kodun bu sürümü yeniden çalışır ve ayrıca gelecekteki olası güncelleştirmelerle karşılaşılması zor olan mscorlib.dll için biraz daha sağlam olacak şekilde tasarlanmıştır - bu da yalnızca incelikle başarısız olur ve her zaman sıfırlanır. Ancak, gelecekte bu koddaki gelecekteki çökmelerle sonuçlanan mscorlib.dll'deki gelecekteki değişikliklere karşı hala bir garanti yoktur.

/// <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(); 
     } 
    } 
İlgili konular