在程序的开发调试过程及发布运行后的状态监控中,日志都有着极其重要的分量,通过在关键逻辑节点将关键数据记录到日志文件当中能帮助我们尽快找到程序问题所在。网上有不少专业成熟的日志组件可用,比如log4net和nlog等,由其专业及受欢迎程度可见日志在一个程序中的重要性。

我只用过log4net,而在用log4net写日志的过程中慢慢觉着太繁琐了点,不就写个日志吗?为毛搞得那么复杂?各种配置让我有点抓狂。

于是我就想,自己来吧!

首先分析一下一个基本的日志类库应该具有的基本功能及实现的时候需要注意和解决的问题:

1.关于日志文件的写入

写日志并不是简单的打开一个文件然后写入数据然后关闭了事,无论是web程序还是桌面程序,首要问题是多线程争抢写入一个日志文件的访问控制,次要问题是要允许其它进程在写入进程未释放日志文件时日志文件能被读取——试想如果日志在写的时候不能被读取那日志将毫无价值。

为了解决多线程写入的问题,多线程写入的数据将被缓存在一个StringBuilder对象中,而后由一个专门的写文件线程来负责取出数据写入到日志文件,以此来保证只有一个线程对日志文件进行写操作,如果再解决在文件流未关闭的情况下让其它进程或线程能读取日志内容,那问题就都不是问题了,而在文件流未关闭的情况下要让其它进程或线程能读取日志内容只需要在打开或创建日志文件的FileStream时指定System.IO.FileShare参数为Read即可。

2.关于日志文件的读取

文件写入成功后会有读取进行查看及分析的需求。内容较少的时候直接记事本打开即可,但是日志较大的时候就费劲了,虽然也有一些专门的软件能打开大文本文件,可打开日志文件有时并不是只为了看上一眼而已,很可能需要提取一些受关注的数据做个统计分析,比如提取某个操作的耗时来做瓶颈参考,因此有必要实现对大文本文件的读取,在读取过程中进行数据的留存分析。

对大文本文件的读取当然要按块来读取,比如一次读取10M字节,这样即便是几个G的文件也没几次可读的,重要的是不能截断单词和宽字符,所以每读取到指定字节数(如10M字节)的数据后需要根据指定的参考字符(如换行符、空格、逗号、句号等)做偏移计算。

对文件的读取在创建文件的读取流的时候必须要指定System.IO.FileShare参数为ReadWrite,否则对正在被写入或未被释放的文件的访问将被拒绝,因为写入的进程已经获得了写入权限,作为后来的读取者一定要允许其它进程可以对文件读写,要不然冲突就是一定的了。

3.关于日志的清理

随着程序常年运行,日志积累得越来越多,而日志应该都有一定的时效性,过了时效期后的日志就没有什么价值了,所以应该对日志做定时的清理操作,因此写日志的时候应该有一个默认的时效值,使日志在到期之后自动删除,以免无限增多浪费了磁盘空间,毕竟磁盘空间是十分有限的。

下面开始上代码:

新建一个 .Net Standard 类库,命名 Logger ,在类库中添加一个 Core 文件夹,在 Core 文件夹添加以下文件:

  1. ILog.cs 接口
  2. Log.cs 密封的接口实现类(不对程序集外提供访问)
  3. TextFileReader.cs 文本文件读取
  4. Factory.cs 工厂类(生产和维护日志对象)
 namespace Logger.Core
{
public interface ILog
{
void Write(string logInfo);
void WriteFormat(string format, params object[] args);
void SaveLogToFile();
void ClearLogFile();
}
}

