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

 

但是当要代理的那个的类,如果没有无参构造函数,这种方法就还是用不了,准备解决这个问题,解决了到时候再贴上解决方案。

 

本来是准备用到生产环境测试一下自己的代码的,结果,我去,有参构造函数,接口,多态,麻蛋都有啊,改好了,再继续贴代码

 

posted @ 2019-06-21 10:49  古兴越  阅读(3065)  评论(4编辑  收藏  举报