文章轉(zhuǎn)載自平娃子(QQ:273206491):http://os.pingwazi.cn/resource/riziserver
系統(tǒng)日志是觀察系統(tǒng)運(yùn)行情況的窗口,它可以幫助我們快速定位錯(cuò)誤、發(fā)現(xiàn)系統(tǒng)性能瓶頸甚至可以分析用戶行為等。下面基于.NetCore開發(fā)的一套簡單的日志服務(wù)器,供大家交流學(xué)習(xí)。
一、為什么要單獨(dú)實(shí)現(xiàn)日志服務(wù)器:
1、系統(tǒng)日志操作(寫日志)是一個(gè)頻率非常高的操作,當(dāng)系統(tǒng)用戶量達(dá)到一定程度后,對于高頻的日志操作很有可能會成為系統(tǒng)的性能瓶頸
2、實(shí)現(xiàn)日志處理與系統(tǒng)的解耦
3、有利于系統(tǒng)的擴(kuò)展(如果日志單臺日志服務(wù)器到達(dá)負(fù)載了,再加一臺日志服務(wù)器就行)
4、利于日志的集中管理
二、采用什么方式實(shí)現(xiàn)日志服務(wù)器
1、日志的傳輸采用tcp協(xié)議進(jìn)行傳輸
2、日志服務(wù)器是監(jiān)聽了指定ip和端口的socket服務(wù)器
3、服務(wù)器的日志操作采用多線程,充分利用服務(wù)器資源
三、如何實(shí)現(xiàn)日志服務(wù)器
1、安裝.NetCore連接數(shù)據(jù)庫的驅(qū)動程序(我這里使用的是mysql),便于將日志信息保存到數(shù)據(jù)中
Install-Package MySql.Data -Version 8.0.15
2、安裝Log4Net,當(dāng)日志服務(wù)器發(fā)生異常的時(shí)候記錄異常日志到文件中,便于后期進(jìn)行錯(cuò)誤處理
Install-Package log4net -Version 2.0.8
2.1、配置Log4Net
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
? <!-- This section contains the log4net configuration settings -->
? <log4net>
? ? <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
? ? ? <file type="log4net.Util.PatternString" value="Log/" />
? ? ? <appendToFile value="true" />
? ? ? <rollingStyle value="Composite" />
? ? ? <staticLogFileName value="false" />
? ? ? <datePattern value="yyyyMMdd'.log'" />
? ? ? <maxSizeRollBackups value="10" />
? ? ? <maximumFileSize value="50MB" />
? ? ? <layout type="log4net.Layout.PatternLayout">
? ? ? ? <conversionPattern value="%date? [%thread]? %-5level? %message%newline" />
? ? ? </layout>
? ? </appender>
? ? <!-- Setup the root category, add the appenders and set the default level -->
? ? <root>
? ? ? <level value="ALL" />
? ? ? <appender-ref ref="RollingLogFileAppender" />
? ? </root>
? </log4net>
</configuration>
2.2、實(shí)現(xiàn)LogHelper類,這個(gè)類主要用于文件日志的記
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Repository;
using System;
using System.IO;
namespace LogServer
{
? ? /// <summary>
? ? /// 日志幫助類
? ? /// </summary>
? ? public class LogHelper
? ? {
? ? ? ? private static ILog Logger { get; set; }
? ? ? ? static LogHelper()
? ? ? ? {
? ? ? ? ? ? if (Logger == null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //log4net注冊
? ? ? ? ? ? ? ? ILoggerRepository loggerRepository = LogManager.CreateRepository("NETCoreRepository");
? ? ? ? ? ? ? ? XmlConfigurator.Configure(loggerRepository, new FileInfo("log4net.config"));
? ? ? ? ? ? ? ? Logger = LogManager.GetLogger(loggerRepository.Name, "InfoLogger");
? ? ? ? ? ? ? ? IAppender[] appenders = loggerRepository.GetAppenders();
? ? ? ? ? ? ? ? ///打印日志的保存路徑
? ? ? ? ? ? ? ? foreach (var appender in appenders)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? RollingFileAppender rollingFileAppender = appender as RollingFileAppender;
? ? ? ? ? ? ? ? ? ? Console.WriteLine(rollingFileAppender.File);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? /// <summary>
? ? ? ? /// 調(diào)試信息
? ? ? ? /// </summary>
? ? ? ? /// <param name="msg"></param>
? ? ? ? /// <param name="ex"></param>
? ? ? ? public static void Debug(string msg, Exception ex = null)
? ? ? ? {
? ? ? ? ? ? if (ex == null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Logger.Debug(msg);
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Logger.Debug(msg, ex);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? //錯(cuò)誤信息
? ? ? ? public static void Error(string msg, Exception ex = null)
? ? ? ? {
? ? ? ? ? ? if (ex == null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Logger.Error(msg);
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Logger.Error(msg, ex);
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
3、客戶端數(shù)據(jù)處理類
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace LogServer
{
? ? public class TcpClientManager
? ? {
? ? ? ? private TcpListener _TcpListener { get; set; }
? ? ? ? private TcpClient _TcpClient { get; set; }
? ? ? ? public TcpClientManager(TcpListener tcpListener, TcpClient tcpClient)
? ? ? ? {
? ? ? ? ? ? this._TcpClient = tcpClient;
? ? ? ? ? ? this._TcpListener = tcpListener;
? ? ? ? }
? ? ? ? public void DealClientData()
? ? ? ? {
? ? ? ? ? ? StringBuilder sb = new StringBuilder();
? ? ? ? ? ? try
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //1、獲取客戶端數(shù)據(jù)流
? ? ? ? ? ? ? ? NetworkStream networkStream = this._TcpClient.GetStream();
? ? ? ? ? ? ? ? //2、創(chuàng)建網(wǎng)絡(luò)流中自定義數(shù)據(jù)類型(int類型的存儲占用4個(gè)字節(jié))
? ? ? ? ? ? ? ? byte[] dataLenByte = new byte[4];
? ? ? ? ? ? ? ? byte[] dataTypeByte = new byte[4];
? ? ? ? ? ? ? ? //3、獲取自定義數(shù)據(jù)值
? ? ? ? ? ? ? ? int readCount = networkStream.Read(dataLenByte, 0, dataLenByte.Length);
? ? ? ? ? ? ? ? while (readCount > 0)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? //3.1、獲取自定義數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? networkStream.Read(dataTypeByte, 0, dataTypeByte.Length);
? ? ? ? ? ? ? ? ? ? int dataLen = BitConverter.ToInt32(dataLenByte, 0);
? ? ? ? ? ? ? ? ? ? int dataType = BitConverter.ToInt32(dataTypeByte, 0);
? ? ? ? ? ? ? ? ? ? if (dataLen > 0)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? //讀取數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? ? byte[] data = new byte[dataLen];
? ? ? ? ? ? ? ? ? ? ? ? int readedDataLen = 0;
? ? ? ? ? ? ? ? ? ? ? ? while (dataLen - readedDataLen > 0)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? int dataBufferReadCount = 0;
? ? ? ? ? ? ? ? ? ? ? ? ? ? if (dataLen - readedDataLen >= 1024)
? ? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dataBufferReadCount = 1024;
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dataBufferReadCount = dataLen - readedDataLen;
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? //讀取數(shù)據(jù)到緩沖區(qū)
? ? ? ? ? ? ? ? ? ? ? ? ? ? byte[] dataBuffer = new byte[dataBufferReadCount];
? ? ? ? ? ? ? ? ? ? ? ? ? ? int tempReadCount = networkStream.Read(dataBuffer, 0, dataBuffer.Length);
? ? ? ? ? ? ? ? ? ? ? ? ? ? //拷貝數(shù)據(jù)到數(shù)據(jù)容器中
? ? ? ? ? ? ? ? ? ? ? ? ? ? dataBuffer.CopyTo(data, readedDataLen);
? ? ? ? ? ? ? ? ? ? ? ? ? ? readedDataLen += tempReadCount;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? sb.Append($"({dataType},'{Encoding.UTF8.GetString(data)}'),");
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? //讀取當(dāng)前客戶端連接的下一條數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? readCount = networkStream.Read(dataLenByte, 0, dataLenByte.Length);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (!string.IsNullOrEmpty(sb.ToString()))
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? using (MySqlConnection conn =
? ? ? ? ? ? ? ? ? ? ? ? new MySqlConnection("server=127.0.0.1;database=Log;uid=root;pwd=root;charset=utf8;"))
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? using (MySqlCommand cmd = conn.CreateCommand())
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? conn.Open();
? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd.CommandText = $"insert into apilog(`Type`,`Msg`) values {sb.ToString().TrimEnd(',')}";
? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd.ExecuteNonQuery();
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? catch (Exception ex)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine($"保存日志信息發(fā)生異常:{ex}");
? ? ? ? ? ? ? ? LogHelper.Error("保存日志信息發(fā)生異常", ex);
? ? ? ? ? ? }
? ? ? ? ? ? finally
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //關(guān)閉客戶端連接
? ? ? ? ? ? ? ? this._TcpClient.Close();
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
4、主程序
using MySql.Data.MySqlClient;
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace LogServer
{
? ? class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? Thread tcpListenerThread = new Thread(() =>
? ? ? ? ? ? {
? ? ? ? ? ? ? ? IPEndPoint ipEndPoint=new IPEndPoint(IPAddress.Any,8888);
? ? ? ? ? ? ? ? TcpListener tcpServer = new TcpListener(ipEndPoint);
? ? ? ? ? ? ? ? tcpServer.Start(100);
? ? ? ? ? ? ? ? while (true)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? Console.WriteLine("日志服務(wù)器正在運(yùn)行中...");
? ? ? ? ? ? ? ? ? ? TcpClient tcpClient = tcpServer.AcceptTcpClient();
? ? ? ? ? ? ? ? ? ? Console.WriteLine("請求來了!開始處理...");
? ? ? ? ? ? ? ? ? ? TcpClientManager tcpClientManager = new TcpClientManager(tcpServer, tcpClient);
? ? ? ? ? ? ? ? ? ? Thread tcpClientThread = new Thread(tcpClientManager.DealClientData);
? ? ? ? ? ? ? ? ? ? tcpClientThread.IsBackground = true;
? ? ? ? ? ? ? ? ? ? tcpClientThread.Start();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ? ? ? ? tcpListenerThread.IsBackground = true;
? ? ? ? ? ? tcpListenerThread.Start();
? ? ? ? ? ? while (true)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine("日志服務(wù)器正在運(yùn)行中...");
? ? ? ? ? ? ? ? Console.ReadKey();
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
5、寫日志測試
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace ApiServer.Controllers
{
? ? [Route("api/[controller]")]
? ? [ApiController]
? ? public class ValuesController : ControllerBase
? ? {
? ? ? ? // GET api/values
? ? ? ? [HttpGet]
? ? ? ? public ActionResult<string> Get(string msg)
? ? ? ? {
? ? ? ? ? ? try
? ? ? ? ? ? {
? ? ? ? ? ? ? ? using (TcpClient tcpClient = new TcpClient("127.0.0.1", 8888))
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? if (string.IsNullOrEmpty(msg))
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? return "請傳正確的請求參數(shù)";
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? //發(fā)送日志數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? for (int i = 0; i < 100; i++)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? byte[] sourceData = Encoding.UTF8.GetBytes(msg);
? ? ? ? ? ? ? ? ? ? ? ? byte[] dataLen = BitConverter.GetBytes(sourceData.Length);
? ? ? ? ? ? ? ? ? ? ? ? byte[] dataType = BitConverter.GetBytes(1);
? ? ? ? ? ? ? ? ? ? ? ? byte[] sendData = new byte[sourceData.Length + 8];
? ? ? ? ? ? ? ? ? ? ? ? dataLen.CopyTo(sendData, 0);
? ? ? ? ? ? ? ? ? ? ? ? dataType.CopyTo(sendData, 4);
? ? ? ? ? ? ? ? ? ? ? ? sourceData.CopyTo(sendData, 8);
? ? ? ? ? ? ? ? ? ? ? ? tcpClient.Client.Send(sendData);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? //關(guān)閉與日志服務(wù)器的連接
? ? ? ? ? ? ? ? ? ? tcpClient.Close();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? catch (Exception ex)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? return ex.Message;
? ? ? ? ? ? }
? ? ? ? ? ? return "日志保存成功";
? ? ? ? }
? ? }
}