ILog.cs

 namespace Logger.Core
{
internal class Log : ILog
{
private System.Text.StringBuilder logSource = null;
private string logFilePre = string.Empty;
private System.IO.FileStream fileStream = null;
private DateTime logFileScanLastTime = DateTime.Now;
private int logFileRetentionDays = ; public Log(string logFilePre)
: this(logFilePre, )
{ }
public Log(string logFilePre, int logFileRetentionDays)
{
this.logFilePre = logFilePre;
this.logSource = new System.Text.StringBuilder();
if (logFileRetentionDays < )
{
logFileRetentionDays = ;
}
this.logFileRetentionDays = logFileRetentionDays;
Factory.SetFileThreadStart();
} private System.IO.FileStream GetFileStream()
{
if (!System.IO.Directory.Exists(Factory.logsDirPath))
{
System.IO.Directory.CreateDirectory(Factory.logsDirPath);
}
System.IO.FileStream fs;
string FilePath = System.IO.Path.Combine(Factory.logsDirPath, this.logFilePre + DateTime.Now.ToString("yyyyMMdd") + ".log");
if (!System.IO.File.Exists(FilePath))
{
if (fileStream != null)
{
fileStream.Close();
}
fileStream = fs = new System.IO.FileStream(FilePath, System.IO.FileMode.CreateNew, System.IO.FileAccess.Write, System.IO.FileShare.Read, , true);
}
else
{
if (fileStream != null)
{
fs = fileStream;
}
else
{
fileStream = fs = new System.IO.FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Write, System.IO.FileShare.Read, , true);
}
}
return fs;
}
private string GetLogText()
{
string s = "";
if (logSource.Length > )
{
lock (logSource)
{
s = logSource.ToString();
logSource.Clear();
}
}
return s;
} public void Write(string logInfo)
{
try
{
if (logSource == null)
{
logSource = new System.Text.StringBuilder();
}
lock (this)
{
logSource.AppendFormat("{0} {1}{2}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff"), logInfo, System.Environment.NewLine);
}
}
catch { }
}
public void WriteFormat(string format, params object[] args)
{
try
{
if (logSource == null)
{
logSource = new System.Text.StringBuilder();
}
lock (this)
{
logSource.AppendFormat("{0} ", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff"));
logSource.AppendFormat(format, args);
logSource.Append(System.Environment.NewLine);
}
}
catch { }
}
public void SaveLogToFile()
{
try
{
string logInfo = GetLogText();
if (logInfo.Length > )
{
System.IO.FileStream fs = GetFileStream();
byte[] buffer = System.Text.UTF8Encoding.UTF8.GetBytes(logInfo);
long lockBegin = fs.Length;
long lockEnd = buffer.Length;
fs.Position = lockBegin;
fs.Lock(lockBegin, lockEnd);
//fs.WriteAsync(buffer, 0, buffer.Length);
fs.Write(buffer, , buffer.Length);
fs.Unlock(lockBegin, lockEnd);
fs.Flush();
//fs.Close();
}
}
catch { }
}
public void ClearLogFile()
{
if ((DateTime.Now - logFileScanLastTime).TotalMinutes < )
{
return;
}
logFileScanLastTime = DateTime.Now;
System.IO.DirectoryInfo directoryInfo = new System.IO.DirectoryInfo(Factory.logsDirPath);
if (!directoryInfo.Exists)
{
return;
}
System.IO.FileInfo[] files = directoryInfo.GetFiles(this.logFilePre + "*.log", System.IO.SearchOption.TopDirectoryOnly);
if (files == null || files.Length < )
{
return;
}
DateTime time = DateTime.Now.AddDays( - logFileRetentionDays);
foreach (System.IO.FileInfo file in files)
{
try
{
if (file.CreationTime < time)
{
file.Delete();
}
}
catch { }
}
} }
}

