C#日志系统之实时打印实现

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_33324878/article/details/81076185

        这个日志系统不算大,但也有一些含金量,初学者可以通过这个项目快速的掌握C#的一些用法,以及winform编程里面用到的思想。实现两大类功能,一类是实时日志,另一类是历史日志,下面是实时日志的界面。

                                                                       效果图

                                                                                        实时日志效果

历史日志效果图

   以上就是效果图,那么我们分析一下思路。日志系统中的实时日志和消息并没有多大区别,我们在VS2013下新建类库,名字就叫Loglib。建完之后,思考一个问题,我们描述一个消息和日志,最基本的就是要描述消息或日志的格式。那么常见的日志的格式都是分了很多等级的。那么代码就来了,我们先定义一个最基本的LogBase类,根据单一性原则,该类只负责描述消息的格式,并不决定日志的动作,代码如下:

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

namespace logLib
{
    public class LogBase
    {
        public string _Msg;
        public LogLevel _level;
        public LogBase(string Msg,LogLevel level )
        {
            _Msg = Msg;
            _level = level; 
        }
    }
    public enum LogLevel
    {
        Information =0,
        warrning,
        error
    }
}

           这里强调一个规范,类内的局部变量要用下划线+变量名。这个logBase实际上定义了消息的协议,就是说你想用这个库或者模块,那么你想传递的消息必然要遵循这个协议。可以看出除了消息原本的内容之外,还加了日志等级,等级用枚举描述。再想下一个问题,我们最终想把这个消息传递到某个窗口上,根据不同的需求,可能你有很多窗口想输出日志信息,可能你自己专门定制了一个专门用于日志打印的附加应用程序,那么我们现在要写的这个类要兼容这几种情况,怎么做?既然和窗口有关系,我们定义一个LogView类,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace logLib
{
   public class LogView
    {
       private static LogView _instance = null;
       private static readonly object InitLock = new object();
       private static List<TextBoxBase> _logList = null;

       private LogView()
       {
           _logList = new List<TextBoxBase>();
       }
       private static LogView Instance
       {
           get
           {
               if(_instance == null)
               {
                   lock(InitLock)
                   {
                       if(_instance == null)
                       {
                           _instance = new LogView();
                       }
                   }
               }
               return _instance;
           }
       }

       private bool AddLogView(TextBoxBase logBox)
       {
           if(_logList.Contains(logBox))
           {
               return false;
           }
           _logList.Add(logBox);
           return true;
       }

       private bool RemovelogView(TextBoxBase logBox)
       {
           if(_logList.Count == 0|| !_logList.Contains(logBox))
           {
               return false;
           }
           _logList.Remove(logBox);
           return true;
       }

       private bool Toast(LogBase msg)
       {
           if(_logList != null)
           {
               _logList.ForEach(logControl => logControl.Invoke(new Action(() =>
                   {
                       if (logControl.Lines.Length > 100)
                       {
                           logControl.Text = @"";
                       }
                       logControl.Text += msg._Msg;
                       logControl.Select(logControl.Text.Length, 0);
                       logControl.ScrollToCaret();
                   })));   
           }
           return true;
       }
       public static bool ToastLog(LogBase log)
       {
           return Instance.Toast(log);
       }

       public static bool AddLogBox(TextBoxBase logBox)
       {
           return Instance.AddLogView(logBox);
       }

       public static bool RemoveLogBox(TextBoxBase logBox)
       {
           return Instance.RemovelogView(logBox);
       }


    }
}

     在这里先不讲代码,先说说我自己的理解,我一直很惊叹于图形界面的底层实现,因为之前学的linux,linux的编程亦或者unix环境高级编程是不涉及图形界面的,这与windows不同,windows有三大模块,user,内核,GDI,也就是说windows操作系统原生的就为你提供了图形界面的接口。而linux不行。那么在面向对象思想的中,一切皆对象,正如在linux操作系统中一切皆文件一样,我们的每一个窗体,每一个控件都是类。都可以实例化成对象来进行传递。当懂得了这个道理的时候,一切都变的灵活了。现在我们来分析代码。当这个类可能被很多地方引用的时候,我们往往考虑单例设计模式。单例设计模式的实现首先是要把构造函数私有化,只有这样,外部的程序调用你的类就实例不了对象,那么为什么上锁呢,是为了防止多线程的访问造成并没有实现单例的后果。

    这里我们怎么实现窗体对象的传递呢,首先上述说明已经说过,任何窗体和控件都是对象。那么在这里我在logView中定义了AddlogView这个方法。这个方法可以理解为窗体的注册类,在外部调用这个方法,可以把窗体对象传递进来存在了一个列表里。这样的话就兼容了以上两种情况。

   操作这一段重点要说的是c#跨线程访问控件。解决这个问题有两种方式,第一种,直接写入一个语句,具体的百度吧,这种方式不推荐。可以让c#忽略安全问题。第二种 用委托的方式。Toast方法就是实现委托的方式,看官们 自己体会吧。

    当上述功能实现之后,我想定义一个接口,直接调用这个方法就可以实现实时打印日志,怎么办,首先我们互相提供接口的时候都是用静态类里的静态方法,好的,那么代码如下:

using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Windows.Forms;
using System.Text;



namespace logLib
{
    public static class log
    {
        public static void Pop(string Msg, LogLevel level)
        {

            LogBase msgEntry = new LogBase(Msg + Environment.NewLine, (LogLevel)(int)level);
            string log = String.Format(@"{0:mm:ss:} {1}: {2}", DateTime.Now,
                msgEntry._level,
                msgEntry._Msg);
            LogView.ToastLog(msgEntry);  
        }
    }
}

  Pop这个方法是最终调用方法,也就是说,在把窗口注册进去之后,我们就可以通过这个Pop方法实现实时打印,界面以及历史日志查询,下篇讲。

 

展开阅读全文

没有更多推荐了,返回首页