2010-03-08 32 views
39

Tôi muốn làm điều gì đó như thế này:Tôi có thể nhận tên/giá trị thông số theo thủ tục từ chức năng hiện đang thực thi không?

public MyFunction(int integerParameter, string stringParameter){ 
    //Do this: 
    LogParameters(); 
    //Instead of this: 
    //Log.Debug("integerParameter: " + integerParameter + 
    //   ", stringParameter: " + stringParameter); 

} 

public LogParameters(){ 
    //Look up 1 level in the call stack (if possible), 
    //Programmatically loop through the function's parameters/values 
    //and log them to a file (with the function name as well). 
    //If I can pass a MethodInfo instead of analyzing the call stack, great. 
} 

Tôi thậm chí không chắc chắn những gì tôi muốn làm là có thể, nhưng nó sẽ rất tốt đẹp để có thể tự động đầu ra tên tham số/giá trị trong thời gian chạy vào một tệp mà không viết mã một cách rõ ràng để ghi lại chúng.

Có thể không?

+2

Close-to-bản sao của http://stackoverflow.com/câu hỏi/135782/generic-logging-of-function-parameters-in-exception-handl ing –

+0

... và http://stackoverflow.com/questions/819576/is-it-possible-to-get-parameters-values-for-each-frame-in-call-stack-in-net –

+0

Dupe này http://stackoverflow.com/questions/1820630/how-to-get-parameter-value-from-stacktrace –

Trả lời

21

Tôi nhận ra người có liên quan đến câu hỏi khác mà đề cập PostSharp, nhưng tôi không thể giúp đăng mã mà giải quyết vấn đề của tôi (sử dụng PostSharp) để mọi người khác có thể được hưởng lợi từ nó.

class Program { 
    static void Main(string[] args) { 
     Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); 
     new MyClass().MyMethod(44, "asdf qwer 1234", 3.14f, true); 
     Console.ReadKey(); 
    } 
} 
public class MyClass { 
    public MyClass() { 
    } 
    [Trace("Debug")] 
    public int MyMethod(int x, string someString, float anotherFloat, bool theBool) { 
     return x + 1; 
    } 
} 
[Serializable] 
public sealed class TraceAttribute : OnMethodBoundaryAspect { 
    private readonly string category; 

    public TraceAttribute(string category) { 
     this.category = category; 
    } 

    public string Category { get { return category; } } 

    public override void OnEntry(MethodExecutionArgs args) { 
     Trace.WriteLine(string.Format("Entering {0}.{1}.", 
             args.Method.DeclaringType.Name, 
             args.Method.Name), category); 

     for (int x = 0; x < args.Arguments.Count; x++) { 
      Trace.WriteLine(args.Method.GetParameters()[x].Name + " = " + 
          args.Arguments.GetArgument(x)); 
     } 
    } 

    public override void OnExit(MethodExecutionArgs args) { 
     Trace.WriteLine("Return Value: " + args.ReturnValue); 

     Trace.WriteLine(string.Format("Leaving {0}.{1}.", 
             args.Method.DeclaringType.Name, 
             args.Method.Name), category); 
    } 
} 

Đơn giản chỉ cần thêm Trace thuộc tính đến một phương pháp sẽ gây ra thông tin gỡ lỗi rất tốt đẹp để có đầu ra, như vậy:

Debug: Entering MyClass.MyMethod. 
x = 44 
someString = asdf qwer 1234 
anotherFloat = 3.14 
theBool = True 
Return Value: 45 
Debug: Leaving MyClass.MyMethod. 
+3

là điều này có thể với phiên bản express của postsharp (miễn phí) không? – Dbl

12

Về mặt lý thuyết có thể với một bản dựng gỡ lỗi và tối ưu hóa bị tắt, nhưng thực tế nói, tôi đề nghị bạn muốn một số mã nguồn viết lại.

Mọi người sẽ tiếp tục thông báo cho bạn phản ánh sẽ hoạt động khi không, vì vậy, here is the function that's actually capable of getting argument values. Nó không có khả năng hoạt động đáng tin cậy khi bật tối ưu hóa (ví dụ: thậm chí có thể không khung ngăn xếp khi nội tuyến đang bật) và cài đặt trình gỡ lỗi để bạn có thể gọi chức năng đó sẽ không đơn giản như bạn đã hy vọng.

+0

Thực ra - điểm rất tốt về tối ưu hóa - Tôi giả sử câu hỏi này liên quan đến công cụ gỡ lỗi .... –

+1

@Ben Voigt - bạn có sử dụng mẫu cho 'ICorDebugILFrame :: GetArgument' không? –

