Overview
Various types of information need to be recorded in the process of program development and operation, and the specific recorded content needs to be set according to actual needs, such as: program operation Debug record, program operation error, general operation information, operation warning, interface operation warning, interface operation error, Interface general operation information, interface operation warnings, sql execution records, LuckTrace, etc. According to the frequency of log records, you can preset days, hours, minutes, and no records are required.
Logging operation is understood as "information queue". The information queue is essentially a multi-threaded key-value collection. Logging is to obtain information from the information queue and then write it to a file.
Create project
Based on the above requirements, create a base class to implement the logging function, create a solution NetDBLogger, add a DBLoggerBase project for logging to the solution, and add an application project DBLoggerSite to the solution.
base class definition
log frequency
Define the enumeration, create the LogRule.cs class file in the project DBLoggerBase, the code:
namespace DBLoggerBase { public enum LogRule { Day, Hour, Minute, No } }
log level
Define the log level enumeration, create the LogLevel.cs class file in the project DBLoggerBase, the code:
namespace DBLoggerBase { public enum LogLevel { /// <summary> /// Program running record, Debug mode /// </summary> AppTrace, /// <summary> /// program run error /// </summary> AppException, /// <summary> /// General running information, main log mode /// </summary> AppInfo, /// <summary> /// run warning, logged by the developer /// </summary> AppWarn, /// <summary> /// Interface running record, Debug mode /// </summary> ApiTrace, /// <summary> /// interface operation error /// </summary> ApiException, /// <summary> /// Interface general operation information, main log mode /// </summary> ApiInfo, /// <summary> /// Interface runtime warning, logged by the developer /// </summary> ApiWarn, /// <summary> ///sql execution record /// </summary> SqlTrace, /// <summary> /// lottery statistics record /// </summary> LuckTrace } }
log write
The log written by program debugging or running enters the key-value collection object, and then obtains information from the collection and writes it to the file in the set directory. The file path is determined by the LogPath set in the application configuration, and the class file Logger.cs is created. The code:
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Timers; namespace DBLoggerBase { public class Logger { /// <summary> /// log queue /// </summary> private static ConcurrentQueue<KeyValuePair<string, string>> _logQueue; /// <summary> ///log folder /// </summary> private string _logPath = System.Configuration.ConfigurationManager.AppSettings["LogPath"].ToString(); private int _size; private Timer _watcher; #region Instance private static readonly object _lockObject = new object(); private static volatile Logger _instance = null; /// <summary> /// Write logs, trigger write operations by queue capacity or timing /// </summary> /// <param name="capacity">Number of records, default is 100</param> /// <param name="seconds">milliseconds, default 60 seconds</param> public Logger(int size = 100, int milliseconds = 60000) { //Create the directory if it doesn't exist if (!Directory.Exists(this._logPath)) Directory.CreateDirectory(this._logPath); _logQueue = new ConcurrentQueue<KeyValuePair<string, string>>(); _size = size; _watcher = new Timer(milliseconds); _watcher.Elapsed += (o, e) => { Submit(); }; _watcher.Start(); } public static Logger Instance() { if (_instance == null) { lock (_lockObject) { if (_instance == null) { #if DEBUG _instance = new Logger(100, 3000); #else _instance = new Logger(); #endif } } } return _instance; } #endregion #region write log /// <summary> /// write log is an enqueue operation /// </summary> /// <param name="str"></param> public void Write(string s, string prefix, LogRule rule = LogRule.Day) { if (string.IsNullOrWhiteSpace(s)) return; DateTime dt = DateTime.Now; string val = string.Concat(dt, "\r\n", s); string key = string.Concat(prefix, GetKey(dt, rule)); Write(key, val); } public void Write(string key, string val) { _logQueue.Enqueue(new KeyValuePair<string, string>(key, val)); if (_logQueue.Count() >= _size) Submit(); } /// <summary> /// write log is an enqueue operation /// </summary> /// <param name="str"></param> public void Write(string s, LogLevel level = LogLevel.AppInfo, LogRule rule = LogRule.Day, long ms = 0) { string prefix = level.ToString(); if (ms > 0) ms = ms / 10 * 10; if (ms > 0) prefix = string.Concat("_", prefix, ms, "_"); Write(s, prefix, rule); } #endregion #region text log /// <summary> /// write text record /// </summary> public void Submit() { //Exclusively because the file can only be written by one process. StreamWriter writer = null; var dict = GetLogText(); string filename; FileInfo file; try { lock (_lockObject) { foreach (var kv in dict) { filename = string.Concat(kv.Key, ".txt"); file = new FileInfo(this._logPath + "/" + filename); //Create the file if it does not exist, true means append writer = new StreamWriter(file.FullName, true, Encoding.UTF8); writer.WriteLine(kv.Value); writer.Close(); } } } finally { if (writer != null) writer.Close(); } } private string GetKey(DateTime dt, LogRule rule) { string key; switch (rule) { case LogRule.Minute: key = dt.ToString("yyyyMMddHHmm"); break; case LogRule.No: key = ""; break; case LogRule.Day: key = dt.ToString("yyyyMMdd"); break; case LogRule.Hour: default: key = dt.ToString("yyyyMMddHH"); break; } return key; } /// <summary> /// get log text a dequeue operation /// Splicing data with the same primary key together to reduce the io operation of writing /// </summary> /// <returns></returns> private ConcurrentDictionary<string, string> GetLogText() { ConcurrentDictionary<string, string> dict = new ConcurrentDictionary<string, string>(); string key, val; do { KeyValuePair<string, string> kv; if (_logQueue.TryDequeue(out kv)) { key = kv.Key; val = string.Concat(kv.Value, "\r\n----------------------------------------\r\n"); dict.AddOrUpdate(key, val, (k, v) => string.Concat(v + "\r\n" + val)); } } while (_logQueue.Count > 0); return dict; } #endregion } }
log method
Provide some methods for application operations to write logs, create a class file DbLogger.cs, code:
using System; using System.Data; using System.Text; namespace DBLoggerBase { #region Logger public static class DbLogger { //Initialize the interface public static void LogWrite(long userId,int teacherId,string techName, string createTime, string valiTime) { StringBuilder sb = new StringBuilder(); sb.AppendLine(string.Concat("execution status: success!")); sb.AppendLine(string.Concat("Template title:Pre-sale end notice")); sb.AppendLine(string.Concat("user ID:" + userId)); sb.AppendLine(string.Concat("analyst ID:" + teacherId)); sb.AppendLine(string.Concat("call time:" + createTime)); sb.AppendLine(string.Concat("End Time:" + valiTime)); Logger.Instance().Write(sb.ToString(), LogLevel.AppInfo); } public static void LogWrite(string message,string userId) { StringBuilder sb = new StringBuilder(); sb.AppendLine(string.Concat("Message content:" + message + " user ID: " + userId)); Logger.Instance().Write(sb.ToString(), LogLevel.AppInfo); } public static void LogWriteMessage(string message) { StringBuilder sb = new StringBuilder(); sb.AppendLine(string.Concat("time:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " Message content:") + message); Logger.Instance().Write(sb.ToString(), LogLevel.AppInfo); } public static void LogException(Exception ex, string lastCommand = null) { StringBuilder sb = new StringBuilder(); if (!string.IsNullOrWhiteSpace(lastCommand)) sb.AppendLine(string.Concat("LastCommand:", lastCommand)); if (ex.InnerException != null) ex = ex.InnerException; sb.AppendLine(string.Concat("exception information: " + ex.Message)); sb.AppendLine(string.Concat("Error source:" + ex.Source)); sb.AppendLine(string.Concat("stack information:\r\n" + ex.StackTrace)); Logger.Instance().Write(sb.ToString(), LogLevel.AppInfo); } public static void LogTrace(IDbCommand cmd, long elapsedMilliseconds = 0) { StringBuilder sb = new StringBuilder(); if (elapsedMilliseconds > 0) sb.AppendLine(string.Format("execution time: {0} ms", elapsedMilliseconds)); sb.AppendLine(string.Format("CommandText: {0}", cmd.CommandText)); sb.AppendLine(string.Format("CommandType: {0}", cmd.CommandType)); sb.AppendLine(string.Format("Parameters: {0}", cmd.Parameters.Count)); foreach (IDbDataParameter m in cmd.Parameters) { sb.Append(string.Format("\tDirection: {0}", m.Direction)); sb.Append(string.Format("\tParameterName: {0}", m.ParameterName)); sb.Append(string.Format("\tDbType: {0}", m.DbType)); sb.AppendLine(string.Format("\tDbValue: {0}", m.Value)); } Logger.Instance().Write(sb.ToString(), LogLevel.SqlTrace, LogRule.Hour, elapsedMilliseconds); } public static void LogLuckTrace(string message,long elapsedMilliseconds = 0) { Logger.Instance().Write(message.ToString(), LogLevel.LuckTrace, LogRule.Day, elapsedMilliseconds); } } #endregion }
At this point, the classes required for the logging function have been created, which will be called in the application below.
App access
Create the Web page Index.aspx in the DBLoggerSite project. Here is an example of recording general information and running exceptions. The code:
using DBLoggerBase; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace DBLoggerSite { public partial class Index : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { try { var x = 10000; var y = decimal.Zero; DbLogger.LogWriteMessage("Index The page calls the logging function!"); var i = x / y; } catch (Exception ex) { DbLogger.LogException(ex, "Divide by zero exception"); } } } }
Open the log file in the D:\Logs\DBLogger directory, the contents are as follows:
2019/3/16 20:56:45
Time: 2019-03-16 20:56:45 Message content: The Index page has invoked the logging function!
----------------------------------------
2019/3/16 20:56:45
LastCommand: Divide by zero exception
Exception Information: Attempt to divide by zero.
Error source: mscorlib
stack info:
at System.Decimal.FCalDivide(Decimal& d1, Decimal& d2)
at System.Decimal.op_Division(Decimal d1, Decimal d2)
At DBLoggerSite.Index.Page_Load(Object sender, EventArgs e) location D:\NetDBLogger\DBLoggerSite\Index.aspx.cs: line number 20
----------------------------------------