必须填写至少10个字的日志
xi3892 authored at 2012-06-21 15:39:28
15.32 KiB
X
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using NewLife.Configuration;
using NewLife.Reflection;

namespace NewLife.Log
{
    /// <summary>ÈÕÖ¾À࣬°üº¬¸ú×Ùµ÷ÊÔ¹¦ÄÜ</summary>
    /// <remarks>
    /// ¸Ã¾²Ì¬Àà°üÀ¨Ð´ÈÕÖ¾¡¢Ð´µ÷ÓÃÕ»ºÍDump½ø³ÌÄÚ´æµÈµ÷ÊÔ¹¦ÄÜ¡£
    /// 
    /// ĬÈÏдÈÕÖ¾µ½Îı¾Îļþ£¬¿Éͨ¹ý¹Ò½Ó<see cref="OnWriteLog"/>ʼþÀ´Ôö¼ÓÈÕÖ¾Êä³ö·½Ê½¡£
    /// ¸Ä±äÈÕÖ¾Êä³ö·½Ê½ºó£¬¿Éͨ¹ý<see cref="UseFileLog"/>²»ÔÙÏòÎı¾ÎļþÊä³öÈÕÖ¾¡£
    /// ¶ÔÓÚ¿ØÖÆ̨¹¤³Ì£¬¿ÉÒÔÖ±½Óͨ¹ý<see cref="UseConsole"/>·½·¨£¬°ÑÈÕÖ¾Êä³öÖض¨ÏòΪ¿ØÖÆ̨Êä³ö£¬²¢ÇÒ¿ÉÒÔΪ²»Í¬Ïß³ÌʹÓò»Í¬ÑÕÉ«¡£
    /// </remarks>
    public static class XTrace
    {
        #region дÈÕÖ¾
        /// <summary>Îı¾ÎļþÈÕÖ¾</summary>
        public static TextFileLog Log = TextFileLog.Create(Config.GetConfig<String>("NewLife.LogPath"));

        private static Boolean _UseFileLog = true;
        /// <summary>ʹÓÃÎļþÈÕÖ¾</summary>
        public static Boolean UseFileLog { get { return _UseFileLog; } set { _UseFileLog = value; } }

        /// <summary>ÈÕ־·¾¶</summary>
        public static String LogPath { get { return Log.LogPath; } }

        /// <summary>Êä³öÈÕÖ¾</summary>
        /// <param name="msg">ÐÅÏ¢</param>
        public static void WriteLine(String msg)
        {
            if (OnWriteLog != null)
            {
                var e = new WriteLogEventArgs(msg);
                OnWriteLog(null, e);
            }

            if (UseFileLog) Log.WriteLine(msg);
        }

        /// <summary>Êä³öÒì³£ÈÕÖ¾</summary>
        /// <param name="ex">Òì³£ÐÅÏ¢</param>
        public static void WriteException(Exception ex)
        {
            if (OnWriteLog != null)
            {
                var e = new WriteLogEventArgs(null, ex);
                OnWriteLog(null, e);
            }
            if (UseFileLog) Log.WriteException(ex);
        }

        /// <summary>Êä³öÒì³£ÈÕÖ¾</summary>
        /// <param name="ex">Òì³£ÐÅÏ¢</param>
        public static void WriteExceptionWhenDebug(Exception ex)
        {
            if (Debug) Log.WriteLine(ex.ToString());
        }

        //private static event EventHandler<WriteLogEventArgs> _OnWriteLog;
        /// <summary>дÈÕ־ʼþ¡£°ó¶¨¸Ãʼþºó£¬XTrace½«²»ÔÙ°ÑÈÕ־дµ½ÈÕÖ¾ÎļþÖÐÈ¥¡£</summary>
        //public static event EventHandler<WriteLogEventArgs> OnWriteLog
        //{
        //    add { _OnWriteLog += value; UseFileLog = false; }
        //    remove { _OnWriteLog -= value; }
        //}
        public static event EventHandler<WriteLogEventArgs> OnWriteLog;

        /// <summary>дÈÕÖ¾</summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        public static void WriteLine(String format, params Object[] args)
        {
            if (OnWriteLog != null)
            {
                var msg = String.Format(format, args);
                var e = new WriteLogEventArgs(msg);
                OnWriteLog(null, e);
            }

            if (UseFileLog) Log.WriteLine(format, args);
        }
        #endregion