Log.cs

 namespace Logger.Core
{
public class TextFileReader
{
bool _readStart = false;
bool _readEnd = false;
System.IO.FileStream _stream = null;
System.Text.Encoding _code = null;
long _fileLength = ;
long _currentPosition = ;
string _readStr = string.Empty;
int _readBytes = ;
string _filePath = "";
readonly string[] _defaultOffsetStrArray = new string[] { System.Environment.NewLine, " ", ",", ".", "!", "?", ";", ",", "。", "!", "?", ";" };
string[] _offsetStrArray = null; public string ReadStr {
get { return _readStr; }
}
public string FilePath {
get { return _filePath; }
set { _filePath = value; }
}
public int ReadBytes {
get { return _readBytes < ? : _readBytes; }
set { _readBytes = value; }
}
public string[] OffsetStrArray {
get { return (_offsetStrArray == null|| _offsetStrArray.Length < )? _defaultOffsetStrArray : _offsetStrArray; }
set { _offsetStrArray = value; }
} public TextFileReader() {
_offsetStrArray = _defaultOffsetStrArray;
}
public TextFileReader(string FilePath)
{
this.FilePath = FilePath;
_offsetStrArray = _defaultOffsetStrArray;
}
private int GetPosition(string readStr, string[] offsetStrArray)
{
int position = -;
for (int i = ; i < offsetStrArray.Length; i++)
{
position = readStr.LastIndexOf(offsetStrArray[i]);
if (position > )
{
break;
}
}
return position;
}
public bool Read()
{
if (!_readStart)
{
//System.IO.FileShare.ReadWrite:允许其它程序读写文件(重要,否则很可能会与负责写入的程序冲突而被拒绝访问)
_stream = new System.IO.FileStream(this.FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
_code = GetType(this.FilePath);
_currentPosition = ;
_fileLength = _stream.Length;
_readStart = true;
}
if (_currentPosition < _fileLength)
{
byte[] readBuffer = new byte[this.ReadBytes];
//设置读取位置
_stream.Seek(_currentPosition, System.IO.SeekOrigin.Begin);
//本次实际读到的字节数
int currentReadBytes = _stream.Read(readBuffer, , readBuffer.Length);
//读取位置偏移
_currentPosition += currentReadBytes; //实际读到的字节少于指定的字节数(在读到最后一批时)
if (currentReadBytes < _readBytes)
{
byte[] temp = new byte[currentReadBytes];
int index = ;
while (index < currentReadBytes)
{
temp[index] = readBuffer[index];
index++;
}
readBuffer = temp;
}
_readStr = _code.GetString(readBuffer);
//如果没有读到最后一个字节则计算位置偏移
if (_currentPosition < _fileLength)
{
int offsetStrPosition = GetPosition(_readStr, this.OffsetStrArray);
if (offsetStrPosition > )//找到内容则计算位置偏移
{
//提取将被移除的内容
string removeStr = _readStr.Substring(offsetStrPosition + );
//移除内容
_readStr = _readStr.Remove(offsetStrPosition + );
//位置后退
_currentPosition = _currentPosition - _code.GetBytes(removeStr).Length;
}
}
}
else
{
_readEnd = true;
_stream.Dispose();
}
return !_readEnd;
} public static System.Text.Encoding GetType(string fullname)
{
System.IO.FileStream fs = new System.IO.FileStream(fullname, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
System.Text.Encoding r = GetType(fs);
fs.Close();
return r;
}
public static System.Text.Encoding GetType(System.IO.FileStream fs)
{
byte[] Unicode = new byte[] { 0xFF, 0xFE, 0x41 };
byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF, 0x00 };
byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF };
System.Text.Encoding reVal = System.Text.Encoding.Default; System.IO.BinaryReader r = new System.IO.BinaryReader(fs, System.Text.Encoding.Default);
int i;
int.TryParse(fs.Length.ToString(), out i);
byte[] ss = r.ReadBytes(i);
if (IsUTF8Bytes(ss) || (ss[] == 0xEF && ss[] == 0xBB && ss[] == 0xBF))
{
reVal = System.Text.Encoding.UTF8;
}
else if (ss[] == 0xFE && ss[] == 0xFF && ss[] == 0x00)
{
reVal = System.Text.Encoding.BigEndianUnicode;
}
else if (ss[] == 0xFF && ss[] == 0xFE && ss[] == 0x41)
{
reVal = System.Text.Encoding.Unicode;
}
r.Close();
return reVal;
}
private static bool IsUTF8Bytes(byte[] data)
{
int charByteCounter = ;
byte curByte;
for (int i = ; i < data.Length; i++)
{
curByte = data[i];
if (charByteCounter == )
{
if (curByte >= 0x80)
{
while (((curByte <<= ) & 0x80) != )
{
charByteCounter++;
}
if (charByteCounter == || charByteCounter > )
{
return false;
}
}
}
else
{
if ((curByte & 0xC0) != 0x80)
{
return false;
}
charByteCounter--;
}
}
if (charByteCounter > )
{
throw new Exception("非预期的byte格式");
}
return true;
} }
}

