一、简介:

log4cplus是C++编写的开源的日志系统. 具有线程安全、灵活、以及多粒度控制的特点,通过将信息划分优先级使其可以面向程序调试、运行、测试、和维护等全生命周期; 你可以选择将信息输出到屏幕、文件、NT event log、甚至是远程服务器;通过指定策略对日志进行定期备份等等。

二、基本要素

将log4cplus文件夹下的msvc10文件夹拷贝出来,使用VS10打开工程,编译源代码和用例。

Layouts:布局器,控制输出消息的格式。

Appenders:挂接器,与布局器紧密配合,将特定格式的消息输出到所挂接

的设备终端如屏幕,文件等等)。

Logger:记录器,保存并跟踪对象日志信息变更的实体,当你需要对一个对

象进行记录时,就需要生成一个logger。

Categories:分类器,层次化(hierarchy)的结构,用于对被记录信息的

分类,层次中每一个节点维护一个logger的所有信息。

Priorities:优先权,包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。

三、基本使用
使用log4cplus的六个步骤:

1. 实例化一个appender对象:
new ConsoleAppender();

2. 实例化一个layout对象:new PatternLayout(格式);

3. 将layout对象绑定(attach)到appender对象:

appender->setLayout(layout);

4. 实例化一个logger对象,调用静态函数:

log4cplus ::Logger
::getInstance("logger_name");

5. 将appender对象绑定(attach)到logger对象:

logger.addAppender(appender),如省略此步骤,标准输出(屏幕)appender

对象会绑定到logger;

6. 设置logger的优先级: logger.setLogLevel(ALL_LOG_LEVEL),如

省略此步骤,各种有限级的消息都将被记录。

例如:

1、appender输出到屏幕:

LOG4CPLUS_DEBUG(logger,"This
is the FIRST log message...");

sleep(1);

LOG4CPLUS_WARN(logger,"This
is the SECOND log message...");

2、iostream模式,appender输出到屏幕:

LOG4CPLUS_INFO(logger,"This is a char: " <<
'x');

LOG4CPLUS_ERROR(logger, "This is a long( hex ): " << std::hex
<<

100000000);

LOG4CPLUS_FATAL(logger, "This is a double: " <<

std::setprecision(15) << 1.2345234234);

3、调试模式,通过loglog来控制输出调试、警告或错误信息,appender输出到屏幕:

LogLog::getLogLog()->debug("This is
a Debug statement...");

LogLog::getLogLog()->warn("This
is a Warning...");

LogLog::getLogLog()->error("This
is a Error...");

LogLog::getLogLog()->setInternalDebugging(true);

LogLog::getLogLog()->setQuietMode(true);

LogLog类实现了debug, warn,
error 函数用于输出调试、警告或错误信息,同时提供了两个方法来进一步控制所输出的信息,其中:setInternalDebugging方法用来控制是否屏蔽输出信息中的调试信息,当输入参数为false则屏蔽,缺省设置为false;setQuietMode方法用来控制是否屏蔽所有输出信息,当输入参数为true则屏蔽,缺省设置为false。

注:LogLog在实现时,写死了输出信息前缀:

LogLog::LogLog():mutex(LOG4CPLUS_MUTEX_CREATE),

debugEnabled(false),quietMode(false),

PREFIX(
LOG4CPLUS_TEXT("log4cplus: ")),

WARN_PREFIX( LOG4CPLUS_TEXT("log4cplus:WARN
")), ERR_PREFIX( LOG4CPLUS_TEXT("log4cplus:ERROR "))

{

}

4、文件模式,appender输出到文件:

SharedAppenderPtr _append(new FileAppender("Test.log"));

_append->setName("filelog");

Logger _logger = Logger::getInstance("test.subtestof_filelog");

_logger.addAppender(_append);

LOG4CPLUS_DEBUG(_logger,"Entering
text");

四、输出格式

1、SimpleLayout是一种简单格式的布局器,在输出的原始信息之前加上LogLevel和一个"-"。

std::auto_ptr _layout(new log4cplus::SimpleLayout());

2、PatternLayout是一种有词法分析功能的模式布局器,一提起模式就会想起正则表达式,这里的模式和正则表达式类似,但是远比后者简单,能够对预定义的标识符进行解析,转换成特定格式输出。

std::string pattern = "%d{%m/%d/%y
%H:%M:%S} - %m [%l] %n";

std::auto_ptr
_layout(new PatternLayout(pattern));

注:

1、"pattern"可以包含普通字符串和预定义的标识符,其中:

(1)普通字符串,能够被直接显示的信息;

(2)预定义标识符,通过"%"与一个或多个字符共同构成预定义的标识符,

能够产生出特定格式信息。

2、各种消息格式:

(1)"%%",转义为%;

(2)"%c{n}",输出logger名称,也可以控制logger名称的显示层次其中数字表示层次。

(3)"%D",显示本地时间,%d显示标准时间,可以通过%d{...}定义更详细的显示格式,比如%d{%H:%M:%s}表示要显示小时:分钟:秒。大括号中可显示的预定义标识符如下:

%a -- 表示礼拜几,英文缩写形式,比如"Fri"

%A -- 表示礼拜几,比如"Friday"

%b -- 表示几月份,英文缩写形式,比如"Oct"

%B -- 表示几月份,"October"

%c -- 标准的日期+时间格式,如
"Sat Oct 16 18:56:19 2004"

%d -- 表示今天是这个月的几号(1-31)"16"

%H -- 表示当前时刻是几时(0-23),如 "18"

%I -- 表示当前时刻是几时(1-12),如 "6"

%j -- 表示今天是哪一天(1-366),如 "290"

%m -- 表示本月是哪一月(1-12),如 "10"

%M -- 表示当前时刻是哪一分钟(0-59),如 "59"

%p -- 表示现在是上午还是下午, AM
or PM

%q -- 表示当前时刻中毫秒部分(0-999),如 "237"

%Q -- 表示当前时刻中带小数的毫秒部分(0-999.999),如
"430.732"

%S -- 表示当前时刻的多少秒(0-59),如 "32"

%U -- 表示本周是今年的第几个礼拜,以周日为第一天开始计算(0-53)。

%w -- 表示礼拜几,(0-6, 礼拜天为0),如 "6"

%W -- 表示本周是今年的第几个礼拜,以周一为第一天开始计算(0-53)。

%x -- 标准的日期格式,如
"10/16/04"

%X -- 标准的时间格式,如
"19:02:34"

%y -- 两位数的年份(0-99),如 "04"

%Y -- 四位数的年份,如
"2004"

%Z -- 时区名,比如
"GMT"

(4)"%F",输出当前记录器所在的文件名称;

(5)"%L",输出当前记录器所在的文件行号;

(6)"%l",输出当前记录器所在的文件名称和行号;

(7)"%m",输出原始信息,这种实现机制可以确保原始信息被嵌入到带格式的信息中。

(8)"%n",换行符;

(9)"%p",输出LogLevel;

(10)"%t",输出记录器所在的线程ID

(11)"%x",嵌套诊断上下文NDC输出,从堆栈中弹出上下文信息,NDC可以用对不同源的log信息(同时地)交叉输出进行区分。

(12)格式对齐:"%-10m"时表示左对齐,宽度是10。

3、TTCCLayout是在PatternLayout基础上发展的一种缺省的带格式输出的布局器,其格式由时间,线程ID,Logger和NDC 组成,其在构造时选择显示本地时间(默认false)或GMT时间

五、文件操作类

log4cplus提供了FileAppender类、DailyRollingFileAppender类和RollingFileAppender类用于文件操作:

1、FileAppender类实现了基本的文件操作功能,构造函数如下:

FileAppender(const
log4cplus::tstring& filename ,

LOG4CPLUS_OPEN_MODE_TYPE mode =

LOG4CPLUS_FSTREAM_NAMESPACE::ios::trunc,

bool
immediateFlush = true);

filename: 文件名;

mode: 文件类型,可选择的文件类型包括app、ate、binary、in、out、trunc,缺省是trunc,表示将先前文件删除。

immediateFlush :缓冲刷新标志,如果为true表示每向文件写一条记录就刷新一次缓存,否则直到FileAppender被关闭或文件缓存已满才更新文件,默认true。

2、RollingFileAppender类可以根据预先设定的大小来决定是否转储,当超过该大小,后续log信息会另存到新文件中,除了定义每个记录文件的大小之外,还要确定在RollingFileAppender类对象构造时最多需要多少个这样的记录文件(maxBackupIndex+1),当存储的文件数目超过maxBackupIndex+1时,会删除最早生成的文件,保证整个文件数目等于maxBackupIndex+1,然后继续记录。

构造函数如下:

log4cplus::RollingFileAppender::RollingFileAppender(

const log4cplus::tstring&
filename, long maxFileSize,

int maxBackupIndex, bool
immediateFlush);

filename : 文件名
maxFileSize : 文件的最大尺寸,尺寸小于200k时等于200k;

maxBackupIndex : 最大记录文件数

immediateFlush : 缓冲刷新标志

SharedAppenderPtr
_append(new RollingFileAppender("Test.log", size

* n, size));

3、DailyRollingFileAppender类可以根据你预先设定的频度来决定是否转储,当超过该频度,后续log信息会另存到新文件中,这里的频度包括:MONTHLY (每月)、WEEKLY(每周)、DAILY(每日)、TWICE_DAILY(每两天)、HOURLY(每时)、MINUTELY(每分)。构造函数如下:

DailyRollingFileAppender::DailyRollingFileAppender(

const
log4cplus::tstring& filename,

DailyRollingFileSchedule
schedule,

bool
immediateFlush,

int maxBackupIndex);

filename : 文件名

schedule : 存储频度

immediateFlush : 缓冲刷新标志

maxBackupIndex : 最大记录文件数

SharedAppenderPtr
_append(new DailyRollingFileAppender("Test.log",

MINUTELY,
true, 5));

五、进阶

通过LogLevelManager、LogLog、Filter三种方式,实现任意时刻输出的LogLevel的信息。

1、背景知识:

log4cplus中logger的存储机制是通过一个层次化的结构(如:hash表)来组织的。

获取Root级别的logger: Logger::getRoot();

自定义的logger:Logger::getInstance("test");

定义其子logger: Logger::getInstance("test.subtest");

设置其LogLevel:
Test.setLogLevel( ... );

subTest.setLogLevel( ... );

log4cplus将输出的log信息按照LogLevel(从低到高)分为:

NOT_SET_LOG_LEVEL(-1) :接受缺省的LogLevel,如果有父logger则继承它的LogLevel;

ALL_LOG_LEVEL(0) :开放所有log信息输出;

TRACE_LOG_LEVEL(0)
:开放trace信息输出(即ALL_LOG_LEVEL);

DEBUG_LOG_LEVEL(10000)
:开放debug信息输出;

INFO_LOG_LEVEL(20000)
:开放info信息输出;

WARN_LOG_LEVEL(30000)
:开放warning信息输出;

ERROR_LOG_LEVEL(40000)
:开放error信息输出;

FATAL_LOG_LEVEL(50000)
:开放fatal信息输出;

OFF_LOG_LEVEL(60000)
:关闭所有log信息输出;

2、LogLevelManager负责设置logger的优先级,各logger通过setLogLevel设置自己的优先级,当某个logger的LogLevel设置成NOT_SET_LOG_LEVEL时,该logger会继承父logger的优先级,另外,如果定义了重名的多个logger, 对其中任何一个的修改都会同时改变其它logger。

例1:

SharedAppenderPtr _append(new
ConsoleAppender());

_append->setName("test");

Logger root = Logger::getRoot();

root.addAppender(_append);

Logger test = Logger::getInstance("test");

Logger
subTest = Logger::getInstance("test.subtest");

LogLevelManager&
llm = getLogLevelManager();

test.setLogLevel(INFO_LOG_LEVEL);

LOG4CPLUS_FATAL(root,"root:"<<llm.toString(root.getChainedLogLevel()));

LOG4CPLUS_FATAL(root,"test:"<<llm.toString(test.getChainedLogLevel()));

LOG4CPLUS_FATAL(root,"test.subtest:"<<llm.toString(subTest.getChainedLogLevel()));

例2:通过设置LogLevel来控制用户的log信息输出:

一下级别逐渐升高:

LOG4CPLUS_TRACE(Logger::getRoot(),"info")

LOG4CPLUS_DEBUG(Logger::getRoot(),"info")

LOG4CPLUS_INFO(Logger::getRoot(),"info")

LOG4CPLUS_WARN(Logger::getRoot(),"info")

LOG4CPLUS_ERROR(Logger::getRoot(),"info")

LOG4CPLUS_FATAL(Logger::getRoot(),"info")

Logger root = Logger::getRoot();

root.setLogLevel(ALL_LOG_LEVEL);   //全部显示

root.setLogLevel(TRACE_LOG_LEVEL); //全部显示

root.setLogLevel(ERROR_LOG_LEVEL); //只显示ERROR、FATAL

root.setLogLevel(OFF_LOG_LEVEL);   //显示log disabled

3、自行定义LogLevel:

定义NEW_LOG_LEVEL:

//DEBUG_LOG_LEVEL  < NEW_LOG_LEVEL
< INFO_LOG_LEVEL

const LogLevel NEW_LOG_LEVEL = 15000;

定义宏:

#define LOG4CPLUS_NEW(logger, logEvent) \

if(logger.isEnabledFor(NEW_LOG_LEVEL))
{ \

log4cplus::tostringstream
_log4cplus_buf; \

_log4cplus_buf
<< logEvent; \

logger.forcedLog(NEW_LOG_LEVEL,
_log4cplus_buf.str(),

__FILE__,
__LINE__);}

在loglevel.cxx中加入#define _HELLO_STRING LOG4CPLUS_TEXT("HELLO") 然后修改log4cplus::tstring defaultLogLevelToStringMethod(LogLevel ll)函数,增加一个判断:case HELLO_LOG_LEVEL:return _HELLO_STRING;重新编译log4cplus源代码。

4、基于脚本配置来过滤log信息:

log4cplus通过PropertyConfigurator类实现基于脚本配置实现对logger、appender和layout的配置及log环境的配置(通过程序也可以)。从而过滤log信息及利用脚本配置来配合实现性能测试,

脚本语法规则:

1、Appender的配置语法:

(1)设置名称:

//设置方法

log4cplus.appender.appenderName=fully.qualified.name.of.appender.class

例如:

log4cplus.appender.append_1=log4cplus::ConsoleAppender

log4cplus.appender.append_2=log4cplus::FileAppender

log4cplus.appender.append_3=log4cplus::RollingFileAppender

log4cplus.appender.append_4=log4cplus::DailyRollingFileAppender

log4cplus.appender.append_4=log4cplus::SocketAppender

(2)设置Filter:

过滤器:LogLevelMatchFilter、LogLevelRangeFilter、StringMatchFilter。

LogLevelMatchFilter:过滤条件包括LogLevelToMatch和

AcceptOnMatch(true|false),
只有当log信息的LogLevel值与

LogLevelToMatch相同,且AcceptOnMatch为true时才会匹配。

LogLevelRangeFilter:过滤条件包括LogLevelMin、LogLevelMax和

AcceptOnMatch,只有当log信息的LogLevel在LogLevelMin、LogLevelMax之间同时AcceptOnMatch为true时才会匹配。

StringMatchFilter:过滤条件包括StringToMatch和AcceptOnMatch,

只有当log信息的LogLevel值与StringToMatch对应的LogLevel值相同,

且AcceptOnMatch为true时会匹配。

过滤条件处理机制:先deny再allow,后写的条件会被先执行。比如:

log4cplus.appender.append_1.filters.1=log4cplus::spi::LogLevelMatchFilter

log4cplus.appender.append_1.filters.1.LogLevelToMatch=TRACE

log4cplus.appender.append_1.filters.1.AcceptOnMatch=true

log4cplus.appender.append_1.filters.2=log4cplus::spi::DenyAllFilter

首先执行filters.2的过滤条件,关闭所有过滤器,然后执行filters.1,仅匹配TRACE信息。

(3)设置Layout:不设置(默认)、TTCCLayout、或PatternLayout

设置TTCCLayout:

log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout

设置PatternLayout:

log4cplus.appender.append_1.layout=log4cplus::PatternLayout

log4cplus.appender.append_1.layout.ConversionPattern=%d{%m/%d/%y
%H:%M:%S,%Q} [%t] %-5p - %m%n

2、logger的配置语法:rootLogger和non-root logger。

rootLogger:

log4cplus.rootLogger=[LogLevel], appenderName,
appenderName, ...

non-root logger:

log4cplus.logger.logger_name=[LogLevel|INHERITED],
appenderName, appenderName, ...

脚本方式使用:加载urconfig.properties自定义的配置文件。

PropertyConfigurator::doConfigure("urconfig.properties");

例:

//定义

log4cplus.rootLogger=TRACE, ALL_MSGS,
TRACE_MSGS, DEBUG_INFO_MSGS, FATAL_MSGS

log4cplus.appender.FATAL_MSGS=log4cplus::RollingFileAppender

log4cplus.appender.FATAL_MSGS.File=fatal_msgs.log

log4cplus.appender.FATAL_MSGS.layout=log4cplus::TTCCLayout

log4cplus.appender.FATAL_MSGS.filters.1=log4cplus::spi::StringMatchFilter

log4cplus.appender.FATAL_MSGS.filters.1.StringToMatch=FATAL

log4cplus.appender.FATAL_MSGS.filters.1.AcceptOnMatch=true

log4cplus.appender.FATAL_MSGS.filters.2=log4cplus::spi::DenyAllFilter

//加载

Logger
root = Logger::getRoot();

PropertyConfigurator::doConfigure("urconfig.properties");

六、嵌入诊断上下文

    NDC是线程特有的,利用了线程局部存储机制,称为线程私有数据(Thread-specificData,或TSD)。

(1)默认格式输出NDC:

NDC& ndc = log4cplus::getNDC();

ndc.push("ur ndc string");

ndc.pop();

ndc.remove();

(2)自定义的输出格式中使用NDC(用%x):

std::string pattern = "NDC:[%x]  - %m %n";

std::auto_ptr<layout></layout> _layout(new
PatternLayout(pattern));

NDC& ndc = log4cplus::getNDC();
ndc.push("ur ndc string");

ndc.pop();

ndc.remove();

(3)线程中直接用NDCContextCreator,不必显式地调用push/pop了,而且当出现异常时,能够确保push与pop的调用是匹配的。

NDCContextCreator _first_ndc("ur ndc string");

七、线程

log4cplus的线程没有考虑同步、死锁,有互斥,使用时派生类中直接重载run函数。如下:

class TestThread : public AbstractThread

{

public:

virtual void run();

};

void TestThread::run()

{

}

八、套接字

定义在namespace log4cplus::helpers中,实现了C/S方式的日志记录。

1、 客户端程序需要做的工作:

//定义一个SocketAppender类型的挂接器:

SharedAppenderPtr
_append(new SocketAppender(host, 8888, "ServerName"));

//把_append加入到logger中:

Logger::getRoot().addAppender(_append);

注:SocketAppender类型不需要Layout,直接调用宏将信息发往loggerServer。

对宏的调用其实是调用了SocketAppender::append,里面有一个数据传输约定,即先发送一个后续数据的总长度,然后再发送实际的数据。如下:

SocketBuffer
buffer = convertToBuffer(event, serverName);

SocketBuffer
msgBuffer(LOG4CPLUS_MAX_MESSAGE_SIZE);

msgBuffer.appendSize_t(buffer.getSize());

msgBuffer.appendBuffer(buffer);

2、服务器端程序需要做的工作:

//定义一个ServerSocket

ServerSocket
serverSocket(port);

//调用accept函数创建一个新的socket与客户端连接

Socket sock =
serverSocket.accept();

//进行数据read/write

SocketBuffer msgSizeBuffer(sizeof(unsigned int));

if(!clientsock.read(msgSizeBuffer))

{

return;

}

unsigned int msgSize = msgSizeBuffer.readInt();

SocketBuffer buffer(msgSize);

if(!clientsock.read(buffer))

{

return;

}

注:为了将读到的数据正常显示出来,需要将SocketBuffer存放的内容转换成InternalLoggingEvent格式:spi::InternalLoggingEvent
event = readFromBuffer(buffer);然后输出:Logger logger =
Logger::getInstance(event.getLoggerName());logger.callAppenders(event);     read/write是按照阻塞方式实现的,对其调用直到满足了所接收或发送的个数才返回。

log4cplus基础知识的更多相关文章

  1. .NET面试题系列[1] - .NET框架基础知识(1)

    很明显,CLS是CTS的一个子集,而且是最小的子集. - 张子阳 .NET框架基础知识(1) 参考资料: http://www.tracefact.net/CLR-and-Framework/DotN ...

  2. RabbitMQ基础知识

    RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...

  3. Java基础知识(壹)

    写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...

  4. selenium自动化基础知识

    什么是自动化测试? 自动化测试分为:功能自动化和性能自动化 功能自动化即使用计算机通过编码的方式来替代手工测试,完成一些重复性比较高的测试,解放测试人员的测试压力.同时,如果系统有不份模块更改后,只要 ...

  5. [SQL] SQL 基础知识梳理(一)- 数据库与 SQL

    SQL 基础知识梳理(一)- 数据库与 SQL [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5902856.html 目录 What's 数据库 ...

  6. [SQL] SQL 基础知识梳理(二) - 查询基础

    SQL 基础知识梳理(二) - 查询基础 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5904824.html 序 这是<SQL 基础知识梳理( ...

  7. [SQL] SQL 基础知识梳理(三) - 聚合和排序

    SQL 基础知识梳理(三) - 聚合和排序 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5926689.html 序 这是<SQL 基础知识梳理 ...

  8. [SQL] SQL 基础知识梳理(四) - 数据更新

    SQL 基础知识梳理(四) - 数据更新 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5929786.html 序 这是<SQL 基础知识梳理( ...

  9. [SQL] SQL 基础知识梳理(五) - 复杂查询

    SQL 基础知识梳理(五) - 复杂查询 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5939796.html 序 这是<SQL 基础知识梳理( ...

随机推荐

  1. Extjs下拉树代码测试总结

    http://blog.csdn.net/kunoy/article/details/8067801 首先主要代码源自网络,对那些无私的奉献者表示感谢! 笔者对这些代码做了二次修改,并总结如下: Ex ...

  2. How to make a combo box with fulltext search autocomplete support?

    I would like a user to be able to type in the second or third word from a TComboBoxitem and for that ...

  3. STM32F401

    The STM32F401 line is the entry level to the STM32 F4 series. It is designed for medical, industrial ...

  4. Windows Embedded Compact 7网络编程概述(上)

    如今,不论是嵌入式设备.PDA还是智能手机,网络都是必不可少的模块.网络使人们更方便地共享设备上的信息和资源.而且,利用智能手机浏览互联网,也逐渐成为生活中的常见手段.物联网所倡导的物物相联,也离不开 ...

  5. vue项目条形码和二维码生成工具试用

    项目开发需要,优惠券分不同类型,简单的使用id生成条形码供店铺使用,麻烦点的需要多个字段的就需要使用二维码来展示了,对应的效果如下 条形码(一维码)使用工具code128 需引入code128.js ...

  6. Android 集成新浪微博分享及授权 (上)

    2014-05-05 20:16 10663人阅读 评论(8) 收藏 举报  分类: android(33)  版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 第一部分  ...

  7. [转] NSMapTable 不只是一个能放weak指针的 NSDictionary

    NSMapTable 不只是一个能放weak指针的 NSDictionary NSMapTable是早在Mac OS X 10.5(Leopard)的引入集合类.乍一看,这似乎是作为一个替换NSDic ...

  8. sqlserver锁机制详解(sqlserver查看锁)

    简介 在SQL Server中,每一个查询都会找到最短路径实现自己的目标.如果数据库只接受一个连接一次只执行一个查询.那么查询当然是要多快好省的完成工作.但对于 大多数数据库来说是需要同时处理多个查询 ...

  9. Run Redis On Windows

    If you go to the current version and open up the bin > release folder, you'll get a ZIP file cont ...

  10. go语言基础之break和continue的区别

    1.break和continue的区别 在循环里面有两个关键操作break和continue,break操作是跳出当前循环,continue是跳过本次循环. 2.break 备注:break可⽤于fo ...