C# 監測每個方法的執行次數和占用時間(測試4)


  今天也要做這個功能,就百度一下,結果搜索到了自己的文章。一開始還沒注意,當看到里面的一個注釋的方法時,一開始還以為自己復制錯了代碼,結果仔細一看網頁的文章,我去,原來是自己寫的,寫的確實不咋地。

把自己的文章的代碼復制了下來,稍微改了一下,運行看了一下效果,然后仔細一看,計算的總時間不對,如下圖:

上一篇文章的地址:https://www.cnblogs.com/guxingy/p/10142242.html

改了幾個地方:

    /// <summary>
    /// 攔截器
    /// </summary>
    public class CallingLogInterceptor : IInterceptor
    {
        private DateTime dt { get; set; }
        private TimeSpan ts { get; set; }

        /// <summary>
        /// 方法執行前
        /// </summary>
        /// <param name="invocation"></param>
        private void PreProceed(IInvocation invocation)
        {
            dt = DateTime.Now;
        }

        /// <summary>
        /// 方法執行后
        /// </summary>
        /// <param name="invocation"></param>
        private void PostProceed(IInvocation invocation)
        {
            ts = DateTime.Now - dt;


            Console.WriteLine($"方法名稱:{invocation.Method.Name}" );
            Console.WriteLine($"開始時間:{dt.ToString("yyyy-MM-dd HH:mm:ss fff")}");
            Console.WriteLine($"結束時間:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}");
            Console.WriteLine($"所用時間:{ts.TotalSeconds}");


            MethodOperationInfo.Add(invocation, ts.TotalMilliseconds);
        }

        /// <summary>
        /// 攔截
        /// </summary>
        /// <param name="invocation"></param>
        public void Intercept(IInvocation invocation)
        {
            this.PreProceed(invocation);
            invocation.Proceed();//調用
            this.PostProceed(invocation);
        }
    }
View Code
    /// <summary>
    /// 測試類1
    /// </summary>
    public class Class5_test1
    {
        public virtual void test1()
        {
            test4();            
            test3();
            test2();
        }
        public virtual void test2()
        {
            System.Threading.Thread.Sleep(1000);
        }
        public virtual void test3()
        {
            System.Threading.Thread.Sleep(2000);
        }
        public virtual void test4()
        {
            System.Threading.Thread.Sleep(3000);
        }
    }
View Code
    public class MyProxyGenerator
    {
        /// <summary>
        /// 創建一個代理對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T CreateProxy<T>() where T : class
        {
            ProxyGenerator generator = new ProxyGenerator();//代理
            CallingLogInterceptor interceptor = new CallingLogInterceptor();//定義 攔截器
            T entity = generator.CreateClassProxy<T>(interceptor);
            return entity;
        }
    }
View Code
    public class Class2
    {
        public static void test1()
        {
            Class5_test1 entity = MyProxyGenerator.CreateProxy<Class5_test1>();
            entity.test1();
            MethodOperationInfo.ShowContainsDetail();

            Console.Read();
        }
    }
View Code

 

說明:所有的測試,只是修改了一下Class5_test1 這個類給

測試1

    /// <summary>
    /// 測試類1
    /// </summary>
    public class Class5_test1
    {
        public virtual void test1()
        {            
            test2();
            test3();
            test4();
        }
        public virtual void test2()
        {
            System.Threading.Thread.Sleep(1000);
        }
        public virtual void test3()
        {
            System.Threading.Thread.Sleep(2000);
        }
        public virtual void test4()
        {
            System.Threading.Thread.Sleep(3000);
        }
    }
View Code

問題:這里test1調用了test2、test3、test4,那么執行test1所用時間,應該是test2、test3、test4所用時間的總和,正確所用時間至少應該是6秒(1+2+3),結果這里輸出的是3秒多,肯定不對。

 

測試2

    /// <summary>
    /// 測試類1
    /// </summary>
    public class Class5_test1
    {
        public virtual void test1()
        {
            test4();
            test3();            
            test2();
        }
        public virtual void test2()
        {
            System.Threading.Thread.Sleep(1000);
        }
        public virtual void test3()
        {
            System.Threading.Thread.Sleep(2000);
        }
        public virtual void test4()
        {
            System.Threading.Thread.Sleep(3000);
        }
    }
View Code

問題:這里的test1方法的所用時間是1秒多,更不對了。

 

