C#日志模块 07

本文详细介绍了如何使用C#构建一个日志模块,包括日志信息模型、数据库交互、DAL层、BLL层以及UI层的设计。模块支持日志的插入、删除、查询和分页显示,日志存储选用MySQL,并提供了根据日期和类型查询的功能。此外,还涵盖了实时日志的处理,通过队列和后台任务实现日志的写入和界面显示。

C#日志模块

日志模块是生产过程中记录,软件运行信息的重要文件,包含了软件的运行日志,警告日志,和错误日志。方便事后程序员排查问题

首先要确定需要的参数,ID是必须的,其次日志得含有日期,标题,内容,日志类型。查询日志一般来说是通过开始时间,结束时间,日志类型来查询。
model层建立LogsInfo

public class LogsInfo
    {
        public int LId { get; set; }
        public string LDataTime { get; set; }
        public string LTitle { get; set; }
        public string LContent { get; set; }
        public LogsType LType { get; set; }
    }
    public enum LogsType
    {
       所有信息,
       错误信息,
       警告信息,
       运行信息
    }
    public class LogsQuery
    {
        public DateTime starttime { get; set; }
        public DateTime endtime { get; set; }
        public LogsType LType { get; set; }
    }

这里因为日志一般数据量很大,且保存时间较久,这里不使用sqlite,我们改用mysql
通过上面参数设计mysql表格
在这里插入图片描述
Dal层建立mysql通用方法类