TextFileReader.cs

 namespace Logger.Core
{
public static class Factory
{
private static object setFileThreadCreateLockObj = new object();
private static object loggerCreateLockObj = new object();
public static readonly string logsDirPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
internal static readonly System.Collections.Generic.Dictionary<string, ILog> loggerDic = new System.Collections.Generic.Dictionary<string, ILog>();
internal static System.Threading.Thread setFileThread = null;
internal static void SetFileThreadStartFunc(object obj)
{
while (true)
{
try
{
foreach (string key in loggerDic.Keys)
{
loggerDic[key].SaveLogToFile();
loggerDic[key].ClearLogFile();
}
System.Threading.Thread.Sleep();
}
catch { }
}
}
public static void SetFileThreadStart()
{
if (setFileThread == null)
{
lock (setFileThreadCreateLockObj)
{
if (setFileThread == null)
{
setFileThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(SetFileThreadStartFunc));
setFileThread.IsBackground = true;
setFileThread.Start(null);
}
}
}
}
public static ILog GetLogger()
{
return GetLogger("Trace");
}
public static ILog GetLogger(string LogFilePre)
{
return GetLogger(LogFilePre, );
}
public static ILog GetLogger(string logFilePre, int logFileRetentionDays)
{
logFilePre = GetLogFilePre(logFilePre);
if (loggerDic.ContainsKey(logFilePre))
{
return loggerDic[logFilePre];
}
else
{
lock (loggerCreateLockObj)
{
if (loggerDic.ContainsKey(logFilePre))
{
return loggerDic[logFilePre];
}
else
{
ILog _logger = new Log(logFilePre, logFileRetentionDays);
loggerDic.Add(logFilePre, _logger);
return _logger;
}
}
}
}
public static string GetLogFilePre(string logFilePre)
{
if (string.IsNullOrEmpty(logFilePre)) {
logFilePre = "Trace";
}
logFilePre = logFilePre.ToLower();
if (!logFilePre.EndsWith("-"))
{
logFilePre = logFilePre + "-";
}
logFilePre = logFilePre.Substring(, ).ToUpper() + logFilePre.Substring();
return logFilePre;
}
public static System.Collections.Generic.List<string> GetLogFilePreList()
{
System.Collections.Generic.List<string> reval = new System.Collections.Generic.List<string>();
foreach(string key in loggerDic.Keys)
{
reval.Add(key);
}
return reval;
} }
}

Factory.cs

以上是实现日志功能的核心代码,下面在类库项目下直接添加两个静态类:

  1. LogWriter.cs 负责写,定义了常规的 Fatal , Error , Info , Debug 等方法及默认的日志时效期
  2. LogReader.cs 负责读,如获取日志类型列表,获取日志文件列表,或取日志文件的TextFileReader对象等
 namespace Logger
{
public static class LogWriter
{
public static Core.ILog Debug()
{
return Core.Factory.GetLogger("Debug", );
}
public static Core.ILog Debug(string logInfo)
{
Core.ILog logger = Debug();
logger.Write(logInfo);
return logger;
}
public static Core.ILog Debug(string format, params object[] args)
{
Core.ILog logger = Debug();
logger.WriteFormat(format, args);
return logger;
}
public static Core.ILog Info()
{
return Core.Factory.GetLogger("Info", );
}
public static Core.ILog Info(string logInfo)
{
Core.ILog logger = Info();
logger.Write(logInfo);
return logger;
}
public static Core.ILog Info(string format, params object[] args)
{
Core.ILog logger = Info();
logger.WriteFormat(format, args);
return logger;
}
public static Core.ILog Error()
{
return Core.Factory.GetLogger("Error", );
}
public static Core.ILog Error(string logInfo)
{
Core.ILog logger = Error();
logger.Write(logInfo);
return logger;
}
public static Core.ILog Error(string format, params object[] args)
{
Core.ILog logger = Error();
logger.WriteFormat(format, args);
return logger;
}
public static Core.ILog Fatal()
{
return Core.Factory.GetLogger("Fatal", );
}
public static Core.ILog Fatal(string logInfo)
{
Core.ILog logger = Fatal();
logger.Write(logInfo);
return logger;
}
public static Core.ILog Fatal(string format, params object[] args)
{
Core.ILog logger = Fatal();
logger.WriteFormat(format, args);
return logger;
}
}
}