結論:

  測試1 和 測試2 的區別,只是在test1里面改變了test2、test3、test4的執行順序,結果時間卻迥然不同,更奇怪的是,test1的所用時間,更是莫名的接近調用的倒數第二個方法。

  測試1里面,test1的所用時間,很接近test4。

  測試2里面,test1的所用時間,很接近test2。

  反正就是所用時間,很接近 方法體里面 最后一個 調用的方法,原因找到了再補充,了解一下動態代理的原理或許就知道了,也獲取是自己寫錯了,估計是第一種可能

 

 

乘着自己好奇,又測試了一下,看下面哦

    /// <summary>
    /// 測試類1
    /// </summary>
    public class Class5_test1
    {
        public virtual void test1()
        {
            Console.WriteLine("開始執行test1");
            test4();
            test3();            
            test2();
            Console.WriteLine("執行完成test1");
        }
        public virtual void test2()
        {
            System.Threading.Thread.Sleep(1000);
        }
        public virtual void test3()
        {
            System.Threading.Thread.Sleep(2000);
        }
        public virtual void test4()
        {
            System.Threading.Thread.Sleep(3000);
        }
    }
View Code

主要是是在,test1方法里面添加了方法的執行情況, 輸出 開始執行 和 執行完成。

 

 

找到原因了:攔截器對象只創建了一個,所以dt一直都是最后調用的那個方法的開始時間

      還是自己寫的代碼有問題啊

 

賦上 改好的全部代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;



using Castle.Core.Interceptor;
using Castle.DynamicProxy;
using System.Reflection;
using Newtonsoft.Json;
using System.Security.Cryptography;

namespace ConsoleApplication3
{
    //原文:https://www.cnblogs.com/guxingy/p/10142242.html

    public class Class2
    {
        public static void test1()
        {
            Class5_test1 entity = MyProxyGenerator.CreateProxy<Class5_test1>();
            entity.test1();
            MethodOperationInfo.ShowContainsDetail();
        }
    }



    public class MyProxyGenerator
    {
        /// <summary>
        /// 創建一個代理對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T CreateProxy<T>() where T : class
        {
            ProxyGenerator generator = new ProxyGenerator();//代理
            CallingLogInterceptor interceptor = new CallingLogInterceptor();//定義 攔截器
            T entity = generator.CreateClassProxy<T>(interceptor);
            return entity;
        }
    }




    /// <summary>
    /// 攔截器
    /// </summary>
    public class CallingLogInterceptor : IInterceptor
    {
        private DateTime dt { get; set; }
        private TimeSpan ts { get; set; }


        /// <summary>
        /// 方法執行前
        /// </summary>
        /// <param name="invocation"></param>
        private void PreProceed(IInvocation invocation)
        {
            //創建一個唯一的標識
            //創建一個對象,並記錄開始時間

            //dt = DateTime.Now;
            //Console.WriteLine($"開始執行,方法名稱:{invocation.Method.Name},開始時間:{dt.ToString("yyyy-MM-dd HH:mm:ss fff")}");
            //Console.WriteLine();

            MethodOperationInfo.SetStartTime(invocation);
        }

        /// <summary>
        /// 方法執行后
        /// </summary>
        /// <param name="invocation"></param>
        private void PostProceed(IInvocation invocation)
        {
            //通用標識找到之前創建的那個對象,並記錄結束時間
            
            //ts = DateTime.Now - dt;
            //Console.WriteLine($"執行完成,方法名稱:{invocation.Method.Name}");
            //Console.WriteLine($"    開始時間:{dt.ToString("yyyy-MM-dd HH:mm:ss fff")}");
            //Console.WriteLine($"    結束時間:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}");
            //Console.WriteLine($"    所用時間:{ts.TotalSeconds}");
            //Console.WriteLine();
            
            MethodOperationInfo.SetEndTime(invocation);
        }

        /// <summary>
        /// 攔截
        /// </summary>
        /// <param name="invocation"></param>
        public void Intercept(IInvocation invocation)
        {
            this.PreProceed(invocation);
            invocation.Proceed();//調用
            this.PostProceed(invocation);
        }
    }


    
    /// <summary>
    /// 擴展
    /// </summary>
    public static class IntExtension
    {
        public static string ToString(this object obj, int len, bool afterFill = true)
        {
            string name = obj.ToString();
            //int count = len - name.Length;//不能用這個 漢字和英文占用的長度不同
            int count = len - System.Text.Encoding.Default.GetBytes(name).Length;

            if (afterFill)
            {
                for (int i = 0; i < count; i++)
                {
                    name += " ";
                }
                return name;

            }
            else
            {
                string value = "";
                for (int i = 0; i < count; i++)
                {
                    value += " ";
                }
                value += name;
                return value;
            }
        }
    }