+0

@Ben Voigt - Tôi cũng muốn xem việc sử dụng mẫu, nếu bạn có thể cung cấp. Cảm ơn câu trả lời! – Pwninstein

6
StackTrace stackTrace = new StackTrace(); 
ParameterInfo[] parameters = stackTrace.GetFrame(1).GetMethod().GetParameters(); 

Lưu ý, GetFrame (1) nhận phương thức gọi thay vì phương pháp hiện tại. Điều này sẽ cung cấp cho bạn kết quả mong muốn của bạn và cho phép bạn thực thi mã bên dưới trong LogParameters().

Bạn sẽ cần gọi hàm LogParameters như dưới đây vì bạn sẽ không thể nhận được các giá trị được phản ánh của integerParameter và stringParameter từ ParameterInfo.

LogParameters(integerParameter, stringParameter); 
+0

Chắc chắn sử dụng StackTrace để lấy StackFrame có liên quan, điều này thậm chí cho phép khai báo chương trình con "ghi nhật ký" với một đối số "chiều sâu" tùy chọn. +1 –

+0

Bản cập nhật cung cấp các giá trị, nhưng có một vài điều có thể xảy ra sai. Đầu tiên, đó là một gánh nặng bảo trì - bất kỳ thay đổi nào đối với danh sách tham số phải được lặp lại trong cuộc gọi ghi nhật ký. Thứ hai, bạn phải nhảy qua một vài vòng phụ nếu hàm ban đầu của bạn có một tham số duy nhất của đối tượng kiểu []. –

3

Trừ khi bạn sử dụng trình gỡ lỗi API, bạn không thể lặp qua tham số giá trị của một phương pháp khác nhau về các cuộc gọi stack. Mặc dù bạn có thể nhận thông số tên từ callstack (như những người khác đã đề cập).

Điều gần nhất sẽ là:

public MyFunction(int integerParameter, string stringParameter){ 
    LogParameters(integerParameter, stringParameter); 
} 

public void LogParameters(params object[] values){ 
    // Get the parameter names from callstack and log names/values 
} 
+0

bạn CÓ THỂ, bạn chỉ cần API gỡ lỗi, không phản ánh –

+0

Cách tiếp cận này là một ý tưởng gọn gàng, nhưng có một vài điều có thể xảy ra sai. Đầu tiên, đó là một gánh nặng bảo trì - bất kỳ thay đổi nào đối với danh sách tham số phải được lặp lại trong cuộc gọi ghi nhật ký. Thứ hai, bạn phải nhảy qua một vài hoops thêm nếu hàm ban đầu của bạn có một tham số duy nhất của kiểu 'object []'. –

+0

@BenVoigt - vâng, đó là lý do tại sao tôi sẽ sử dụng AOP để làm một điều như vậy những ngày này. –

1

Tôi làm theo các hướng dẫn và tạo lớp này:

public static class Tracer 
{ 
    public static void Parameters(params object[] parameters) 
    { 
     #if DEBUG 
      var jss = new JavaScriptSerializer(); 

      var stackTrace = new StackTrace(); 

      var paramInfos = stackTrace.GetFrame(1).GetMethod().GetParameters(); 

      var callingMethod = stackTrace.GetFrame(1).GetMethod(); 
      Debug.WriteLine(string.Format("[Func: {0}", callingMethod.DeclaringType.FullName + "." + callingMethod.Name + "]")); 

      for (int i = 0; i < paramInfos.Count(); i++) 
      { 
       var currentParameterInfo = paramInfos[i]; 

       var currentParameter = parameters[i]; 

       Debug.WriteLine(string.Format(" Parameter: {0}", currentParameterInfo.Name)); 

       Debug.WriteLine(string.Format(" Value: {0}", jss.Serialize(currentParameter))); 
      } 
      Debug.WriteLine("[End Func]"); 
     #endif 
    } 
} 

Gọi như sau:

public void Send<T>(T command) where T : Command 
{ 
    Tracer.Parameters(command); 
} 

Và đầu ra trông như thế này

[Func: SimpleCQRS.FakeBus.Send] 
    Parameter: command 
    Value: {"InventoryItemId":"f7005197-bd20-42a6-b35a-15a6dcc23c33","Name":"test record"} 
[End Func] 

Editing

.........

