C#学写了一个程序记录日志的方法(Log类)

1.错误和警告信息单独生产文本进行记录;

2.日志到一定内存阈值可以打包压缩,单独存储起来,修改字段MaxLogFileSizeForCompress的值即可;

3.Log类调用举例:Log.Txt(JB.信息,“日志记录内容”,"通道1");

using System;
using System.IO;
using System.Windows.Forms;
using ICSharpCode.SharpZipLib.Zip;namespace _程序日志
{// 定义日志级别枚举,虽然取消了日志级别过滤功能,但保留该枚举方便后续可能的扩展public enum JB{错误,  //ERROR,代表程序运行过程中出现的错误情况,通常是需要重点关注和排查问题的日志级别警告, //WARN,用于表示程序运行时出现的一些可能会引发问题的异常情况,但不像错误那样严重,起到提醒作用信息, //INFO,一般记录程序正常运行过程中的一些常规信息,用于记录流程、状态等内容调试  //DEBUG,主要用于开发阶段,帮助开发者调试程序,输出更详细的程序执行过程信息}public class Log{// 日志文件基础路径,可通过配置等方式修改,这里指定了默认的基础路径,后续所有日志文件都会基于此路径进一步构建具体的文件路径private static string _logFilePath = "D:\\Logs\\程序日志";// 日志文件大小阈值,这里可按需调整,单位为字节,当日志文件达到这个大小后,会触发相应的文件压缩等处理逻辑private static long MaxLogFileSizeForCompress = 100 * 1024 * 1024;// 存放压缩日志文件的文件夹路径,可按需配置,用于存放经过压缩处理后的日志文件,便于节省磁盘空间以及对历史日志进行归档管理private static string CompressFolderPath = "D:\\Logs\\压缩日志";// 用于标记当前正在写入的日志文件是文本A还是文本B,初始化为文本A,通过这个标记来实现类似双缓冲的机制,方便在文件切换、压缩等操作时保证日志记录的连续性private static bool isUsingTextA = true;// 记录当前正在使用的日志文件路径(文本A或文本B),会根据程序运行过程中的实际情况动态更新,指向当前真正进行日志写入操作的文件路径private static string currentLogFilePath;// 记录文本A的文件路径,专门用于记录文本A对应的文件完整路径,方便后续针对文本A进行一些特定操作,比如检查文件大小等private static string textAFilePath;// 新增用于记录错误日志的文件路径(文本A和文本B),在发现有“错误”级别日志时,对应文本A的错误日志文件路径会存储在这里,方便后续操作private static string errorTextAFilePath;// 对应文本B的错误日志文件路径,与isUsingTextA标记配合,当切换到文本B进行日志记录时,错误日志也会相应切换写入到这个文件路径对应的文件中(如果需要的话)private static string errorTextBFilePath;// 新增用于记录警告日志的文件路径(文本A和文本B),功能类似上述错误日志文件路径,用于存放“警告”级别日志对应的文件路径private static string warnTextAFilePath;// 文本B的警告日志文件路径,用于在相应阶段存储“警告”级别日志信息private static string warnTextBFilePath;// 辅助方法:确保文件夹存在private static void EnsureFolderExists(string folderPath){// 检查指定的文件夹路径对应的文件夹是否已经存在if (!Directory.Exists(folderPath)){try{// 如果文件夹不存在,则尝试创建该文件夹Directory.CreateDirectory(folderPath);}catch (IOException ex){// 如果在创建文件夹过程中出现IOException异常(比如权限不足、磁盘已满等原因导致无法创建文件夹)// 这里通过消息框向用户展示创建文件夹失败的异常信息,方便用户知晓问题所在MessageBox.Show($"创建文件夹 {folderPath} 失败,异常信息: {ex.Message}");// 重新抛出异常,让调用这个方法的上层代码知道创建文件夹出现了问题,以便进行进一步的处理,比如终止程序或者尝试其他恢复操作throw;}}}/// <summary>/// 保存txt文档,即实现日志记录功能,是整个日志记录模块的核心方法,接收日志级别、要保存的内容以及通道等参数来决定如何将日志信息保存到对应的文件中/// </summary>/// <param name="jb">日志级别(目前未使用级别过滤功能),通过传入不同的日志级别来区分不同重要程度的日志信息,方便后续查看和分析日志时筛选不同级别的内容</param>/// <param name="zhi">保存内容,即具体要记录到日志文件中的文本信息,描述了程序运行过程中发生的相关事件、状态等内容</param>/// <param name="tongdao">通道,可用于对日志进行分类,比如按照不同的功能模块、业务流程等划分不同的通道,便于对日志进行归类查看和管理</param>public static void Txt(Enum jb, string zhi, string tongdao){string basePath = _logFilePath;string year = DateTime.Now.ToString("yyyy-MM");//年月日文件夹,根据当前时间获取年份和月份信息,用于构建日志文件所在的年月文件夹路径,便于按时间对日志进行分类存储string passageway = tongdao;//通道文件夹,使用传入的通道参数作为文件夹名称,用于进一步细分日志文件的存储位置// 构建日志文件所在文件夹路径,将基础路径、通道文件夹和年月文件夹路径组合起来,形成完整的日志文件所在文件夹的路径string logFolderPath = Path.Combine(basePath, passageway, year);EnsureFolderExists(logFolderPath);// 拼接完整日志文件路径,文件名格式保持不变(这里以简单的日期格式为例,你可按需调整),以当前日期作为文件名(格式为yyyy-MM-dd.txt),方便按天查看和管理日志文件string filename = DateTime.Now.ToString("yyyy-MM-dd") + ".txt";if (isUsingTextA){textAFilePath = Path.Combine(logFolderPath, filename);currentLogFilePath = textAFilePath;// 同时确定错误和警告日志对应的文本A文件路径,在原文件名基础上添加相应前缀(Error_和WARN_),用于区分不同级别的专用日志文件errorTextAFilePath = Path.Combine(logFolderPath, "Error_" + filename);warnTextAFilePath = Path.Combine(logFolderPath, "WARN_" + filename);}else{currentLogFilePath = Path.Combine(logFolderPath, filename);// 同时确定错误和警告日志对应的文本B文件路径,同样添加相应前缀,以对应文本B的情况errorTextBFilePath = Path.Combine(logFolderPath, "Error_" + filename);warnTextBFilePath = Path.Combine(logFolderPath, "WARN_" + filename);}bool fileExists = File.Exists(currentLogFilePath);// 先处理文件已存在且未超过阈值的情况,直接打开并追加内容,这种情况下无需进行复杂的文件切换或压缩等操作,直接在现有文件末尾追加新的日志信息即可if (fileExists){long fileSize = new FileInfo(currentLogFilePath).Length;if (fileSize < MaxLogFileSizeForCompress){WriteToExistingFile(jb, zhi, currentLogFilePath);// 根据日志级别额外处理错误和警告日志写入对应的专用文件,调用该方法来处理将“错误”和“警告”级别日志分别写入对应的专用文件的操作HandleSpecialLogLevel(jb, zhi);return;}}// 如果文件不存在或者已超过阈值,执行创建文件或切换文件等逻辑,比如首次创建日志文件或者当前文件已满需要切换到新文件进行日志记录等情况FileStream fs = null;try{fs = fileExists ? File.Open(currentLogFilePath, FileMode.Append) : File.Create(currentLogFilePath);WriteToFileAndHandleSize(jb, zhi, fs, currentLogFilePath);}catch (IOException ex){MessageBox.Show($"打开或创建日志文件 {currentLogFilePath} 失败,异常信息: {ex.Message}");throw; // 重新抛出异常,避免程序继续执行可能导致的数据不一致等问题,确保上层代码知道日志文件操作出现了异常}finally{if (fs != null){fs.Close();}}}/// <summary>/// 根据日志级别处理错误和警告日志写入对应的专用文件,该方法根据传入的日志级别判断是“错误”还是“警告”级别,然后分别进行对应的文件写入操作/// </summary>private static void HandleSpecialLogLevel(Enum jb, string zhi){if (jb.ToString() == "错误"){string currentErrorFilePath = isUsingTextA ? errorTextAFilePath : errorTextBFilePath;EnsureErrorFileExists(currentErrorFilePath);WriteToSpecialFile(currentErrorFilePath, zhi);}else if (jb.ToString() == "警告"){string currentWarnFilePath = isUsingTextA ? warnTextAFilePath : warnTextBFilePath;EnsureWarnFileExists(currentWarnFilePath);WriteToSpecialFile(currentWarnFilePath, zhi);}}/// <summary>/// 确保错误日志文件存在,如果不存在则创建,该方法用于保证在写入“错误”级别日志到对应的专用文件时,文件是存在的,避免出现写入失败的情况/// </summary>private static void EnsureErrorFileExists(string errorFilePath){if (!File.Exists(errorFilePath)){try{// 使用File.Create创建文件,创建后需要手动关闭文件流,这里直接调用Close方法关闭File.Create(errorFilePath).Close();}catch (IOException ex){MessageBox.Show($"创建错误日志文件 {errorFilePath} 失败,异常信息: {ex.Message}");throw;}}}/// <summary>/// 确保警告日志文件存在,如果不存在则创建,与EnsureErrorFileExists类似,只是针对“警告”级别日志对应的专用文件进行存在性检查和创建操作/// </summary>private static void EnsureWarnFileExists(string warnFilePath){if (!File.Exists(warnFilePath)){try{File.Create(warnFilePath).Close();}catch (IOException ex){MessageBox.Show($"创建警告日志文件 {warnFilePath} 失败,异常信息: {ex.Message}");throw;}}}/// <summary>/// 向专用文件写入日志内容,负责将具体的日志内容按照一定格式写入到指定的专用文件(错误或警告日志文件)中/// </summary>private static void WriteToSpecialFile(string specialFilePath, string zhi){FileStream fs = null;try{// 以追加模式打开指定的专用文件,以便在文件末尾添加新的日志内容fs = File.Open(specialFilePath, FileMode.Append);using (StreamWriter sw = new StreamWriter(fs)){// 将写入位置定位到文件末尾,确保新的日志内容追加在已有内容之后sw.BaseStream.Seek(0, SeekOrigin.End);sw.Write($"操作时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}  内容:{zhi}\n");sw.Flush();}}catch (IOException ex){MessageBox.Show($"打开或创建特殊日志文件 {specialFilePath} 失败,异常信息: {ex.Message}");throw;}finally{if (fs != null){fs.Close();}}}/// <summary>/// 向已存在且未超过阈值的文件中写入内容,用于向已经存在且文件大小未超过设定阈值的普通日志文件中追加新的日志信息,按照一定格式写入日志内容并进行必要的异常处理/// </summary>private static void WriteToExistingFile(Enum jb, string zhi, string currentLogFilePath){FileStream fs = null;try{// 以追加模式打开当前的普通日志文件,准备写入新的日志内容fs = File.Open(currentLogFilePath, FileMode.Append);using (StreamWriter sw = new StreamWriter(fs)){// 将写入位置定位到文件末尾,使得新日志信息添加在文件已有内容的后面sw.BaseStream.Seek(0, SeekOrigin.End);// 获取当前日志级别字符串表示(虽然取消了日志级别过滤功能,但这里保留获取级别字符串表示的代码),用于在日志内容中记录当前日志的级别信息string zt = jb.ToString();sw.Write("[{0}] 操作时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "  内容:{1}\n", zt, zhi);sw.Flush();}}catch (IOException ex){MessageBox.Show($"写入日志到文件 {currentLogFilePath} 失败,异常信息: {ex.Message}");throw; // 重新抛出异常,保证数据完整性,不丢失此次写入数据的异常情况,确保日志记录的可靠性}finally{if (fs != null){fs.Close();}}}/// <summary>/// 向文件写入内容,并处理文件大小超过阈值的情况,在向普通日志文件写入内容后,检查文件大小是否超过阈值,如果超过则触发文件压缩、切换等相关处理逻辑/// </summary>private static void WriteToFileAndHandleSize(Enum jb, string zhi, FileStream fs, string currentLogFilePath){using (StreamWriter sw = new StreamWriter(fs)){// 将写入位置定位到文件末尾,确保日志内容按顺序追加到文件末尾sw.BaseStream.Seek(0, SeekOrigin.End);// 获取当前日志级别字符串表示(虽然取消了日志级别过滤功能,但这里保留获取级别字符串表示的代码),用于在日志记录中体现日志级别信息string zt = jb.ToString();sw.Write("[{0}] 操作时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "  内容:{1}\n", zt, zhi);sw.Flush();}// 获取文件大小(如果文件存在的话),只针对文本A进行大小检查,根据当前是否正在使用文本A以及文件是否存在来决定是否检查文件大小并进行后续处理if (isUsingTextA && File.Exists(currentLogFilePath)){long fileSize = new FileInfo(currentLogFilePath).Length;if (fileSize >= MaxLogFileSizeForCompress){HandleFileCompressionAndSwitch(currentLogFilePath);}}}/// <summary>/// 处理文件压缩以及切换使用的文件(从文本A切换到文本B),当文本A对应的日志文件大小达到阈值后,执行文件压缩操作,将文本A文件压缩保存到指定的压缩文件夹中,然后删除原文本A文件,并切换到使用文本B进行后续日志记录/// </summary>private static void HandleFileCompressionAndSwitch(string currentLogFilePath){// 确保压缩文件夹存在,如果不存在则创建,为即将进行的文件压缩操作准备好存放压缩文件的目标文件夹EnsureFolderExists(CompressFolderPath);// 生成压缩文件的文件名(文本A的压缩文件名,带上时分秒信息),在原日志文件名基础上添加当前时分秒信息,使得压缩文件名具有唯一性,便于区分不同时间压缩的文件string zipFileName = Path.GetFileNameWithoutExtension(currentLogFilePath) + "_" + DateTime.Now.ToString("HH-mm-ss") + ".zip";string zipFilePath = Path.Combine(CompressFolderPath, zipFileName);try{// 使用SharpZipLib进行文件压缩(文本A),创建输出的压缩文件流,然后基于此流创建ZipOutputStream对象用于实际的压缩操作,并设置较高的压缩级别(0 - 9,9为最高压缩比,可按需调整)以获得较好的压缩效果using (FileStream fsOut = File.Create(zipFilePath)){using (ZipOutputStream zipStream = new ZipOutputStream(fsOut)){zipStream.SetLevel(9);ZipEntry entry = new ZipEntry(Path.GetFileName(currentLogFilePath));zipStream.PutNextEntry(entry);using (FileStream fsIn = File.OpenRead(currentLogFilePath)){byte[] buffer = new byte[4096];int sourceBytes;do{sourceBytes = fsIn.Read(buffer, 0, buffer.Length);zipStream.Write(buffer, 0, sourceBytes);} while (sourceBytes > 0);}zipStream.CloseEntry();}}}catch (IOException ex){MessageBox.Show($"压缩日志文件 {currentLogFilePath} 失败,异常信息: {ex.Message}");throw; // 重新抛出异常,避免掩盖压缩失败问题,确保上层代码知道压缩操作出现了异常情况}try{// 删除原日志文件(文本A)(可根据实际需求考虑是否备份等其他操作)File.Delete(currentLogFilePath);}catch (IOException ex){MessageBox.Show($"删除原日志文件 {currentLogFilePath} 失败,异常信息: {ex.Message}");throw; // 重新抛出异常,防止后续出现文件冲突等问题}// 切换到使用文本B进行后续写入isUsingTextA = false;}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/888325.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux(centos) 环境部署,安装JDK,docker(mysql, redis,nginx,minio,nacos)

目录 1.安装JDK (非docker)1.1 将文件放在目录下&#xff1a; /usr/local/jdk1.2 解压至当前目录1.3 配置环境变量 2.安装docker2.1 验证centos内核2.2 安装软件工具包2.3 设置yum源2.4 查看仓库中所有docker版本&#xff0c;按需选择安装2.5 安装docker2.6 启动docker 并 开机…

电阻改善信号完整性

1.为什么电路端接电阻能改善信号完整性 由于电信号在PCB上传输&#xff0c;因此在PCB设计中可以把PCB走线认为是信号的通道&#xff0c;当该通道的 物理结构&#xff08;线宽&#xff0c;线到参考面的距离等&#xff09;发生变化&#xff0c;特别是有一些突变时&#xff0c;都会…

Java基础面试题,46道Java基础八股文(4.8万字,30+手绘图)

Java是一种广泛使用的编程语言&#xff0c;由Sun Microsystems&#xff08;现为Oracle Corporation的一部分&#xff09;在1995年首次发布。它是一种面向对象的语言&#xff0c;这意味着它支持通过类和对象的概念来构造程序。 Java设计有一个核心理念&#xff1a;“编写一次&am…

记录下nginx接口代理配置问题

其中api和api1是前面定义的upstream&#xff0c;ip相同只是端口不同。 一开始/api1/直接 像api一样 proxy_pass http://api1这样是不行的&#xff0c;因为会代理到 后端的 /api1/...接口&#xff0c;而后端实际接口地址是 /api/..... 所以必须像上面写法才能将外网的 /api…

高效流程图绘制:开发设计流程图利器

在选择画流程图的工具时&#xff0c;不同的项目和使用场景会决定最佳的工具。以下是几款常见的流程图工具&#xff0c;并结合具体项目使用场景提供建议&#xff1a; 1. Lucidchart 特点&#xff1a; 在线协作&#xff1a;支持多人实时协作&#xff0c;适合团队合作。模板丰富&…

【Python网络爬虫笔记】7-网络爬虫的搜索工具re模块

目录 一、网络爬虫中的正则表达式和re模块&#xff08;一&#xff09;数据提取的精确性&#xff08;二&#xff09;处理复杂的文本结构&#xff08;三&#xff09;提高数据处理效率 二、正则表达式的内涵&#xff08;一&#xff09;、常用元字符&#xff08;二&#xff09;、量…

Day4:生信新手笔记 — R语言简单命令与Rstudio配置

一、Rstudio的界面展示 (很像Matlab风格) 二、Rstudio设置字体大小 三、 用Rproject管理工作目录 工作目录(working directory) 即当前所在的目录&#xff0c;是脚本、图片、文件的默认保存位置&#xff0c;也是文件读取的默认位置。R语言只能和一个文件夹进行互动&#xff0…

koa中间件

文章目录 1. koa中间件简介2. 中间件类型1. 应用级中间件2. 路由级中间件3. 错误处理中间件4. 第三方中间件 3.中间件执行流程 1. koa中间件简介 在Koa中&#xff0c;中间件呈现为一个异步函数&#xff0c;该函数支持 async/await 语法&#xff0c;它接收两个参数&#xff1a;…

【开源】A060-基于Spring Boot的游戏交易系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看项目链接获取⬇️&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600个选题ex…

泷羽sec:shell编程(9)不同脚本的互相调用和重定向操作

声明&#xff1a; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

Pod Pending无法调度

根据您提供的Kubernetes调度警告信息&#xff0c;以下是可能的原因分析&#xff1a; Insufficient Memory: 有1个节点因为内存不足而无法调度Pod。这可能是因为该节点上已经运行的Pod消耗了大量内存&#xff0c;没有足够的资源来运行新的Pod。 Pod Affinity/Anti-Affinity: 有…

QT 左右 上下,拉伸 分配窗口大小

要的效果是以下&#xff1a; QT C 两个QWideget A B现在有放在一个窗口QWideget Test内&#xff0c;初始比例要2&#xff1a;8 ,现在我要 A B 两个窗口中间 当鼠标移到他中间时&#xff0c;有条线&#xff0c;可以左右移动来控件 A B 窗口所占的大小widgetB &#xff08;有 wi…

pyqt6简单应用

from PyQt6.QtWidgets import QWidget,QPushButton,QLineEdit,QLabel,QApplication from PyQt6.QtGui import QPixmap,QIcon,QCursor from PyQt6.QtCore import Qt from PyQt6 import QtCoreimport sysclass Ui_window(QWidget):def __init__(self):super().__init__()# 设置窗…

【Spring篇】初始Spring MVC框架之Spring MVC入门程序编写

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】【Mybatis篇】【Spring篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;Spring MVC概述 …

祖先序列重建结合机器学习改进双键还原酶-文献精读87

Ancestral Sequence Reconstruction Meets Machine Learning: Ene Reductase Thermostabilization Yields Enzymes with Improved Reactivity Profiles 祖先序列重建结合机器学习&#xff1a;酶还原酶热稳定化产生具有改进反应性特征的酶 摘要 烯还原酶&#xff08;EREDs&…

华为的USG6000为什么不能ping通

前言&#xff1a; 防火墙usg6000v的镜像 链接: https://pan.baidu.com/s/1uLRk0-hnHRTLYLx1Pnplow?pwdtymp 提取码: tymp 看了好多毒文章&#xff0c;感觉写作业更有意思&#xff0c;可以了解新的知识 内容&#xff1a; 首先看毒文章是这样说的&#xff0c;华为的防火墙是…

untiy之碰撞体编辑器

在进行游戏开发时经常会遇到复杂构造的物体&#xff0c;那么如何类似的物体增加碰撞体呢&#xff0c;通过unity自带的collider是很麻烦的&#xff0c;这里介绍一个插件 Easy Collider editor轻松解决这个需求 1. 打开easy collider editor编辑器 2. 选择要添加碰撞体的物体&…

【Leetcode】《双指针出击:多数和问题的“破阵之匙”,解锁高效算法密码》

前言 &#x1f31f;&#x1f31f;本期讲解关于双指针解决多数和问题~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…

【docker】Windows11创建Ubuntu-desktop并使用VNC完成远程访问

【docker】Windows11创建Ubuntu-desktop并使用VNC完成远程访问 文章目录 【docker】Windows11创建Ubuntu-desktop并使用VNC完成远程访问前言创建Ubuntu容器下载镜像运行容器连接容器 搭建容器XFCE桌面环境安装ubuntu桌面 总结 前言 docker ubuntu容器在深度学习领域的使用过程…

【Vulkan入门】01-列举物理设备

目录 先叨叨git信息主要逻辑VulkanEnvEnumeratePhysicalDevices()PrintPhysicalDevices() 编译并运行程序 先叨叨 上一篇已经创建了VkInstance&#xff0c;本篇我们问问VkInstance&#xff0c;在当前平台上有多少个支持Vulkan的物理设备。 git信息 repository: https://gite…