public class MysqlHelper
    {
        private static readonly string conStr = "Database='mydatabase';Data Source='localhost';Port='3360';User Id='root';Password='123456';charset='utf8';pooling=true";
        /// <summary>
        /// 1、执行增(insert) 删(delete) 改(update) 的方法
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pms"></param>
        /// <returns></returns>
        public static int ExecuteNonQuery(string sql, params MySqlParameter[] pms)
        {
            using (MySqlConnection con = new MySqlConnection(conStr))
            {
                using (MySqlCommand cmd = new MySqlCommand(sql, con))
                {
                    if (pms.Length>0)
                    {
                        if (pms[0] != null)
                        {
                            cmd.Parameters.AddRange(pms);
                        }
                    }
                    con.Open();
                    return cmd.ExecuteNonQuery();
                }
            }
        }

        /// <summary>
        /// 2、执行查询,返回单个值的方法
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pms"></param>
        /// <returns></returns>
        public static object ExecuteScalar(string sql, params MySqlParameter[] pms)
        {
            using (MySqlConnection con = new MySqlConnection(conStr))
            {
                using (MySqlCommand cmd = new MySqlCommand(sql, con))
                {
                    if (pms != null)
                    {
                        cmd.Parameters.AddRange(pms);
                    }
                    con.Open();
                    return cmd.ExecuteScalar();
                }
            }
        }

        /// <summary>
        /// 3、执行查询,返回多行多列的方法
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pms"></param>
        /// <returns></returns>
        public static MySqlDataReader ExecuteReader(string sql, params MySqlParameter[] pms)
        {
            MySqlConnection con = new MySqlConnection(conStr);
            using (MySqlCommand cmd = new MySqlCommand(sql, con))
            {
                if (pms != null)
                {
                    cmd.Parameters.AddRange(pms);
                }
                try
                {
                    con.Open();
                    //这个枚举参数,表示将来使用完毕reader后,在关闭reader的
                    //同时,在sqldatareader内部会将关联的connection对象也关闭掉
                    return cmd.ExecuteReader
                        (System.Data.CommandBehavior.CloseConnection);
                }
                catch
                {
                    con.Close();
                    con.Dispose();
                    throw;
                }

            }

        }

        /// <summary>
        /// 4、查询数据返回DataTable
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pms"></param>
        /// <returns></returns>
        public static DataTable ExecuteDataTable(string sql, params MySqlParameter[] pms)
        {
            try
            {
                //构造连接对象
                using (MySqlConnection conn = new MySqlConnection(conStr))
                {
                    //构造桥接器对象
                    MySqlDataAdapter adapter = new MySqlDataAdapter(sql, conn);
                    //加参数查找
                    if (pms[0] != null)
                    {
                        adapter.SelectCommand.Parameters.AddRange(pms);
                    }
                    //数据表对象
                    DataTable table = new DataTable();
                    //将数据存到table中
                    adapter.Fill(table);
                    //返回数据表
                    return table;

                }
            }
            catch (Exception ex)
            {
                DataTable table = new DataTable();
                return table;

            }

        }

建立好通用方法后,我们接着在Dal声名类

public class LogsDal
    {
        /// <summary>
        /// 插入
        /// </summary>
        /// <param name="li"></param>
        /// <returns></returns>
        public int Insert(LogsInfo li)
        {
            //构造sql语句
            string sql =
                "insert into Logs(LDataTime,LTitle,LContent,LType) values(@datatime,@title,@content,@type)";
            //数组的初始化器
            MySqlParameter[] ps =
                {
                   new MySqlParameter("@datatime",li.LDataTime),
                   new MySqlParameter("@title",li.LTitle),
                   new MySqlParameter("@content",li.LContent),
                   new MySqlParameter("@type",li.LType.ToString()),
                };
            //执行
            return MysqlHelper.ExecuteNonQuery(sql, ps);
        }
        /// <summary>
        /// 删除n天以外的日志
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int DeleteTime(int date)
        {
            ///DELETE FROM 表名 WHERE 时间字段 BETWEEN 开始时间 AND 结束时间
            ///DELETE FROM 表名 WHERE 时间字段>=开始时间 AND 时间字段<=结束时间
            string sql = "delete From `logs` where DATE(LDataTime) <= DATE(DATE_SUB(NOW(),INTERVAL @time day)) ";
            MySqlParameter[] ps =
                {
                   new MySqlParameter("@time",date)
                };
            return MysqlHelper.ExecuteNonQuery(sql, ps);

        }
        /// <summary>
        /// 清空表
        /// </summary>
        /// <returns></returns>
        public int EmptyTable()
        {
            string sql = "truncate Logs";
            return MysqlHelper.ExecuteNonQuery(sql);
        }
        /// <summary>
        /// 删除在n-k之间的数据
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int Delete(DateTime startdate,DateTime endtime)
        {
            ///DELETE FROM 表名 WHERE 时间字段 BETWEEN 开始时间 AND 结束时间
            ///DELETE FROM 表名 WHERE 时间字段>=开始时间 AND 时间字段<=结束时间
            string sql = "delete From `logs` where LDataTime between @starttime and @endtime";
            MySqlParameter[] ps =
                {
                   new MySqlParameter("@starttime",startdate),
                   new MySqlParameter("@endtime",endtime)
                };
            return MysqlHelper.ExecuteNonQuery(sql, ps);

        }
        /// <summary>
        /// 通过时间来查找数据
        /// 包含开始时间,结束时间,信息类型
        /// </summary>
        /// <returns></returns>
        public List<LogsInfo> Getlist(LogsQuery lq)
        {
            List<LogsInfo> list = new List<LogsInfo>();
            List<MySqlParameter> sqllist = new List<MySqlParameter>();
            string sql = "select * from Logs where LDataTime>= @starttime and LDataTime<=@endtime";
            sqllist.Add(new MySqlParameter("@starttime", lq.starttime));
            sqllist.Add(new MySqlParameter("@endtime", lq.endtime));
            if (lq.LType != LogsType.所有信息)
            {
                sql = String.Concat(sql, " and LType=@type");
                sqllist.Add(new MySqlParameter("@type", lq.LType.ToString()));
            }
            using (MySqlDataReader reader = MysqlHelper.ExecuteReader(sql, sqllist.ToArray()))
            {
                if (reader.HasRows)
                {
                    while (reader.Read())
                    {
                        LogsInfo model = new LogsInfo()
                        {
                            LId = reader.GetInt32(0),
                            LDataTime = reader.GetString(1),
                            LTitle = reader.GetString(2),
                            LContent = reader.GetString(3),
                            LType = (LogsType)Enum.Parse(typeof(LogsType), reader.GetString(4))
                        };
                        list.Add(model);
                    }
                }
            }
            return list;
        }
    }

这里由于要对查询出来的数据进行整理分页
所以在COMMON层声名

public class LogsHelper
    {
        /// <summary>
        /// 把一个集合分割成一个集合为num个数的集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Lists"></param>
        /// <param name="num"></param>
        /// <returns></returns>
        public static List<List<T>> SpiltList<T>(List<T> Lists, int num) //where T:class
        {
            List<List<T>> fz = new List<List<T>>();
            //组数
            int GroupNum;
            //元素数量大于等于 一组的个数
            if (Lists.Count >= num)
            {
                if (Lists.Count % num == 0)
                {
                    GroupNum = Lists.Count / num;
                }
                else
                {
                    GroupNum = Lists.Count / num + 1;
                }
                //给每一组分配元素
                for (int i = 0; i < GroupNum; i++)
                {
                    List<T> cList = new List<T>();
                    if (i + 1 == GroupNum)
                    {
                        cList = Lists.Skip(num * i).ToList<T>();
                    }
                    else
                    {
                        cList = Lists.Skip(num * i).Take(num).ToList<T>();
                    }
                    fz.Add(cList);
                }
            }
            else
            {
                fz.Add(Lists);//元素数量小于分组数量
            }
            return fz;
        }
    }

随后我们在Bll层建立LogsBll

    #region << 版 本 注 释 >>
    /*----------------------------------------------------------------
    // Copyright (C) 2022 XXX单位
    // 版权所有。 
    //
    // 文件名:StandardSoftware
    // 文件功能描述:日志业务逻辑层
    //
    // 
    // 创建者:liangjinhao
    // 时间:2022/4/25 15:59:00
    // 功能:标准化软件
    //
    // 修改人:
    // 时间:
    // 修改说明:
    //
    // 版本:V1.0.0
    //----------------------------------------------------------------*/
    #endregion
    public class LogsBll
    {
        LogsDal Dal = new LogsDal();
        /// <summary>
        /// 私有化构造函数
        /// </summary>
        private LogsBll()
        {

        }
        /// <summary>
        /// 声名一个静态变量
        /// </summary>
        private static LogsBll LoList;
        /// <summary>
        /// 单例模式
        /// </summary>
        /// <returns></returns>
        public static LogsBll Create()
        {
            if (LoList == null)
            {
                LoList = new LogsBll();
            }
            return LoList;
        }
        /// <summary>
        /// 获得日志集合
        /// </summary>
        /// <returns></returns>
        private List<LogsInfo> GetList(LogsQuery lq)
        {
            return Dal.Getlist(lq);
        }
        /// <summary>
        /// 插入一行数据
        /// </summary>
        /// <param name="li"></param>
        /// <returns></returns>
        public bool Add(LogsInfo li)
        {
            return Dal.Insert(li) > 0;
        }
        /// <summary>
        /// 移除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool Remove(int date)
        {
            return Dal.DeleteTime(date) > 0;
        }
        /// <summary>
        /// 清空表格
        /// </summary>
        /// <returns></returns>
        public bool EmptyTable()
        {
            return Dal.EmptyTable() > 0;

        }
        /// <summary>
        /// 删除在n-k时间之间的数据
        /// </summary>
        /// <param name="startdate"></param>
        /// <param name="endtime"></param>
        /// <returns></returns>
        public bool Delete(DateTime startdate, DateTime endtime)
        {
            return Dal.Delete(startdate, endtime) > 0;
        }
        /// <summary>
        /// 查询日志  并将数据分割成 多个集合
        /// </summary>
        /// <param name="lq"></param>
        /// <returns></returns>
        public List<List<LogsInfo>> Getlists(LogsQuery lq)
        {
            return LogsHelper.SpiltList<LogsInfo>(GetList(lq), 50);
        }

    }

最后在UI层设计数据库查询界面
日志可通过日期+类型查询
将查询出来的日志50条为一页进行切割并显示查询的总条数,总页数
在这里插入图片描述

 public partial class LogsForm : Form
    {
        public LogsForm()
        {
            InitializeComponent();
            Init();
        }
        public void Init()
        {
            this.Text = "";
            this.dgvLogs.ReadOnly = true;
            cbxType.DataSource = Enum.GetNames(typeof(LogsType));
            _pages = 0;
        }
        LogsBll bll = LogsBll.Create();
        List<List<LogsInfo>> list=new List<List<LogsInfo>>();
        private int _pages;
        private void RefreshTips()
        {
            this.lblCurrentPage.Text =String.Format("第{0}页",_pages);
            this.lblAllPage.Text = String.Format("共{0}页",list.Count);
            this.lblNum.Text= String.Format("共{0}条记录",50 * (list.Count - 1) + list[list.Count - 1].Count); 
        }
        private void btnQuert_Click(object sender, EventArgs e)
        {
            this.BeginInvoke(new Action(() =>
            {
                Cursor.Current = Cursors.WaitCursor;
                LogsQuery lq = new LogsQuery()
                {
                    starttime = dataTimeStart.Value,
                    endtime = dataTimeEnd.Value,
                    LType = (LogsType)Enum.Parse(typeof(LogsType), cbxType.Text)
                };
                list = bll.Getlists(lq);
                List<LogsInfo> list1 = list[0];
                if (list.Count > 0)
                {
                    dgvLogs.DataSource = list[0];
                    _pages = 1;
                    RefreshTips();
                }
            }));
            Cursor.Current= Cursors.Default;
        }

        private void btnHomePage_Click(object sender, EventArgs e)
        {
            this.BeginInvoke(new Action(() =>
            {
                if (list.Count > 0)
                {
                    dgvLogs.DataSource = list[0];
                    _pages = 1;
                    RefreshTips();
                }
                else
                {
                    return;
                }
            }));

        }

        private void btnEndPage_Click(object sender, EventArgs e)
        {
            this.BeginInvoke(new Action(() =>
            {
                if (list.Count > 0)
                {
                    dgvLogs.DataSource = list[list.Count - 1];
                    _pages = list.Count;
                    RefreshTips();
                }
                else
                {
                    return;
                }
            }));           
        }

        private void btnPreviousPage_Click(object sender, EventArgs e)
        {
            this.BeginInvoke(new Action(() =>
            {
                if (list.Count > 0)
                {
                    if(_pages > 1)
                    {
                        _pages--;
                        dgvLogs.DataSource = list[_pages-1];
                        RefreshTips();
                    }
                }
                else
                {
                    return;
                }
            }));
        }

        private void btnNextPage_Click(object sender, EventArgs e)
        {
            this.BeginInvoke(new Action(() =>
            {
                if (list.Count > 0)
                {
                    if (_pages < list.Count)
                    {
                        _pages++;
                        dgvLogs.DataSource = list[_pages-1];
                        RefreshTips();
                    }
                }
                else
                {
                    return;
                }
            }));
        }

    }

设计删除日志的UI
在这里插入图片描述

public partial class LogsSetingFrom : Form
    {
        public LogsSetingFrom()
        {
            InitializeComponent();
        }
        public void Init()
        {
            this.FormBorderStyle = FormBorderStyle.None;
            this.ControlBox = false;
            this.Text = "";
        }
        LogsBll bll = LogsBll.Create();
        private void btnDelect_Click(object sender, EventArgs e)
        {
            bll.EmptyTable();
            MessageBox.Show("删除成功");
        }

        private void btnDelectBetween_Click(object sender, EventArgs e)
        {
            bll.Delete(dtpStart.Value, dtpend.Value);
            MessageBox.Show("删除成功");
        }
    }

软件实时运行的日志界面
首先需要先建立一个队列用来存储日志内容,通过任务或者线程来对日志进行不断地写入,显示
到界面上
Bll层声名

    #region << 版 本 注 释 >>
    /*----------------------------------------------------------------
    // Copyright (C) 2022 XXX单位
    // 版权所有。 
    //
    // 文件名:StandardSoftware
    // 文件功能描述:日志队列
    //
    // 
    // 创建者:liangjinhao
    // 时间:2022/4/25 15:59:00
    // 功能:标准化软件
    //
    // 修改人:
    // 时间:
    // 修改说明:
    //
    // 版本:V1.0.0
    //----------------------------------------------------------------*/
    #endregion
    public class LogsQueue
    {
        /// <summary>
        /// 日志队列    用于MainFrom中日志textbox控件展示日志使用
        /// </summary>
        private static ConcurrentQueue<LogsInfo> RunningLogQueue = new ConcurrentQueue<LogsInfo>();

        /// <summary>
        /// 向UI日志显示队列中添加一个对象
        /// </summary>
        /// <param name="log"></param>
        public static void RunningLogQueue_AddOne(LogsInfo log)
        {
            log.LDataTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            RunningLogQueue.Enqueue(log);
        }

        /// <summary>
        /// 从UI日志队列中取出一个对象
        /// </summary>
        /// <param name="log"></param>
        /// <returns></returns>
        public static bool RunningLogQueue_GetOne(out LogsInfo log)
        {
            return RunningLogQueue.TryDequeue(out log);
        }

        /// <summary>
        /// 判断UI日志队列中是否有对象
        /// </summary>
        /// <returns></returns>
        public static bool RunningLogQueue_HaveElement()
        {
            if (RunningLogQueue.Count <= 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }

接着继续在Bll层声名任务,这里需要使用到Action事件,用来传递数据到UI层

    #region << 版 本 注 释 >>
    /*----------------------------------------------------------------
    // Copyright (C) 2022 XXX单位
    // 版权所有。 
    //
    // 文件名:StandardSoftware
    // 文件功能描述:日志显示,写入数据库,保存本地等
    //
    // 
    // 创建者:liangjinhao
    // 时间:2022/4/25 15:59:00
    // 功能:标准化软件
    //
    // 修改人:
    // 时间:
    // 修改说明:
    //
    // 版本:V1.0.0
    //----------------------------------------------------------------*/
    #endregion
    public class TaskLogs
    {
        LogsBll logbll;
        SystemSetBll ssbll;
        //定义事件
        public event Action<LogsInfo> SentLogsTxt;
        public TaskLogs()
        {
            logbll = LogsBll.Create();
            ssbll= SystemSetBll.Create();
            Task_UpdateRunningLog();
        }
        private CancellationTokenSource tskQueue1_cts = null;
        private void Task_UpdateRunningLog()
        {
            tskQueue1_cts = new CancellationTokenSource();
            Task task1 = new Task(() => { UpdateRunningLog(); }, tskQueue1_cts.Token, TaskCreationOptions.LongRunning);
            task1.Start();
        }
        private void UpdateRunningLog()
        {
            try
            {
                while (!tskQueue1_cts.IsCancellationRequested)
                {
                    if (LogsQueue.RunningLogQueue_HaveElement())
                    {
                        LogsInfo log = null;
                        if (LogsQueue.RunningLogQueue_GetOne(out log))
                        {
                            logbll.Add(log);//写入数据库
                            if (SentLogsTxt != null)
                            {
                                SentLogsTxt(log);//显示界面
                            }
                            SaveLog(log);
                        }
                    }
                    Thread.Sleep(10);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void SaveLog(LogsInfo log)
        {
            try
            {
                string kk = ssbll.GetValue<string>(PName.日志存放地址);
                if (!Directory.Exists(ssbll.GetValue<string>(PName.日志存放地址)))
                { 
                    Directory.CreateDirectory(ssbll.GetValue<string>(PName.日志存放地址));
                }
                string df = DateTime.Now.ToLongDateString();
                string addressErrorLog = ssbll.GetValue<string>(PName.日志存放地址) + "\\" + df + @"_other.txt";
                if (!File.Exists(addressErrorLog)) File.Create(addressErrorLog).Close();
                StringBuilder strBuilderErrorMessage = new StringBuilder();
                strBuilderErrorMessage.Append("日期:" + log.LDataTime + "\r\n");
                strBuilderErrorMessage.Append("标题:" + log.LTitle + "\r\n");
                strBuilderErrorMessage.Append("内容:"+log.LContent + "\r\n");
                strBuilderErrorMessage.Append("类型:" + log.LType.ToString() + "\r\n");
                strBuilderErrorMessage.Append("___________________________________________________\r\n");

                using (FileStream fs = new FileStream(addressErrorLog, FileMode.Append, FileAccess.Write))
                {
                    StreamWriter sw = new StreamWriter(fs, Encoding.Default);
                    sw.WriteLine(strBuilderErrorMessage);
                    sw.Close();
                    fs.Close();
                }
            }
            catch (Exception ex) {
                throw ex;
            }
        }
    }

最后设计软件运行得实时日志UI
在这里插入图片描述这里使用委托来显示数据到界面上

public partial class LogsMainFrom : Form
    {
        public LogsMainFrom()
        {
            InitializeComponent();
            tlogs.SentLogsTxt += ShowLogs;          
        }
        TaskLogs tlogs=new TaskLogs();
        private void LogsMainFrom_Load(object sender, EventArgs e)
        {
            this.FormBorderStyle = FormBorderStyle.None;
            this.ControlBox = false;
            this.Text = "";
            this.tbxLogs.ReadOnly = true;
        }
        private void ShowLogs(LogsInfo logs)
        {
            Invoke(new Action(() =>
            {
                if (this.tbxLogs.Lines.Length > 1000) this.tbxLogs.Clear();//如果超过1000行,日志清零
                string msg = String.Concat(logs.LDataTime ,"  " ,logs.LTitle + "  " ,logs.LContent + "  " ,logs.LType.ToString() , "\r\n");
                this.tbxLogs.AppendText(msg);//显示日志到界面上
                this.tbxLogs.Select(this.tbxLogs.TextLength, 0);//光标定位到文本最后
            }));
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Abril_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值