基于.NetCore的日志服务器

文章转载自平娃子(QQ:273206491):http://os.pingwazi.cn/resource/riziserver

系统日志是观察系统运行情况的窗口,它可以帮助我们快速定位错误、发现系统性能瓶颈甚至可以分析用户行为等。下面基于.NetCore开发的一套简单的日志服务器,供大家交流学习。

一、为什么要单独实现日志服务器:

1、系统日志操作(写日志)是一个频率非常高的操作,当系统用户量达到一定程度后,对于高频的日志操作很有可能会成为系统的性能瓶颈

2、实现日志处理与系统的解耦

3、有利于系统的扩展(如果日志单台日志服务器到达负载了,再加一台日志服务器就行)

4、利于日志的集中管理

二、采用什么方式实现日志服务器

1、日志的传输采用tcp协议进行传输

2、日志服务器是监听了指定ip和端口的socket服务器

3、服务器的日志操作采用多线程,充分利用服务器资源

三、如何实现日志服务器

1、安装.NetCore连接数据库的驱动程序(我这里使用的是mysql),便于将日志信息保存到数据中

Install-Package MySql.Data -Version 8.0.15

2、安装Log4Net,当日志服务器发生异常的时候记录异常日志到文件中,便于后期进行错误处理

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、实现LogHelper类,这个类主要用于文件日志的记

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>

        /// 调试信息

        /// </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);

            }

        }

        //错误信息

        public static void Error(string msg, Exception ex = null)

        {

            if (ex == null)

            {

                Logger.Error(msg);

            }

            else

            {

                Logger.Error(msg, ex);

            }

        }

    }

}

3、客户端数据处理类

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、获取客户端数据流

                NetworkStream networkStream = this._TcpClient.GetStream();

                //2、创建网络流中自定义数据类型(int类型的存储占用4个字节)

                byte[] dataLenByte = new byte[4];

                byte[] dataTypeByte = new byte[4];

                //3、获取自定义数据值

                int readCount = networkStream.Read(dataLenByte, 0, dataLenByte.Length);

                while (readCount > 0)

                {

                    //3.1、获取自定义数据

                    networkStream.Read(dataTypeByte, 0, dataTypeByte.Length);

                    int dataLen = BitConverter.ToInt32(dataLenByte, 0);

                    int dataType = BitConverter.ToInt32(dataTypeByte, 0);

                    if (dataLen > 0)

                    {

                        //读取数据

                        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;

                            }

                            //读取数据到缓冲区

                            byte[] dataBuffer = new byte[dataBufferReadCount];

                            int tempReadCount = networkStream.Read(dataBuffer, 0, dataBuffer.Length);

                            //拷贝数据到数据容器中

                            dataBuffer.CopyTo(data, readedDataLen);

                            readedDataLen += tempReadCount;

                        }

                        sb.Append($"({dataType},'{Encoding.UTF8.GetString(data)}'),");

                    }

                    //读取当前客户端连接的下一条数据

                    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($"保存日志信息发生异常:{ex}");

                LogHelper.Error("保存日志信息发生异常", ex);

            }

            finally

            {

                //关闭客户端连接

                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("日志服务器正在运行中...");

                    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("日志服务器正在运行中...");

                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 "请传正确的请求参数";

                    }

                    //发送日志数据

                    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);

                    }

                    //关闭与日志服务器的连接

                    tcpClient.Close();

                }

            }

            catch (Exception ex)

            {

                return ex.Message;

            }

            return "日志保存成功";

        }

    }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,614评论 0 3
  • 英文文档,一开始我也是抗拒的,边翻译边看,也就花费了1个小时基本就阅读过了,我的英文基础其实很差。附上链接:链接:...
    lonecolonel阅读 9,483评论 3 1
  • (http://www.cnblogs.com/zhangchenliang/p/4546352.html) 1、...
    凌雲木阅读 2,332评论 0 2
  • 本文是对FTP相关操作,例如新建/删除目录、新建/删除文件、获取文件/目录列表等的总结,方便日后调用。 其实C#操...
    Nmao阅读 1,153评论 0 0
  • 同频共振,播种富而有爱的种子 发愿: 愿正法久住,愿众生离苦得乐 时时与源头联结, 我之富有,与众生共享...
    福气东来阅读 572评论 0 0