Và tôi đã mở rộng chức năng theo dõi của mình để thực sự làm một công việc tuyệt vời cho tôi. Để theo dõi mọi chức năng và chức năng gọi của nó, v.v., bạn có thể sử dụng StrackTrace.GetFrame (2) để sử dụng chức năng bổ sung. Và bây giờ đầu ra của tôi giàu hơn nhiều. Tôi cũng đã sử dụng thư viện Json.NET's để xuất các đối tượng JSON được định dạng đẹp mắt. Ngoài ra, đầu ra có thể được dán trong một tệp JavaScript trống và xem đầu ra được tô màu.

Bây giờ đầu ra của tôi trông như thế này:

//Func: HomeController(Constructor): CQRSGui.Controllers.HomeController(Constructor) 
//From: RuntimeTypeHandle.CreateInstance: System.RuntimeTypeHandle.CreateInstance 
var parameters = {} 

//Func: HomeController.Add: CQRSGui.Controllers.HomeController.Add 
//From: System.Object lambda_method(System.Runtime.CompilerServices.Closure, System.Web.Mvc.ControllerBase, System.Object[]) 
var parameters = { 
    "name": "car" 
} 

//Func: Command(Constructor): SimpleCQRS.Command(Constructor) 
//From: CreateInventoryItem(Constructor): SimpleCQRS.CreateInventoryItem(Constructor) 
var parameters = {} 

//Func: CreateInventoryItem(Constructor): SimpleCQRS.CreateInventoryItem(Constructor) 
//From: HomeController.Add: CQRSGui.Controllers.HomeController.Add 
var parameters = { 
    "inventoryItemId": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
    "name": "car" 
} 

//Func: FakeBus.Send: SimpleCQRS.FakeBus.Send 
//From: HomeController.Add: CQRSGui.Controllers.HomeController.Add 
var parameters = { 
    "command": { 
     "InventoryItemId": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
     "Name": "car" 
    } 
} 

//Func: InventoryCommandHandlers.Handle: SimpleCQRS.InventoryCommandHandlers.Handle 
//From: FakeBus.Send: SimpleCQRS.FakeBus.Send 
var parameters = { 
    "message": { 
     "InventoryItemId": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
     "Name": "car" 
    } 
} 

//Func: AggregateRoot(Constructor): SimpleCQRS.AggregateRoot(Constructor) 
//From: InventoryItem(Constructor): SimpleCQRS.InventoryItem(Constructor) 
var parameters = {} 

//Func: InventoryItem(Constructor): SimpleCQRS.InventoryItem(Constructor) 
//From: InventoryCommandHandlers.Handle: SimpleCQRS.InventoryCommandHandlers.Handle 
var parameters = { 
    "id": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
    "name": "car" 
} 

//Func: Event(Constructor): SimpleCQRS.Event(Constructor) 
//From: InventoryItemCreated(Constructor): SimpleCQRS.InventoryItemCreated(Constructor) 
var parameters = {} 

//Func: InventoryItemCreated(Constructor): SimpleCQRS.InventoryItemCreated(Constructor) 
//From: InventoryItem(Constructor): SimpleCQRS.InventoryItem(Constructor) 
var parameters = { 
    "id": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
    "name": "car" 
} 

//Func: AggregateRoot.ApplyChange: SimpleCQRS.AggregateRoot.ApplyChange 
//From: InventoryItem(Constructor): SimpleCQRS.InventoryItem(Constructor) 
var parameters = { 
    "event": { 
     "Id": "d974cd27-430d-4b22-ad9d-22ea0e6a2559", 
     "Name": "car", 
     "Version": 0 
    } 
} 

Mạnh mẽ, phải không? Tôi chỉ cần nhìn thấy đầu ra và không cần phải phá vỡ các ứng dụng một lần nữa và một lần nữa và không cần phải kiểm tra vào xem và cửa sổ địa phương. Tôi thích cách này. Tôi đã đặt chức năng tracker.Parameters ở mọi nơi trong ứng dụng của tôi và bây giờ tôi đã tự động gỡ lỗi ứng dụng.

Một thứ mà tôi đã thêm vào chức năng xuất của tôi là một cuộc gọi đến Sự kiện lỗi trong tuần tự hóa. Và xử lý nó từ Json.NET. Trên thực tế bạn có thể rơi vào một lỗi tham chiếu vòng tròn. Tôi bị bắt. Và cũng nếu có nhiều lỗi serialization bạn có thể bắt chúng và sau đó bạn có thể hiển thị lỗi serialization ngay dưới đầu ra đối tượng tham số.

+1

Cách tiếp cận này là một ý tưởng gọn gàng, nhưng có một vài điều có thể đi sai. Đầu tiên, đó là một gánh nặng bảo trì - bất kỳ thay đổi nào đối với danh sách tham số phải được lặp lại trong cuộc gọi ghi nhật ký.Thứ hai, bạn phải nhảy qua một vài hoops thêm nếu hàm ban đầu của bạn có một tham số duy nhất của kiểu 'object []'. –

