日志记录类(明确FileStreamDictionary等用法)

Posted 逗号-佳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日志记录类(明确FileStreamDictionary等用法)相关的知识,希望对你有一定的参考价值。

     一个好的程序对于日志的处理是必不可少的。好的日志记录可以帮助我们减少更好的查找错误和系统的维护。今天整理一下自己工作中平时用来记录日志的类,同时也补补基础知识。

     功能: 根据程序App.config中配置的路径,创建日志文件并将程序的日志写到相应的文件中。

     首先来看一下我之前自己写的一个用于写日志的类,源代码如下:

技术分享图片
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;

namespace LogHelp
{
    public class LogHelp
    {
        private string _filename;
        private static Dictionary<long, long> lockDic = new Dictionary<long, long>();

        //获取或设置文件名称
        public string FileName
        {
            get { return _filename; }
            set { _filename = value; }
        }

        //构造函数;根据传入的文件名,将日志写到相应的路径下的文件中。
        public LogHelp(string filename)
        {
            string folder = ConfigurationManager.AppSettings["LogPath"]+"\\"+System.DateTime.Now.Date.ToString("yyyyMMdd");
            if (!System.IO.Directory.Exists(folder))
                System.IO.Directory.CreateDirectory(folder);
            if (!filename.ToLower().EndsWith(".txt"))
            {
                filename = filename.Split(.)[0];
                filename += ".txt";
            }

            _filename = folder + "\\" + filename;
        }

        //创建文件
        public void Create(string fileName)
        {
            if (!System.IO.File.Exists(fileName))
            {
                using (System.IO.FileStream fs= System.IO.File.Create(fileName))
                {
                    fs.Close();
                }
            }
        }

        //写入文本
        private void Write(string content, string newLine)
        {
            if (string.IsNullOrEmpty(_filename))
            {
                throw new Exception("FileName不能为空!");
            }
            using(System.IO.FileStream fs=new System.IO.FileStream(_filename,System.IO.FileMode.OpenOrCreate,System.IO.FileAccess.ReadWrite,System.IO.FileShare.ReadWrite,8,System.IO.FileOptions.Asynchronous))
            {
                //FileStream只能处理字节,须通过编码将字符数据转换成字节。
                //新建字节型数组dataArrary对象,dataArrary对象得到了content+newLine的Encoding的值
                Byte[] dataArrary = System.Text.Encoding .Default.GetBytes(content+newLine);
                bool flag = true;
                long slen = dataArrary.Length;
                long len = 0;
                while (flag)
                {
                    try
                    {
                        if (len >= fs.Length)
                        {
                            fs.Lock(len, slen);
                            lockDic[len] = slen;
                            flag = false;
                        }
                        else
                        {
                            len = fs.Length;
                        }
                    }
                    catch (Exception ex)
                    {
                        while (!lockDic.ContainsKey(len))
                        {
                            len += lockDic[len];
                        }
                    }
                }
                fs.Seek(len, System.IO.SeekOrigin.Begin); //seek设置文件的读取和写入位置
                fs.Write(dataArrary, 0, dataArrary.Length);
                fs.Close();
            }
        }

        //写入文件内容
        public void WriteLine(string content)
        {
            this.Write(content, System.Environment.NewLine);
        }

        //写入文件
        public void Write(string content)
        {
            this.Write(content, "");
        }

    }
}
View Code

   这个类基本就可以实现日志的记录啦,当然都是很基础的功能哈。自己在住程序中创建实体类,直接调用即可啦。

   比如我在App.config文件中配置了路径: "D:\study\log", 在主程序中调用如下:

技术分享图片
public class Program
{
     private static LogHelp log = new LogHelp("LogTest");
     static void Main(string[] args)
     {
         log.WriteLine("Hello World");
     }
}
View Code

   直接运行程序,你就可以在D:\study\log\yyyymmdd\logtest.txt 文件中看到写入的”Hello World"啦。就这么简单,没什么可说的啦。

   好啦,接下来,我们来看一下日志类中用到的Dictionary 和FileStream 用法的一些总结,加深了解,多了解没坏处啦。

Dictionary的用法总结:    

   需要引入命名空间: System.Collections.Generic (程序集:mscorlib)

   Dictionary<string,string>是一个泛型,他本身有集合的功能有时可以把它看成数组;

   他的结构是这样的: Dictionary<[key],[value]>,

   他的特点是存入对象是需要与[key]值一一对应的存入该泛型;

   1. 用法一、常规用

       增加键值对之前需要判断是否存在该键,如果已经存在该键而且不判断,将抛出异常。所以这样每次都要进行判断,很麻烦,在备注使用了一个扩展方法:

    构建一个Dictionary: Dictionary<string, string> plist = new Dictionary<string,string>;

    读取Dictionary中的Key和Value,判断是否包含某个Key:  plist.ContainsKey("");

    遍历Key: foreach(var key in plist.keys);

    遍历Value: foreach(string value in plist.values);

    遍历Key和Value: foreach(var dic in plist) { dic.key,dic.value};

   2. 用法二、Dictionary的value为一个数组,代码示例如下:

public static void Sample()
{
   Dictionary<string,string[]> dic = new Dictionary<string,string[]>();
   string[] zhejiang = {"aa","bb","cc"};  
   string[] shanghai = {“pudong","waitan"};
   dic.Add("ZJ", zhejiang);
   dic.Add("SH",shanghai);
}

   3. 用法三、Dictionary的Value为一个类;

public static void Sample3()
{
   Dictionary<string, string> stuList = new Dictionary<string,string>();
   Student stu=null;
   for(int i=0; i<3;i++)
   {
      stu = new Student();
      stu.Num = i.ToString();
      stu.Name = "StuName"+i.ToString();
      stuList.Add(i.ToString(),stu);
   }
}

备注: Dictionary的扩展方法使用:

 
技术分享图片
public static void DicSample4()
{
       //1)普通调用
        Dictionary<int, String> dict = new Dictionary<int, String>();
        DictionaryExtensionMethodClass.TryAdd(dict, 1, "ZhangSan");
        DictionaryExtensionMethodClass.TryAdd(dict, 2, "WangWu");
        DictionaryExtensionMethodClass.AddOrPeplace(dict, 3, "WangWu");
        DictionaryExtensionMethodClass.AddOrPeplace(dict, 3, "ZhangWu");
        DictionaryExtensionMethodClass.TryAdd(dict, 2, "LiSi");

        //2)TryAdd 和 AddOrReplace 这两个方法具有较强自我描述能力,用起来很省心,而且也简单:
        dict.AddOrPeplace(20, "Orange");
        dict.TryAdd(21, "Banana");
        dict.TryAdd(22, "apple");

        //3)像Linq或jQuery一样连起来写   
        dict.TryAdd(10, "Bob")
              .TryAdd(11, "Tom")
              .AddOrPeplace(12, "Jom");
        //4) 获取值
        String F = "Ba";
        dict.TryGetValue(31, out F);
        Console.WriteLine("F : {0}",F);

        foreach (var dic in dict)
        {
            Console.WriteLine("Output : Key : {0}, Value : {1}", dic.Key, dic.Value);
        }
        //5)下面是使用GetValue获取值
        var v1 = dict.GetValue(111,null);
        var v2 = dict.GetValue(10,"abc");

        //6)批量添加
        var dict1 = new Dictionary<int,int>();
        dict1.AddOrPeplace(3, 3);
        dict1.AddOrPeplace(5, 5);

        var dict2 = new Dictionary<int, int>();
        dict2.AddOrPeplace(1, 1);
        dict2.AddOrPeplace(4, 4);
        dict2.AddRange(dict1, false);
  }
  扩展方法所在的类
    public static class DictionaryExtensionMethodClass 
    {
        /// <summary>
        /// 尝试将键和值添加到字典中:如果不存在,才添加;存在,不添加也不抛导常
        /// </summary>
        public static Dictionary<TKey, TValue> TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
        {
            if (dict.ContainsKey(key) == false)
                dict.Add(key, value);
            return dict;
        }

        /// <summary>
        /// 将键和值添加或替换到字典中:如果不存在,则添加;存在,则替换
        /// </summary>
        public static Dictionary<TKey, TValue> AddOrPeplace<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
        {
            dict[key] = value;
            return dict;
        }

        /// <summary>
        /// 获取与指定的键相关联的值,如果没有则返回输入的默认值
        /// </summary>
        public static TValue GetValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue defaultValue)
        {
            return dict.ContainsKey(key)?dict[key] : defaultValue;
        }

        /// <summary>
        /// 向字典中批量添加键值对
        /// </summary>
        /// <param name="replaceExisted">如果已存在,是否替换</param>
        public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> dict, IEnumerable<KeyValuePair<TKey, TValue>> values, bool replaceExisted)
        {
            foreach (var item in values)
            {
                if (dict.ContainsKey(item.Key) == false || replaceExisted)
                    dict[item.Key] = item.Value;
            }
            return dict;
        }


    }
