Work Time Manager [Open Source Project] - Create your own log component 2.0 refactoring

Keywords: C# github Windows

Hello all, I'm back again.

 

This time we really started to talk about the development ideas of small and useful modules or components in open source projects.

 

At the same time, the software has been updated to 1.60 version, supporting new users to register, you can no longer use a unified test account.

 

You can download it through the following paths:

1. Take a fellow project on GitHub, download it locally, generate it, and get the latest version of the program.

2. Local beta v0.5 users can click updates directly in the upper right corner of the program.

3. Finally, the following compression packages are given to non-Microsoft developers, which can be directly decompressed and used.

[Click Download]

 

 

 

If you don't know what software it is, check out this blog:

[WPF Material Design Example Open Source Project]Work Time Manager

 

Today, let's talk about the client's log components.

 

I don't know whether components are appropriate or not. Anyway, they are small modules that belong to software and can be reused. I call them components.

The log class is a typical component, and the log has many characteristics.

1. Will be used anywhere in the software

2. Called anytime

3. High utilization rate

4. Frequent io operations

 

 

When I first came into contact with c #, I used Log4net, but at that time, the idea was that a program might be only a few meters in size, and a log component would be better than my main program, which was obviously inappropriate.

So two years ago, before I graduated, I started to make my first log component.

[net] Create your own log component -- improved version

The basic ideas are good, including threading, blocking, resource competition and so on.

As the saying goes, newborn calves are not afraid of tigers, and they do it without knowing much about it.

Write the first version of the log component by opening threads.

 

 

However, after all, young, the problem is obvious. You can't hit 100 in a second.

So when I went back to c# development, I took some time to refactor it.

 

First, start with the overall architecture:
 
- Old component features:
* Multithread queues are used, and mutex variables are used to control the writing of text by threads.
* Controlling the contention for resources through single-case locking
* Threads are randomly selected to be locked, and the log written may not be in the right order of time.
* A thread operates on text once, and switches operate on one thread, writing only one variable at a time.
 
- Advantages:
* Multi-threaded operation, surface improves operation efficiency
* Single lock to ensure uniqueness
 
- Disadvantages:
* Under performance, overoperation of io results in severe performance redundancy
* Write only one at a time
 
- Improvement
* Use producer-consumer mode, operate separately, and limit the number of threads
* Use stack queue, first in first out, ensure log order
* Single IO variable, batch write operation
 
Revamping results:
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Threading;

namespace Helper
{
    public static class LogHelper
    {
        private static readonly Queue LogQueue = new Queue();

        private static bool _isStreamClose = true;

        private static bool _isThreadBegin = false;

        private static StreamWriter _fileStreamWriter;

        private static readonly string fileName =@"BugLog.txt";

        static int _intervalTime = 10000;// 10s

        static System.Timers.Timer _timer = new System.Timers.Timer(_intervalTime);

        /// <summary>
        /// Add a log queue
        /// </summary>
        /// <param name="message"></param>
        public static void AddLog(string message)
        {
            string logContent = $"[{DateTime.Now:yyyy-MM-dd hh:mm:ss}] =>{message}";
            LogQueue.Enqueue(logContent);
            if (!_isThreadBegin)
            {
                BeginThread();
            }
        }

        public static void AddLog(Exception ex)
        {
            var logContent = $"[{DateTime.Now:yyyy-MM-dd hh:mm:ss}]The error occurred in:{ex.Source},\r\n Contents:{ex.Message}";
            logContent += $"\r\n  Track:{ex.StackTrace}";
            LogQueue.Enqueue(logContent);
            if (!_isThreadBegin)
            {
                BeginThread();
            }
        }

        /// <summary>
        /// Read a piece of data from the log queue
        /// </summary>
        /// <returns></returns>
        private static object GetLog()
        {
            return LogQueue.Dequeue();
        }

        /// <summary>
        /// Open Timing Query Thread
        /// </summary>
        public static void BeginThread()
        {
            _isThreadBegin = true;

            //instantiation Timer Class, set the interval time to 10000 milliseconds;     

            _timer.Interval = _intervalTime;

            _timer.Elapsed += SetLog;

            //Execute the event at the time of arrival;   

            _timer.AutoReset = true;

            //Settings are executed once( false)It's still going on.(true);     

            _timer.Enabled = true;
        }


        /// <summary>
        /// Write to the log
        /// </summary>
        private static void SetLog(object source, System.Timers.ElapsedEventArgs e)
        {
            if (LogQueue.Count == 0)
            {
                if (_isStreamClose) return;
                _fileStreamWriter.Flush();
                _fileStreamWriter.Close();
                _isStreamClose = true;
                return;
            }
            if (_isStreamClose)
            {
                Isexist();
                string errLogFilePath = Environment.CurrentDirectory + @"\Log\" + fileName.Trim();
                if (!File.Exists(errLogFilePath))
                {
                    FileStream fs1 = new FileStream(errLogFilePath, FileMode.Create, FileAccess.Write);
                    _fileStreamWriter = new StreamWriter(fs1);
                }
                else
                {
                    _fileStreamWriter = new StreamWriter(errLogFilePath, true);
                }
                _isStreamClose = false;
            }

            var strLog = new StringBuilder();

            var onceTime = 50;

            var lineNum = LogQueue.Count > onceTime ? onceTime : LogQueue.Count;

            for (var i = 0; i < lineNum; i++)
            {
                strLog.AppendLine(GetLog().ToString());
            }

            _fileStreamWriter.WriteLine(strLog.ToString());

        }

        /// <summary>
        /// Determine whether a log file exists
        /// </summary>
        private static void Isexist()
        {
            string path = Environment.CurrentDirectory + @"\Log\";
            if (!File.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
        }
    }
}

 

The code has no application of third-party components and can be used by copying this file directly.

At present, there are no special cases to deal with, such as the occupation of log files, temporary shutdown of software, queue trigger time and the number of batch writes. This is only a basic demo.

Of course, if you want to know how to improve in the future, you can pay attention to the project. GitHub .

 

Of course, I look forward to your better suggestions, you learn together, you have good ideas, do not want to write, I will help you achieve!

 

Welcome everyone to spray, only criticism is the best driving force for progress!

 

At the same time: WorkTime Manager's online API is about to open. Welcome developers to develop the corresponding peripheral applications!

Please pay attention to: http://api.timemanager.online./

Posted by boushley on Sat, 22 Jun 2019 13:35:55 -0700