3

Đây là lớp Tiện ích tạo nhật ký.

internal class ParamaterLogModifiedUtility 
{ 
    private String _methodName; 
    private String _paramaterLog; 

    private readonly JavaScriptSerializer _serializer; 
    private readonly Dictionary<String, Type> _methodParamaters; 
    private readonly List<Tuple<String, Type, object>>_providedParametars; 

    public ParamaterLogModifiedUtility(params Expression<Func<object>>[] providedParameters) 
    { 
     try 
     { 
      _serializer = new JavaScriptSerializer(); 
      var currentMethod = new StackTrace().GetFrame(1).GetMethod(); 

      /*Set class and current method info*/ 
      _methodName = String.Format("Class = {0}, Method = {1}", currentMethod.DeclaringType.FullName, currentMethod.Name); 

      /*Get current methods paramaters*/ 
      _methodParamaters = new Dictionary<string, Type>(); 
      (from aParamater in currentMethod.GetParameters() 
      select new { Name = aParamater.Name, DataType = aParamater.ParameterType }) 
      .ToList() 
      .ForEach(obj => _methodParamaters.Add(obj.Name, obj.DataType)); 

      /*Get provided methods paramaters*/ 
      _providedParametars = new List<Tuple<string, Type, object>>(); 
      foreach (var aExpression in providedParameters) 
      { 
       Expression bodyType = aExpression.Body; 

       if (bodyType is MemberExpression) 
       { 
        AddProvidedParamaterDetail((MemberExpression)aExpression.Body); 
       } 
       else if (bodyType is UnaryExpression) 
       { 
        UnaryExpression unaryExpression = (UnaryExpression)aExpression.Body; 
        AddProvidedParamaterDetail((MemberExpression)unaryExpression.Operand); 
       } 
       else 
       { 
        throw new Exception("Expression type unknown."); 
       } 
      } 

      /*Process log for all method parameters*/ 
      ProcessLog(); 

     } 
     catch (Exception exception) 
     { 
      throw new Exception("Error in paramater log processing.", exception); 
     } 
    } 

    private void ProcessLog() 
    { 
     try 
     { 
      foreach (var aMethodParamater in _methodParamaters) 
      { 
       var aParameter = 
        _providedParametars.Where(
         obj => obj.Item1.Equals(aMethodParamater.Key) && obj.Item2 == aMethodParamater.Value).Single(); 
       _paramaterLog += String.Format(@" ""{0}"":{1},", aParameter.Item1, _serializer.Serialize(aParameter.Item3)); 
      } 
      _paramaterLog = (_paramaterLog != null) ? _paramaterLog.Trim(' ', ',') : string.Empty; 
     } 
     catch (Exception exception) 
     { 
      throw new Exception("MathodParamater is not found in providedParameters."); 
     } 
    } 

    private void AddProvidedParamaterDetail(MemberExpression memberExpression) 
    { 
     ConstantExpression constantExpression = (ConstantExpression) memberExpression.Expression; 
     var name = memberExpression.Member.Name; 
     var value = ((FieldInfo) memberExpression.Member).GetValue(constantExpression.Value); 
     var type = value.GetType(); 
     _providedParametars.Add(new Tuple<string, Type, object>(name, type, value)); 
    } 


    public String GetLog() 
    { 
     return String.Format("{0}({1})", _methodName, _paramaterLog); 
    } 

} 

Sử dụng Utility

class PersonLogic 
{ 
    public bool Add(PersonEntity aPersonEntity, ushort age = 12, String id = "1", String name = "Roy") 
    { 
     string log = new ParamaterLogModifiedUtility(() => aPersonEntity,() => age,() => id,() => name).GetLog(); 
     return true; 
    } 
} 

Bây giờ Gọi Công dụng

class Program 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      PersonLogic personLogic = new PersonLogic(); 
      personLogic.Add(id: "1", age: 24, name: "Dipon", aPersonEntity: new PersonEntity() { Id = "1", Name = "Dipon", Age = 24 }); 
     } 
     catch (Exception exception) 
     { 
      Console.WriteLine("Error."); 
     } 
     Console.ReadKey(); 
    } 
} 

Result Log:

 Class = MethodParamatersLog.Logic.PersonLogic, Method = Add("aPersonEntity":{"CreatedDateTime":"\/Date(1383422468353)\/","Id":"1","Name":"Dipon","Age":24}, "age":24, "id":"1", "name":"Dipon") 
Các vấn đề liên quan