    /// <summary>
    /// 操作日志
    /// </summary>
    public class MethodOperationInfo
    {
        public string NameSpaceName { get; set; }
        public string ClassName { get; set; }
        public string MethodName { get; set; }
        public string Parameters { get; set; }
        public string ParameterTypes { get; set; }
        public double TotalMilliseconds { get; set; }


        public string Name { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public bool IsEnd { get; set; }//標識這個對象是否完成計算耗時
        
        




        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="invocation"></param>
        public MethodOperationInfo(IInvocation invocation)
        {
            StartTime = DateTime.Now;
            NameSpaceName = invocation.TargetType.Namespace;
            ClassName = invocation.TargetType.Name;
            MethodName = invocation.Method.Name;//方法名
            ParameterTypes = GetParameterTypes(invocation);//參數類型
            Parameters = GetParameter(invocation);//參數
            Name = GetName(invocation);
            IsEnd = false;
        }


        /// <summary>
        /// 操作完成
        /// </summary>
        /// <param name="invocation"></param>
        public void OperationFinish(IInvocation invocation)
        {
            EndTime = DateTime.Now;
            TotalMilliseconds = (EndTime - StartTime).TotalMilliseconds;
            IsEnd = true;
        }





        //存放 所有的 詳細信息
        public static List<MethodOperationInfo> operationInfos = new List<MethodOperationInfo>();
        

        /// <summary>
        /// 設置開始時間
        /// </summary>
        /// <param name="invocation"></param>
        public static void SetStartTime(IInvocation invocation)
        {
            MethodOperationInfo model = new MethodOperationInfo(invocation);
            operationInfos.Add(model);
        }

        /// <summary>
        /// 設置結束時間
        /// </summary>
        /// <param name="invocation"></param>
        public static void SetEndTime(IInvocation invocation)
        {
            MethodOperationInfo model = GetModel(invocation);
            model.OperationFinish(invocation);
        }

        /// <summary>
        /// 獲取一個對象
        /// </summary>
        /// <param name="invocation"></param>
        /// <returns></returns>
        public static MethodOperationInfo GetModel(IInvocation invocation)
        {
            string name = GetName(invocation);
            return operationInfos.Where(c => (c.Name == name) && !c.IsEnd).FirstOrDefault();
        }

        /// <summary>
        /// 獲取 這個方法 唯一的編號,針對方法而已
        /// </summary>
        /// <param name="invocation"></param>
        /// <returns></returns>
        public static string GetName(IInvocation invocation)
        {
            string NameSpaceName = invocation.TargetType.Namespace;
            string ClassName = invocation.TargetType.Name;
            string MethodName = invocation.Method.Name;//方法名
            string ParameterTypes = GetParameterTypes(invocation);
            string Parameters = GetParameter(invocation);//參數

            string strId = $"{NameSpaceName},{ClassName},{MethodName},{ParameterTypes}";

            //MD5加密
            byte[] result = Encoding.Default.GetBytes(strId);
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] output = md5.ComputeHash(result);
            return BitConverter.ToString(output).Replace("-", "");
        }


        /// <summary>
        /// 獲取方法的參數類型
        /// 如:(System.String, System.Object, System.Int32)
        /// </summary>
        /// <param name="invocation"></param>
        /// <returns></returns>
        public static string GetParameterTypes(IInvocation invocation)
        {
            MethodInfo mInfo = invocation.Method;
            ParameterInfo[] pInfos = mInfo.GetParameters();

            string str = "";
            str += "(";
            for (int j = 0; j < pInfos.Length; j++)
            {
                var p = pInfos[j];
                string pTypeName = $"{p.ParameterType.ToString()}, ";
                if (p.ParameterType.IsGenericType && (p.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>)))
                {
                    pTypeName = $"{Nullable.GetUnderlyingType(p.ParameterType).Name}?, ";
                }
                str += pTypeName;
            }
            str = str.TrimEnd(' ').TrimEnd(',');
            str += ")";

            return str;
        }

        /// <summary>
        /// 獲取方法的參數
        /// </summary>
        /// <param name="invocation"></param>
        /// <returns></returns>
        public static string GetParameter(IInvocation invocation)
        {
            string Parameters = "";//參數
            if ((invocation.Arguments != null) && (invocation.Arguments.Length > 0))
            {
                Parameters = JsonConvert.SerializeObject(invocation.Arguments);
            }
            return Parameters;
        }