        #region ¹¹Ôì
        static XTrace()
        {
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        }
        #endregion

        #region ʹÓÿØÖÆ̨Êä³ö
        private static Int32 init = 0;
        /// <summary>ʹÓÿØÖÆ̨Êä³öÈÕÖ¾£¬Ö»Äܵ÷ÓÃÒ»´Î</summary>
        /// <param name="useColor"></param>
        public static void UseConsole(Boolean useColor = true)
        {
            if (init > 0 || Interlocked.CompareExchange(ref init, 1, 0) != 0) return;
            if (!Runtime.IsConsole) return;

            if (useColor)
                OnWriteLog += XTrace_OnWriteLog2;
            else
                OnWriteLog += XTrace_OnWriteLog;
        }

        private static void XTrace_OnWriteLog(object sender, WriteLogEventArgs e)
        {
            Console.WriteLine(e.ToString());
        }

        static Dictionary<Int32, ConsoleColor> dic = new Dictionary<Int32, ConsoleColor>();
        static ConsoleColor[] colors = new ConsoleColor[] { ConsoleColor.White, ConsoleColor.Yellow, ConsoleColor.Magenta, ConsoleColor.Red, ConsoleColor.Cyan, ConsoleColor.Green, ConsoleColor.Blue };
        private static void XTrace_OnWriteLog2(object sender, WriteLogEventArgs e)
        {
            lock (dic)
            {
                ConsoleColor cc;
                var key = e.ThreadID;
                if (!dic.TryGetValue(key, out cc))
                {
                    //lock (dic)
                    {
                        //if (!dic.TryGetValue(key, out cc))
                        {
                            cc = colors[dic.Count % 7];
                            dic[key] = cc;
                        }
                    }
                }
                var old = Console.ForegroundColor;
                Console.ForegroundColor = cc;
                Console.WriteLine(e.ToString());
                Console.ForegroundColor = old;
            }
        }
        #endregion

        #region À¹½ØWinFormÒì³£
        private static Int32 initWF = 0;
        private static Boolean _ShowErrorMessage;
        //private static String _Title;