View Code

   其他常见的属性和方法的说明:

   Comparer:  获取用于确定字段中键是否相等的IEqualityComparer;

   Count:        获取包含在Dictionary中键值对的数目;

   Item:           获取或设置与指定的键相关联的值;

   Keys:          获取包含Dictionary中键的集合;

   Values:       获取包含Dictionary中的值的集合;

   Add:            将指定的键和值添加到字典中;

   Clear:          从Dictionary中移除所有的键和值;

   ContainsKey: 确定Dictionary是否包含指定的键;

   ContainsValue: 确定Dictionary是否包含特定值;

   GetEnumerator: 返回循环访问Dictionary的枚举值;

   GetType:      获取当前实例的Type(从Object继承);

   Remove:      从Dictionary中移除所指定的键的值;

   ToString:      返回表示当前Object的String.(从Object继承);

   TryGetValue:  获取与指定的键相关联的值。

FileStream用法总结:

   引用命名空间: using System.IO

   FileStream类只能处理原始字节(raw byte)。FileStream类可以用于任何数据文件,而不仅仅是文本文件。FileStream对象可以用于读取诸如图像和声音的文件,FileStream读取出来的是字节数组,然后通过编码转换将字节数组转换成字符串。

   1. 读取文件:

       第一步: 声明一个FileStream类的对象:

       FileStream fsRead = new FileStream(string path, FileMode mode, FileAccess access);

       参数:

       path: 要操作文件的路径,路径可以是绝对路径或者相对路径;

       mode: 操作文件的方式,打开或者创建;

       access: 操作文件中的数据,读取或者写入。

       第二步: 调用fsRead对象的方法Read;

      下面方法是从文件中读取数据,再把数据写入一个字节数组;

       FileStream.Read(byte[] array, int offset, int count);

       参数:

       array: 用了存储fsRead对象读取到数据的字节数组;

       offset: 开始读取数据的位置,通常是0.

       count: 最多读取的字节数。

   2. 写入文件:

       第一步: 声明一个FileStream类的对象:

       FileStream fsWrite = new FileStream(string path, FileMode mode, FileAccess access);

       第二步: 调用fsWrite对象的方法Write;

       FileStream.Write(byte[] array, int offset, int count): 将字节数组数据写入到指定的文本。           

FileStream常用的属性和方法:

属性:

CanRead 判断当前流是否支持读取,返回bool值,True表示可以读取

CanWrite 判断当前流是否支持写入,返回bool值,True表示可以写入

方法:

Read() 从流中读取数据,返回字节数组

Write() 将字节块(字节数组)写入该流

Seek() 设置文件读取或写入的起始位置

Flush() 清除该流缓冲区,使得所有缓冲的数据都被写入到文件中

Close() 关闭当前流并释放与之相关联的所有系统资源

文件的访问方式:(FileAccess)

包括三个枚举:

FileAccess.Read(对文件读访问)

FileAccess.Write(对文件进行写操作)

FileAccess.ReadWrite(对文件读或写操作)

文件打开模式:(FileMode)包括6个枚举

FileMode.Append 打开现有文件准备向文件追加数据,只能同FileAccess.Write一起使用

FileMode.Create 指示操作系统应创建新文件,如果文件已经存在,它将被覆盖

FileMode.CreateNew 指示操作系统应创建新文件,如果文件已经存在,将引发异常

FileMode.Open 指示操作系统应打开现有文件,打开的能力取决于FileAccess所指定的值

FileMode.OpenOrCreate 指示操作系统应打开文件,如果文件不存在则创建新文件

FileMode.Truncate 指示操作系统应打开现有文件,并且清空文件内容

文件共享方式:(FileShare)

FileShare方式是为了避免几个程序同时访问同一个文件会造成异常的情况。

文件共享方式包括四个:

FileShare.None 谢绝共享当前文件

FileShare.Read 充许别的程序读取当前文件

FileShare.Write 充许别的程序写当前文件

FileShare.ReadWrite 充许别的程序读写当前文件

使用FileStream类创建文件流对象:

FileStream(String 文件路径,FileMode 文件打开模式)

FileStream(String 文件路径,FileMode 文件打开模式,FileAccess 文件访问方式)

FileStream(String 文件路径,FileMode 文件打开模式,FileAccess 文件访问方式,FileShare 文件共享方式)

使用File类来创建对象:(常用)

自定义打开文件的方式:File.Open(String,FileMode);

打开文件进行读取: File.OpenRead(String);

打开文件进行写入: File.OpenWrite(String);

注:

对文件的读写操多不管代码有多少,无非就是下面的三步:

1.创建文件读写流对象

2.对文件进行读写

3.关闭文件流

 

 

 

   

以上是关于日志记录类(明确FileStreamDictionary等用法)的主要内容,如果未能解决你的问题,请参考以下文章

filebeat配置日志记录(等级)

C++日志记录类以及日志记录程序

logging模块

自定义错误日志记录类

改进的日志记录类

java aop实现http接口日志记录