LogWriter.cs

 namespace Logger
{
public static class LogReader
{
public static System.Collections.Generic.List<string> GetLogFilePreList()
{
return Core.Factory.GetLogFilePreList();
}
public static System.IO.FileInfo[] GetLogFiles(string logFilePre)
{
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(Core.Factory.logsDirPath);
if (!dir.Exists)
{
return new System.IO.FileInfo[] { };
}
logFilePre = Core.Factory.GetLogFilePre(logFilePre);
System.IO.FileInfo[] fis = dir.GetFiles(logFilePre + "*.log", System.IO.SearchOption.TopDirectoryOnly);
if (fis == null)
{
fis = new System.IO.FileInfo[] { };
}
return fis;
}
public static Core.TextFileReader GetTextFileReader(System.IO.FileInfo logFileInfo)
{
Core.TextFileReader textFileReader = new Core.TextFileReader(logFileInfo.FullName);
textFileReader.ReadBytes = * * ;
return textFileReader;
}
}
}

LogReader

新建一个控制台程序来测试一下,测试代码:

 class Program
{
static void Main(string[] args)
{
Writer();
Reader();
}
static void Writer()
{
for (var i = ; i < ; i++)
{
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(WriterFunc));
thread.IsBackground = true;
thread.Start(i);
}
}
static void WriterFunc(object num)
{
int threadNum = (int)num;
while (true)
{
Logger.LogWriter.Info("这是线程{0}", threadNum);
Logger.LogWriter.Error("这是线程{0}", threadNum);
Logger.LogWriter.Fatal("这是线程{0}", threadNum);
System.Threading.Thread.Sleep();
}
}
static void Reader()
{
string cmd = "";
while (cmd != "r")
{
Console.Write("输入 r 读取日志:");
cmd = Console.ReadLine();
} System.Collections.Generic.List<string> preList = Logger.LogReader.GetLogFilePreList();
if (preList.Count < )
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("未能检索到日志记录!");
Console.ResetColor();
Reader();
}
Console.WriteLine("-----------------------------------------------------------"); Console.WriteLine("编号\t类型前缀");
Console.ForegroundColor = ConsoleColor.Red;
for (var i = ; i < preList.Count; i++)
{
Console.WriteLine("{0}\t{1}", i + , preList[i]+"*");
}
Console.ResetColor();
Console.WriteLine("-----------------------------------------------------------"); Console.Write("输入编号读取日志文件列表:");
int preNum = GetInputNum(, preList.Count); var files = Logger.LogReader.GetLogFiles(preList[preNum-]);
if (files.Length < )
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("未能检索到日志文件!");
Console.ResetColor();
Reader();
}
Console.WriteLine("-----------------------------------------------------------"); Console.WriteLine("编号\t日志文件");
Console.ForegroundColor = ConsoleColor.Red;
for (var i = ; i < files.Length; i++)
{
Console.WriteLine("{0}\t{1}", i + , System.IO.Path.GetFileName(files[i].FullName));
}
Console.ResetColor();
Console.WriteLine("-----------------------------------------------------------"); Console.Write("输入编号读取日志:");
int fileNum = GetInputNum(, files.Length);
Console.WriteLine("-----------------------------------------------------------"); var reader = Logger.LogReader.GetTextFileReader(files[fileNum - ]);
while (reader.Read())
{
Console.Write(reader.ReadStr);
} Console.WriteLine(); Reader(); }
static int GetInputNum(int min, int max)
{
int num = -;
while (true)
{
string inputNum = Console.ReadLine();
bool flag = false;
if (System.Text.RegularExpressions.Regex.IsMatch(inputNum, @"^\d{1,9}$"))
{
num = Convert.ToInt32(inputNum);
flag = num <= max && num >= min;
}
if (!flag)
{
Console.Write("输入不合法,请重新输入:");
num = -;
}
else
{
break;
}
}
return num;
}
}

Program.cs

程序运行截图:

至此,一个日志类库就算完成了。

鉴于个人水平问题,不敢妄言更高效或更优雅,但是可以集成到其它项目中工作了,该代码作者在公司的实际项目中有使用。

不用各种繁杂的配置,想写就写,如果想要添加一个其它类型的日志只要在LogWriter.cs中增加方法即可。

(^_^)大神莫笑,小菜莫怕,欢迎善意的沟通和交流!

通过编写一个简单的日志类库来加深了解C#的文件访问控制的更多相关文章

  1. Linux下一个简单的日志系统的设计及其C代码实现

    1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回 ...

  2. 用Python编写一个简单的Http Server

    用Python编写一个简单的Http Server Python内置了支持HTTP协议的模块,我们可以用来开发单机版功能较少的Web服务器.Python支持该功能的实现模块是BaseFTTPServe ...

  3. 编写一个简单的C++程序

    编写一个简单的C++程序 每个C++程序都包含一个或多个函数(function),其中一个必须命名为main.操作系统通过调用main来运行C++程序.下面是一个非常简单的main函数,它什么也不干, ...

  4. 使用Java编写一个简单的Web的监控系统cpu利用率,cpu温度,总内存大小

    原文:http://www.jb51.net/article/75002.htm 这篇文章主要介绍了使用Java编写一个简单的Web的监控系统的例子,并且将重要信息转为XML通过网页前端显示,非常之实 ...

  5. 编写一个简单的Web Server

    编写一个简单的Web Server其实是轻而易举的.如果我们只是想托管一些HTML页面,我们可以这么实现: 在VS2013中创建一个C# 控制台程序 编写一个字符串扩展方法类,主要用于在URL中截取文 ...

  6. javascript编写一个简单的编译器(理解抽象语法树AST)

    javascript编写一个简单的编译器(理解抽象语法树AST) 编译器 是一种接收一段代码,然后把它转成一些其他一种机制.我们现在来做一个在一张纸上画出一条线,那么我们画出一条线需要定义的条件如下: ...

  7. Java入门篇(一)——如何编写一个简单的Java程序

    最近准备花费很长一段时间写一些关于Java的从入门到进阶再到项目开发的教程,希望对初学Java的朋友们有所帮助,更快的融入Java的学习之中. 主要内容包括JavaSE.JavaEE的基础知识以及如何 ...

  8. 用 Go 编写一个简单的 WebSocket 推送服务

    用 Go 编写一个简单的 WebSocket 推送服务 本文中代码可以在 github.com/alfred-zhong/wserver 获取. 背景 最近拿到需求要在网页上展示报警信息.以往报警信息 ...

  9. 用C语言编写一个简单的词法分析程序

    问题描述: 用C或C++语言编写一个简单的词法分析程序,扫描C语言小子集的源程序,根据给定的词法规则,识别单词,填写相应的表.如果产生词法错误,则显示错误信息.位置,并试图从错误中恢复.简单的恢复方法 ...

随机推荐

  1. mysql在Windows下使用mysqldump命令手动备份数据库和自动备份数据库

    手动备份: cmd控制台: 先进入mysql所在的bin目录下,如:cd C:\Program Files\MySQL\MySQL Server 5.5\bin mysqldump -u root - ...

  2. Python-----多线程threading用法

    threading模块是Python里面常用的线程模块,多线程处理任务对于提升效率非常重要,先说一下线程和进程的各种区别,如图 概括起来就是 IO密集型(不用CPU) 多线程计算密集型(用CPU) 多 ...

  3. python list用法

    a = ['张三','李四','赵六','王五'] #打印列表 print(a) #增 a.apppend('徐七') print(a) a.insert(1,'张六') print(a) #删 a. ...

  4. Python 运维之路

    第一章:Python基础知识 1.Python 变量了解 .Python 二进制 .Python 字符编码 4.Python if条件判断 5.Python while循环 6.Python for循 ...

  5. ORA-19606: Cannot copy or restore to snapshot control file

    delete obsolete; 发现出现报错 ORA-19606: Cannot copy or restore to snapshot control file 解决办法:CONFIGURE SN ...

  6. Developing Vert.x Modules using the Standard Project

    The module The tests Unit tests Integration tests Java integration tests JavaScript integration test ...

  7. JDK(java development kit java开发工具包)的安装

    想要进行Java开发工作,首先我们得进行JDK的下载.安装.配置.测试,如果是新手,我们不妨新建一个文件夹,在文件夹下面新建".java"文件,用记事本打开,写一段简单的java入 ...

  8. python3中argparse模块

    1.定义:argparse是python标准库里面用来处理命令行参数的库 2.命令行参数分为位置参数和选项参数:         位置参数就是程序根据该参数出现的位置来确定的              ...

  9. go-switch特点

    程序中遇到有枚举分支逻辑时,需要用到 switch 代替多个 if else 判断. 学习过程遇到一些与C#不同点,记录下. 语法: switch expr { case x1 : //expr为x1 ...

  10. Redis 单机版

    Redis 支持单机版和集群,下面的步骤是单机版安装步骤 1. # yum install -y gcc-c++ 1.1 由于是c语言编写,所以需要安装支持组件 2. 把压缩包上传到linux服务器上 ...