        /// <summary>À¹½ØWinFormÒì³£²¢¼Ç¼ÈÕÖ¾£¬¿ÉÖ¸¶¨ÊÇ·ñÓÃ<see cref="MessageBox"/>ÏÔʾ¡£</summary>
        /// <param name="showErrorMessage">·¢Îª²¶»ñÒ쳣ʱ£¬ÊÇ·ñÏÔʾÌáʾ£¬Ä¬ÈÏÏÔʾ</param>
        public static void UseWinForm(Boolean showErrorMessage = true)
        {
            _ShowErrorMessage = showErrorMessage;

            if (initWF > 0 || Interlocked.CompareExchange(ref initWF, 1, 0) != 0) return;
            //if (!Application.MessageLoop) return;

            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
            Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
            //AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        }

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            XTrace.WriteLine("" + e.ExceptionObject);
            if (e.IsTerminating)
            {
                XTrace.WriteLine("Òì³£Í˳ö£¡");
                //XTrace.WriteMiniDump(null);
                if (_ShowErrorMessage && Application.MessageLoop) MessageBox.Show("" + e.ExceptionObject, "Òì³£Í˳ö", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                if (_ShowErrorMessage && Application.MessageLoop) MessageBox.Show("" + e.ExceptionObject, "³ö´í", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            XTrace.WriteLine(e.Exception.ToString());
            if (_ShowErrorMessage && Application.MessageLoop) MessageBox.Show("" + e.Exception, "³ö´í", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        #endregion

        #region ÊôÐÔ
        private static Boolean? _Debug;
        /// <summary>ÊÇ·ñµ÷ÊÔ¡£Èç¹û´úÂëÖ¸¶¨ÁËÖµ£¬ÔòÖ»»áʹÓôúÂëÖ¸¶¨µÄÖµ£¬·ñÔòÿ´Î¶¼¶ÁÈ¡ÅäÖá£</summary>
        public static Boolean Debug
        {
            get
            {
                if (_Debug != null) return _Debug.Value;

                try
                {
                    //return Config.GetConfig<Boolean>("NewLife.Debug", Config.GetConfig<Boolean>("Debug", false));
                    return Config.GetMutilConfig<Boolean>(false, "NewLife.Debug", "Debug");
                }
                catch { return false; }
            }
            set { _Debug = value; }
        }

        private static String _TempPath;
        /// <summary>ÁÙʱĿ¼</summary>
        public static String TempPath
        {
            get
            {
                if (_TempPath != null) return _TempPath;

                TempPath = Config.GetConfig<String>("NewLife.TempPath", "XTemp");
                return _TempPath;
            }
            set
            {
                _TempPath = value;
                if (String.IsNullOrEmpty(_TempPath)) _TempPath = "XTemp";
                if (!Path.IsPathRooted(_TempPath)) _TempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _TempPath);
                _TempPath = Path.GetFullPath(_TempPath);
            }
        }
        #endregion

        #region Dump
        /// <summary>дµ±Ç°Ï̵߳ÄMiniDump</summary>
        /// <param name="dumpFile">Èç¹û²»Ö¸¶¨£¬Ôò×Ô¶¯Ð´ÈëÈÕ־Ŀ¼</param>
        public static void WriteMiniDump(String dumpFile)
        {
            if (String.IsNullOrEmpty(dumpFile))
            {
                dumpFile = String.Format("{0:yyyyMMdd_HHmmss}.dmp", DateTime.Now);
                if (!String.IsNullOrEmpty(LogPath)) dumpFile = Path.Combine(LogPath, dumpFile);
            }

            MiniDump.TryDump(dumpFile, MiniDump.MiniDumpType.WithFullMemory);
        }

        /// <summary>
        /// ¸ÃÀàҪʹÓÃÔÚwindows 5.1 ÒÔºóµÄ°æ±¾£¬Èç¹ûÄãµÄwindowsºÜ¾É£¬¾Í°ÑWindbgÀïÃæµÄdll¿½±´¹ýÀ´£¬Ò»°ã¶¼Ã»ÓÐÎÊÌâ¡£
        /// DbgHelp.dll ÊÇwindows×Ô´øµÄ dllÎļþ ¡£
        /// </summary>
        static class MiniDump
        {
            [DllImport("DbgHelp.dll")]
            private static extern Boolean MiniDumpWriteDump(
            IntPtr hProcess,
            Int32 processId,
            IntPtr fileHandle,
            MiniDumpType dumpType,
           ref MinidumpExceptionInfo excepInfo,
            IntPtr userInfo,
            IntPtr extInfo);

            /// <summary>MINIDUMP_EXCEPTION_INFORMATION</summary>
            struct MinidumpExceptionInfo
            {
                public UInt32 ThreadId;
                public IntPtr ExceptionPointers;
                public UInt32 ClientPointers;
            }

            [DllImport("kernel32.dll")]
            private static extern uint GetCurrentThreadId();

            public static Boolean TryDump(String dmpPath, MiniDumpType dmpType)
            {
                //ʹÓÃÎļþÁ÷À´´´½¡ .dmpÎļþ
                using (FileStream stream = new FileStream(dmpPath, FileMode.Create))
                {
                    //È¡µÃ½ø³ÌÐÅÏ¢
                    Process process = Process.GetCurrentProcess();

                    // MINIDUMP_EXCEPTION_INFORMATION ÐÅÏ¢µÄ³õʼ»¯
                    MinidumpExceptionInfo mei = new MinidumpExceptionInfo();

                    mei.ThreadId = (UInt32)GetCurrentThreadId();
                    mei.ExceptionPointers = Marshal.GetExceptionPointers();
                    mei.ClientPointers = 1;

                    //ÕâÀïµ÷ÓõÄWin32 API
                    Boolean res = MiniDumpWriteDump(
                    process.Handle,
                    process.Id,
                    stream.SafeFileHandle.DangerousGetHandle(),
                    dmpType,
                   ref mei,
                    IntPtr.Zero,
                    IntPtr.Zero);

                    //Çå¿Õ stream
                    stream.Flush();
                    stream.Close();

                    return res;
                }
            }

            public enum MiniDumpType
            {
                None = 0x00010000,
                Normal = 0x00000000,
                WithDataSegs = 0x00000001,
                WithFullMemory = 0x00000002,
                WithHandleData = 0x00000004,
                FilterMemory = 0x00000008,
                ScanMemory = 0x00000010,
                WithUnloadedModules = 0x00000020,
                WithIndirectlyReferencedMemory = 0x00000040,
                FilterModulePaths = 0x00000080,
                WithProcessThreadData = 0x00000100,
                WithPrivateReadWriteMemory = 0x00000200,
                WithoutOptionalData = 0x00000400,
                WithFullMemoryInfo = 0x00000800,
                WithThreadInfo = 0x00001000,
                WithCodeSegs = 0x00002000
            }
        }
        #endregion

        #region µ÷ÓÃÕ»
        /// <summary>¶ÑÕ»µ÷ÊÔ¡£
        /// Êä³ö¶ÑÕ»ÐÅÏ¢£¬ÓÃÓÚµ÷ÊÔʱ´¦Àíµ÷ÓÃÉÏÏÂÎÄ¡£
        /// ±¾·½·¨»áÔì³É´óÁ¿ÈÕÖ¾£¬ÇëÉ÷Óá£
        /// </summary>
        public static void DebugStack()
        {
            var msg = GetCaller(2, 0, Environment.NewLine);
            WriteLine("µ÷ÓöÑÕ»£º" + Environment.NewLine, msg);
        }

        /// <summary>¶ÑÕ»µ÷ÊÔ¡£</summary>
        /// <param name="maxNum">×î´ó²¶»ñ¶ÑÕ»·½·¨Êý</param>
        public static void DebugStack(int maxNum)
        {
            var msg = GetCaller(2, maxNum, Environment.NewLine);
            WriteLine("µ÷ÓöÑÕ»£º" + Environment.NewLine, msg);
        }

        /// <summary>¶ÑÕ»µ÷ÊÔ</summary>
        /// <param name="start">¿ªÊ¼·½·¨Êý£¬0ÊÇDebugStackµÄÖ±½Óµ÷ÓÃÕß</param>
        /// <param name="maxNum">×î´ó²¶»ñ¶ÑÕ»·½·¨Êý</param>
        public static void DebugStack(int start, int maxNum)
        {
            // ÖÁÉÙÌø¹ýµ±Ç°Õâ¸ö
            if (start < 1) start = 1;
            var msg = GetCaller(start + 1, maxNum, Environment.NewLine);
            WriteLine("µ÷ÓöÑÕ»£º" + Environment.NewLine, msg);
        }

        /// <summary>»ñÈ¡µ÷ÓÃÕ»</summary>
        /// <param name="start"></param>
        /// <param name="maxNum"></param>
        /// <param name="split"></param>
        /// <returns></returns>
        public static String GetCaller(int start = 1, int maxNum = 0, String split = null)
        {
            // ÖÁÉÙÌø¹ýµ±Ç°Õâ¸ö
            if (start < 1) start = 1;
            var st = new StackTrace(start, true);

            if (String.IsNullOrEmpty(split)) split = "<-";

            Type last = null;
            var asm = Assembly.GetEntryAssembly();
            var entry = asm == null ? null : asm.EntryPoint;

            var sb = new StringBuilder();
            int count = st.FrameCount;
            if (maxNum > 0 && maxNum < count) count = maxNum;
            for (int i = 0; i < count; i++)
            {
                var sf = st.GetFrame(i);
                var method = sf.GetMethod();
                // Ìø¹ý<>ÀàÐ͵ÄÄäÃû·½·¨

                if (method == null || String.IsNullOrEmpty(method.Name) || method.Name[0] == '<' && method.Name.Contains(">")) continue;

                //var name = method.ToString();
                //// È¥µôÇ°ÃæµÄ·µ»ØÀàÐÍ
                //var p = name.IndexOf(" ");
                //if (p >= 0) name = name.Substring(p + 1);

                var mix = MethodInfoX.Create(method);
                var type = method.DeclaringType ?? method.ReflectedType;
                if (type != null)
                {
                    if (type != last)
                        sb.Append(mix.Name);
                    else
                        sb.Append(mix.TinyName);
                }
                else
                    sb.Append(mix.Name);
                //sb.Append(MethodInfoX.Create(method).Name);

                if (i < count - 1) sb.Append(split);

                last = type;

                // Èç¹ûµ½´ïÁËÈë¿Úµã£¬¿ÉÒÔ½áÊøÁË
                if (method == entry) break;
            }
            return sb.ToString();
        }
        #endregion
    }
}