        /// <summary>
        /// 顯示日志
        /// </summary>
        /// <param name="ShowDetailRecord">是否顯示詳情記錄-比較占用時間</param>
        /// <param name="IsFilter">是否開啟過濾</param>
        public static void Show(bool ShowDetailRecord = true, bool IsFilter = false)
        {
            StringBuilder sb = new StringBuilder();
            DateTime beforDT = DateTime.Now;
            List<string> list_Show_Method = new List<string>() { "GetSingle_Value1" };//可改為配置參數



            //每個方法-耗時            
            string str = "";
            double totalMilliseconds_AllMethod = 0;


            //去重
            var operationInfos_NotRepeat = operationInfos.GroupBy(c => c.Name).Select(c => c.First()).ToList();

            //分組統計
            operationInfos.GroupBy(c => c.Name).Select(c => new
            {
                Name = c.Key,
                ExecuteNumber = c.Count(),
                TotalMilliseconds = c.Sum(a => a.TotalMilliseconds)
            }).ToList().ForEach(c =>
            {
                var item = operationInfos_NotRepeat.Where(x => x.Name == c.Name).First();
                totalMilliseconds_AllMethod += item.TotalMilliseconds;

                str += $"命名空間:{item.NameSpaceName.ToString(40)}     ";
                str += $"類名:{item.ClassName.ToString(30)}     ";
                str += $"方法:{item.MethodName.ToString(80)}     ";
                str += $"次數:{c.ExecuteNumber.ToString(10)}     ";
                str += $"耗時:{c.TotalMilliseconds.ToString(10, false) }毫秒     ";
                str += $"\r\n";
            });
            sb.Append(str + "\r\n\r\n");

            //方法-總耗時
            str = "";
            str += $"所有方法-耗時:{totalMilliseconds_AllMethod}毫秒    ";
            str += $"{totalMilliseconds_AllMethod / 1000}秒    ";
            str += $"{(totalMilliseconds_AllMethod / 1000 / 60).ToString("f2")}分鍾    ";
            str += $"當前時間:{DateTime.Now}    ";
            sb.Insert(0, str + "\r\n\r\n");



            #region 方法 每次耗時的 詳情信息

            if (ShowDetailRecord)
            {
                for (int i = 0; i < operationInfos.Count; i++)
                {
                    Console.WriteLine($"處理數據-當前行:{operationInfos.Count - i}");
                    var item = operationInfos[i];

                    //數據過濾                   
                    if (IsFilter && !list_Show_Method.Contains(item.MethodName))
                    {
                        continue;
                    }

                    sb.Append($"命名空間:{item.NameSpaceName.ToString(40)}     ");
                    sb.Append($"類名:{item.ClassName.ToString(30)}     ");
                    sb.Append($"方法:{item.MethodName + item.ParameterTypes.ToString(80)}     ");
                    sb.Append($"耗時:{item.TotalMilliseconds.ToString(10, false) }毫秒     ");
                    sb.Append($"參數:{item.Parameters.ToString(50)}     ");
                    sb.Append($"\r\n");
                }
            }
            #endregion

            //計算日志-耗時
            sb.Insert(0, $"計算日志-耗時:{DateTime.Now.Subtract(beforDT).TotalSeconds.ToString("#0.00000")}秒 \r\n\r\n");


            System.IO.File.WriteAllText($"LOG_{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.txt", sb.ToString());
            Console.WriteLine("完成!");
        }

        /// <summary>
        /// 顯示日志 包含詳情數據
        /// </summary>
        public static void ShowContainsDetail()
        {
            Show(true);
        }

        /// <summary>
        /// 顯示日志 不包含詳情數據
        /// </summary>
        public static void ShowNotContainsDetail()
        {
            Show(false);
        }
    }




    

    /// <summary>
    /// 測試類1
    /// </summary>
    public class Class5_test1
    {
        public virtual void test1()
        {
            Console.WriteLine("主方法-test1-開始執行");
            Console.WriteLine();

            for (int i = 0; i < 4; i++)
            {
                test4();
            }
            for (int i = 0; i < 3; i++)
            {
                test3();
            }
            for (int i = 0; i < 2; i++)
            {
                test2();
            }

            Console.WriteLine("主方法-test1-執行完成");
            Console.WriteLine();
        }
        public virtual void test2()
        {
            System.Threading.Thread.Sleep(200);
        }
        public virtual void test3()
        {
            System.Threading.Thread.Sleep(300);
        }
        public virtual void test4()
        {
            System.Threading.Thread.Sleep(400);
        }
    }








}
View Code

 

但是當要代理的那個的類,如果沒有無參構造函數,這種方法就還是用不了,准備解決這個問題,解決了到時候再貼上解決方案。

 

本來是准備用到生產環境測試一下自己的代碼的,結果,我去,有參構造函數,接口,多態,麻蛋都有啊,改好了,再繼續貼代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM