初次使用glog
一、安装配置
1、简单介绍
google 出的一个C++轻量级日志库,支持下面功能:
◆ 參数设置,以命令行參数的方式设置标志參数来控制日志记录行为;
◆ 严重性分级,依据日志严重性分级记录日志;
◆ 可有条件地记录日志信息。
◆ 条件中止程序。丰富的条件判定宏,可预设程序终止条件。
◆ 异常信号处理。程序异常情况。可自己定义异常处理过程。
◆ 支持debug功能;
◆ 自己定义日志信息;
◆ 线程安全日志记录方式。
◆ 系统级日志记录;
◆ google perror风格日志信息。
◆ 精简日志字符串信息
2、安装
下载地址:https://code.google.com/p/google-glog/downloads/list (假设不能訪问则点击这里下载)
解压安装:
tar zxvf glog-0.3.3.tar.gz && cd glog-0.3.3 && ./configure && make
头文件目录为 /src/glog 。链接库为 .libs/libglog.{a,so}
帮助文档为 doc/glog.html 或直接訪问下面 URL: http://google-glog.googlecode.com/svn/trunk/doc/glog.html
3、简单 Demo
#include <glog/logging.h> int main(int argc,char* argv[])
{
LOG(INFO) << "Hello,GLOG!";
}
链接时,须要 -lglog ,也可能会须要 -lunwind -lpthread (有一次遇到的,记不起来了,一般不须要)
运行时,可使用 ./glogdemo 将日志输出到 stderr,可使用 valgrind 检測。未发现内存泄漏。
二、用法
1、错误级别
GLOG 有四个错误级别,枚举例如以下:
enum SeverityLevel
{
google::INFO = 0,
google::WARNING = 1,
google::ERROR = 2,
google::FATAL = 3,
};
2、Flags 设置:
在上面的简单 Demo 中,仅仅能将日志输出到 stderr ,假设想将日志重定向到文件,须要:
google::InitGoogleLogging(argv[0]);
/*
GLOG代码
*/
google::ShutdownGoogleLogging();
则默认执行会将日志输出到 /tmp 文件夹下(格式为 "<program name>.<hostname>.<user name>.log.<severity level>.<date>.<time>.<pid>")。也能够使用设置暂时环境变量的方式(给命令行參数加上 GLOG_ 前缀),如 GLOG_logtostderr=1
./your_application 将日志输出到 stderr。
另外,glog 使用了 gflags 库,假设已经安装好了 gflags 库(./configure && make && make install 然后再配置编译GLOG库)还能够通过 ./your_application --logtostderr=1 来指定执行參数。但须要在使用时加上例如以下代码:(详见 http://www.cnblogs.com/tianyajuanke/p/3467572.html)
google::ParseCommandLineFlags(&argc, &argv, true);
/*
GLOG代码
*/
google::ShutDownCommandLineFlags();
但此方法会使 valgrind 检測有内存泄漏(截止 valgrind 3.9.0 依旧如此)。这是因为使用 gflags 库造成的。(即使不使用此方法,但仅仅要在编译 glog 库之前安装好了 gflags 库,就会使 libglog.so 有内存泄漏,介意者可进入 gflags 文件夹 make uninstall 之后,再对 glog 进行 ./configure && make )。
经常使用的执行參数例如以下:
logtostderr (bool, default=false) //是否将全部日志输出到 stderr,而非文件
alsologtostderr(bool,default=false) //是否同一时候将日志输出到文件和stderr
minloglevel (int, default=google::INFO) //限制输出到 stderr 的部分信息,包含此错误级别和更高错误级别的日志信息
stderrthreshold (int, default=google::ERROR) //除了将日志输出到文件之外,还将此错误级别和更高错误级别的日志同一时候输出到 stderr,这个仅仅能使用 -stderrthreshold=1 或代码中设置,而不能使用环境变量的形式。(这个參数能够替代上面两个參数)
colorlogtostderr(bool, default=false) //将输出到 stderr 上的错误日志显示对应的颜色
v (int, default=0) //仅仅记录此错误级别和更高错误级别的 VLOG 日志信息
log_dir (string, default="") //设置日志文件输出文件夹
v (int, default=0) //仅仅有当自己定义日志(VLOG)级别值小于此值时,才进行输出,默觉得0(注:自己定义日志的优先级与GLOG内置日志优级相反,值越小优先级越高。!。)。 vmodule (string, default="") //分文件(不包含文件名称后缀,支持通配符)设置自己定义日志的可输出级别,如:GLOG_vmodule=server=2,client=3 表示文件名称为server.* 的仅仅输出小于 2 的日志,文件名称为 client.* 的仅仅输出小于 3 的日志。 假设同一时候使用 GLOG_v 选项,将覆盖 GLOG_v 选项。
很多其它执行參数见 logging.cc 中的 DEFINE_ 开头的定义。
执行參数设置的第三种方法是,能够在代码里通过加上 FLAGS_ 前缀来设置,如:
FLAGS_stderrthreshold=google::INFO;
FLAGS_colorlogtostderr=true;
3、条件输出:
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; //当条件满足时输出日志
LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie"; //google::COUNTER 记录该语句被运行次数。从1開始。在第一次运行输出日志之后,每隔 10 次再输出一次日志信息
LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER << "th big cookie"; //上述两者的结合,只是要注意,是先每隔 10 次去推断条件是否满足,假设滞则输出日志;而不是当满足某条件的情况下,每隔 10 次输出一次日志信息。 LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie"; //当此语句运行的前 20 次都输出日志。然后不再输出
演示代码例如以下:
#include <glog/logging.h> int main(int argc,char* argv[])
{
google::InitGoogleLogging(argv[0]);
FLAGS_stderrthreshold=google::INFO;
FLAGS_colorlogtostderr=true;
for(int i = 1; i <= 100;i++)
{
LOG_IF(INFO,i==100)<<"LOG_IF(INFO,i==100) google::COUNTER="<<google::COUNTER<<" i="<<i;
LOG_EVERY_N(INFO,10)<<"LOG_EVERY_N(INFO,10) google::COUNTER="<<google::COUNTER<<" i="<<i;
LOG_IF_EVERY_N(WARNING,(i>50),10)<<"LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER="<<google::COUNTER<<" i="<<i;
LOG_FIRST_N(ERROR,5)<<"LOG_FIRST_N(INFO,5) google::COUNTER="<<google::COUNTER<<" i="<<i;
}
google::ShutdownGoogleLogging();
}
输出结果例如以下:
I1210 13:23:20.059790 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=1 i=1
E1210 13:23:20.060670 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=1 i=1
E1210 13:23:20.061272 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=2 i=2
E1210 13:23:20.061337 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=3 i=3
E1210 13:23:20.061393 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=4 i=4
E1210 13:23:20.061450 6322 test01.cpp:13] LOG_FIRST_N(INFO,5) google::COUNTER=5 i=5
I1210 13:23:20.061506 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=11 i=11
I1210 13:23:20.061529 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=21 i=21
I1210 13:23:20.061553 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=31 i=31
I1210 13:23:20.061575 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=41 i=41
I1210 13:23:20.061599 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=51 i=51
W1210 13:23:20.061621 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=51 i=51
I1210 13:23:20.061667 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=61 i=61
W1210 13:23:20.061691 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=61 i=61
I1210 13:23:20.061738 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=71 i=71
W1210 13:23:20.061761 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=71 i=71
I1210 13:23:20.061807 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=81 i=81
W1210 13:23:20.061831 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=81 i=81
I1210 13:23:20.061877 6322 test01.cpp:11] LOG_EVERY_N(INFO,10) google::COUNTER=91 i=91
W1210 13:23:20.061902 6322 test01.cpp:12] LOG_IF_EVERY_N(INFO,(i>50),10) google::COUNTER=91 i=91
I1210 13:23:20.062140 6322 test01.cpp:10] LOG_IF(INFO,i==100) google::COUNTER=0 i=100
4、日志类型
LOG //内置日志
VLOG //自己定义日志
DLOG //DEBUG模式可输出的日志
DVLOG //DEBUG模式可输出的自己定义日志
SYSLOG //系统日志。同一时候通过 syslog() 函数写入到 /var/log/message 文件
PLOG //perror风格日志,设置errno状态并输出到日志中
RAW_LOG //线程安全的日志,须要#include <glog/raw_logging.h>
前六种的日志用法全然同样(包含条件日志输出),而 RAW_LOG 用法比較特殊,且不支持条件日志输出。另外不接受 colorlogtostderr 的颜色设置。自己定义日志也不接受 colorlogtostderr
的颜色设置,另外其日志严重级别也为自己定义数字。且与默认日志严重级别相反。数字越小严重级别越高。如:
1 #include <glog/logging.h>
2 #include <glog/raw_logging.h>
3
4 class GLogHelper
5 {
6 public:
7 GLogHelper(char* program)
8 {
9 google::InitGoogleLogging(program);
10 FLAGS_stderrthreshold=google::INFO;
11 FLAGS_colorlogtostderr=true;
12 FLAGS_v = 3;
13 }
14 ~GLogHelper()
15 {
16 google::ShutdownGoogleLogging();
17 }
18 };
19
20 int main(int argc,char* argv[])
21 {
22 GLogHelper gh(argv[0]);
23 LOG(ERROR)<<"LOG";
24 VLOG(3)<<"VLOG";
25 DLOG(ERROR)<<"DLOG";
26 DVLOG(3)<<"DVLOG";
27 SYSLOG(ERROR)<<"SYSLOG";
28 PLOG(ERROR)<<"PLOG";
29 RAW_LOG(ERROR,"RAW_LOG");
30 }
输出结果例如以下:
E1211 03:04:22.718116 13083 test01.cpp:23] LOG
I1211 03:04:22.719225 13083 test01.cpp:24] VLOG
E1211 03:04:22.719297 13083 test01.cpp:25] DLOG
I1211 03:04:22.719365 13083 test01.cpp:26] DVLOG
E1211 03:04:22.719391 13083 test01.cpp:27] SYSLOG
E1211 03:04:22.719650 13083 test01.cpp:28] PLOG: Success [0]
E1211 03:04:22.719650 13083 test01.cpp:29] RAW: RAW_LOG
5、CHECK 宏
当通过该宏指定的条件不成立的时候,程序会中止,而且记录相应的日志信息。功能类似于ASSERT,差别是 CHECK 宏不受 NDEBUG 约束,在 release 版中相同有效。
眼下这个功能我临时不须要,就不实践了,简介下,如:
CHECK(port == 80)<<"HTTP port 80 is not exit.";
其他还有:CHECK_EQ、 CHECK_NOTNULL、CHECK_STREQ、CHECK_DOUBLE_EQ 等推断数字、空指针。字符串。浮点数的 CHECK 宏。须要使用时能够搜索 glog/logging.h 文件里以 CHECK_ 开头的宏定义。
此外。类似的。还有 PCHECK 和 RAW_CHECK 版本号,用法类似。仅仅是 RAW_CHECK 用法特殊。形如 RAW_CHECK(i<3,"RAW_CHECK");
6、core dumped
通过 google::InstallFailureSignalHandler(); 就可以注冊。将 core dumped 信息输出到 stderr,如:
#include <glog/logging.h>
#include <string>
#include <fstream> //将信息输出到单独的文件和 LOG(ERROR)
void SignalHandle(const char* data, int size)
{
std::ofstream fs("glog_dump.log",std::ios::app);
std::string str = std::string(data,size);
fs<<str;
fs.close();
LOG(ERROR)<<str;
} class GLogHelper
{
public:
GLogHelper(char* program)
{
google::InitGoogleLogging(program);
FLAGS_colorlogtostderr=true;
google::InstallFailureSignalHandler();
//默认捕捉 SIGSEGV 信号信息输出会输出到 stderr,能够通过以下的方法自己定义输出方式:
google::InstallFailureWriter(&SignalHandle);
}
~GLogHelper()
{
google::ShutdownGoogleLogging();
}
}; void fun()
{
int* pi = new int;
delete pi;
pi = 0;
int j = *pi;
} int main(int argc,char* argv[])
{
GLogHelper gh(argv[0]);
fun();
}
输出的错误报告例如以下,可定位错误于 fun() 函数内:
E1211 06:07:04.787719 15444 test01.cpp:11] *** Aborted at 1386742024 (unix time) try "date -d @1386742024" if you are using
GNU date ***
E1211 06:07:04.789120 15444 test01.cpp:11] PC: @ 0x401227 fun()
E1211 06:07:04.789481 15444 test01.cpp:11] *** SIGSEGV (@0x0) received by PID 15444 (TID 0x7f03ce478720) from PID 0; stack trace: ***
E1211 06:07:04.791168 15444 test01.cpp:11] @ 0x7f03cd505960 (unknown)
E1211 06:07:04.791453 15444 test01.cpp:11] @ 0x401227 fun()
E1211 06:07:04.791712 15444 test01.cpp:11] @ 0x40125b main
E1211 06:07:04.792908 15444 test01.cpp:11] @ 0x7f03cd4f1cdd __libc_start_main
E1211 06:07:04.793227 15444 test01.cpp:11] @ 0x400fc9 (unknown)
段错误 (core dumped)
假设不使用 google::InstallFailureSignalHandler(); 则仅仅会输出 “段错误” 三个字,难于排查。
7、其他经常使用配置
google::SetLogDestination(google::ERROR,"log/prefix_"); //第一个參数为日志级别。第二个參数表示输出文件夹及日志文件名称前缀。
google::SetStderrLogging(google::INFO); //输出到标准输出的时候大于 INFO 级别的都输出;等同于 FLAGS_stderrthreshold=google::INFO;
FLAGS_logbufsecs =0; //实时输出日志
FLAGS_max_log_size =100; //最大日志大小(MB)
#define GOOGLE_STRIP_LOG 3 // 小于此级别的日志语句将在编译时清除。以减小编译后的文件大小,必须放在 #include 前面才有效。
8、日志文件说明
假设可运行文件名称为 "test",则将日志输出到文件后。还会生成 test.ERROR,test.WARNING,test.INFO 三个链接文件。分别链接到相应级别的日志文件。假设日志输出超过 FLAGS_max_log_size 设置的大小,则会分为多个文件存储。链接文件就会指向当中最新的相应级别的日志文件。
所以当日志文件较多时,查看链接文件来查看最新日志挺方便的。
三、有用封装
GLogHelper.h 例如以下:
#include <glog/logging.h>
#include <glog/raw_logging.h> //将信息输出到单独的文件和 LOG(ERROR)
void SignalHandle(const char* data, int size); class GLogHelper
{
public:
//GLOG配置:
GLogHelper(char* program);
//GLOG内存清理:
~GLogHelper();
};
GlogHelper.cpp 例如以下:
#include <stdlib.h>
#include "GLogHelper.h" //配置输出日志的文件夹:
#define LOGDIR "log"
#define MKDIR "mkdir -p "LOGDIR //将信息输出到单独的文件和 LOG(ERROR)
void SignalHandle(const char* data, int size)
{
std::string str = std::string(data,size);
/*
std::ofstream fs("glog_dump.log",std::ios::app);
fs<<str;
fs.close();
*/
LOG(ERROR)<<str;
//也能够直接在这里发送邮件或短信通知。只是这种方法是被回调多次的(每次回调仅仅输出一行错误信息,所以如上面的记录到文件,也须要>以追加模式方可)。所以这里发邮件或短信不是非常适合。只是倒是能够调用一个 SHELL 或 PYTHON 脚本,而此脚本会先 sleep 3秒左右,然后将错
误信息通过邮件或短信发送出去,这样就不须要监控脚本定时高频率运行,浪费效率了。
} //GLOG配置:
GLogHelper::GLogHelper(char* program)
{
system(MKDIR);
google::InitGoogleLogging(program); google::SetStderrLogging(google::INFO); //设置级别高于 google::INFO 的日志同一时候输出到屏幕
FLAGS_colorlogtostderr=true; //设置输出到屏幕的日志显示对应颜色
//google::SetLogDestination(google::ERROR,"log/error_"); //设置 google::ERROR 级别的日志存储路径和文件名称前缀
google::SetLogDestination(google::INFO,LOGDIR"/INFO_"); //设置 google::INFO 级别的日志存储路径和文件名称前缀
google::SetLogDestination(google::WARNING,LOGDIR"/WARNING_"); //设置 google::WARNING 级别的日志存储路径和文件名称前缀
google::SetLogDestination(google::ERROR,LOGDIR"/ERROR_"); //设置 google::ERROR 级别的日志存储路径和文件名称前缀
FLAGS_logbufsecs =0; //缓冲日志输出,默觉得30秒。此处改为马上输出
FLAGS_max_log_size =100; //最大日志大小为 100MB
FLAGS_stop_logging_if_full_disk = true; //当磁盘被写满时,停止日志输出
google::SetLogFilenameExtension("91_"); //设置文件名称扩展。如平台?或其他须要区分的信息
google::InstallFailureSignalHandler(); //捕捉 core dumped
google::InstallFailureWriter(&SignalHandle); //默认捕捉 SIGSEGV 信号信息输出会输出到 stderr,能够通过以下的方法自己定义输出>方式:
}
//GLOG内存清理:
GLogHelper::~GLogHelper()
{
google::ShutdownGoogleLogging();
}
測试文件 test.cpp 例如以下:
#include "GLogHelper.h" int main(int argc,char* argv[])
{
//要使用 GLOG ,仅仅须要在 main 函数開始处加入这句就可以
GLogHelper gh(argv[0]); LOG(INFO)<<"INFO";
LOG(ERROR)<<"ERROR";
}
三、自己定义改动
參考:http://www.cppfans.org/1566.html
1、添加日志按天输出
glog默认是依据进程ID是否改变和文件大小是否超过预定值来确定是否须要新建日志文件的。此处能够參考glog源代码 logging.cc 文件里的 void LogFileObject::Write 函数中
if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() ||
PidHasChanged()) {
我们仅仅须要在此处加一个日期推断就能够了,PidHasChanged() 定义于 utilities.cc 文件里,能够加一个类似的 DayHasChanged() 函数(注意 utilities.h 文件里加上函数声明):
static int32 g_main_day = 0;
bool DayHasChanged()
{
time_t raw_time;
struct tm* tm_info; time(&raw_time);
tm_info = localtime(&raw_time); if (tm_info->tm_mday != g_main_day)
{
g_main_day = tm_info->tm_mday;
return true;
} return false;
}
再改动 void LogFileObject::Write 函数中的推断条件就可以:
if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() ||
PidHasChanged() || DayHasChanged()) {
(注:參考 http://www.cppfans.org/1566.html)
版权声明:本文博客原创文章,博客,未经同意,不得转载。
初次使用glog的更多相关文章
- HashTable初次体验
用惯了数组.ArryList,初次接触到HashTable.Dictionary这种字典储存对于我来说简直就是高大上. 1.到底什么是HashTable HashTable就是哈希表,和数组一样,是一 ...
- 初次启动app校验的活动图和分析
初次启动活动图 version 1 version 2 version 3 根据上图的活动图分析,可能存在较严重的问题: 主线程中如果发现是sdcard的url,则可能进行重命名 FirstEnter ...
- 百度地图API试用--(初次尝试)
2016-03-17: 百度地图API申请key的步骤相对简单,不做过多阐述. 初次使用百度地图API感觉有点神奇,有些功能加进来以后有点问题,注释掉等有空再解决. 代码如下: <%@ page ...
- [masmplus]初次使用报external symbol _start 是配置问题
初次使用masmplus 其中在 codesg segment 使用了 start 标记, 并在end处标明了:end start 但是默认的masmplus 会提示 start 为 不认识的 e ...
- 初次接触json...
这两天发现很多网站显示图片版块都用了瀑布流模式布局的:随着页面滚动条向下滚动,这种布局还会不断加载数据并附加至当前尾部.身为一个菜鸟级的程序员,而且以后可能会经常与网站打交道,我觉得我还是很有必要去尝 ...
- Zookeeper初次使用
下面介绍Linux系统中Zookeeper的初次使用方法. 1.jdk安装和zookeeper下载 首先从jdk官网中下载jdk文件,然后将文件放在/usr/local/java目录下解压,并打开.b ...
- 初次使用IDEA的相关技巧
前言:由于初次使用IDEA,所以很多配置都不是非常熟悉,经过一下午慢慢熟悉和同事的帮助,终于有所斩获,现在我把这个总结写出来,希望能够帮助初次使用的java工程师. 1:下载和安装 下载地址:http ...
- 20145330《Java学习笔记》第一章课后练习8知识总结以及IDEA初次尝试
20145330<Java学习笔记>第一章课后练习8知识总结以及IDEA初次尝试 题目: 如果C:\workspace\Hello\src中有Main.java如下: package cc ...
- 关于初次使用Verilog HDL语言需要懂的基本语法
关于初次使用Verilog HDL语言需要懂的基本语法 1.常量 数字表达式全面的描述方式为:<位宽><进制><数字> 8’b10101100,表示位宽为8的二进制 ...
随机推荐
- [SCSS] Write similar classes with the SCSS @for Control Directive
Writing similar classes with minor variations, like utility classes, can be a pain to write and upda ...
- 【2001】关于N!的问题
Time Limit: 3 second Memory Limit: 2 MB 编写程序,计算n!以十进制数形式表示的数中最右边一个非零数字,并找出在它右边有几个零. 例如:12!=1*2*3*4*5 ...
- Java8获取参数名及Idea/Eclipse/Maven配置
在Java8之前,代码编译为class文件后,方法参数的类型固定,但是方法名称会丢失,方法名称会变成arg0.arg1.....而现在,在Java8开始可以在class文件中保留参数名,这就给反射带来 ...
- Ajax基础与Json应用(一)
一.Ajax概念 Ajax是异步的javacript和xml 发音: Ajax [ˈeɪˌdʒæks] 二.同步与异步 传统方式(同步):一个请求对应一个回应,他们是同步的,回应不完成,没办法对这个页 ...
- [React Router v4] Parse Query Parameters
React Router v4 ignores query parameters entirely. That means that it is up to you to parse them so ...
- VS2013 Qt5显示中文字符
VS2013上建立的Qt5project中显示中文字符的两种方式: 1. QStringLiteral("開始") 2. QString::fromLocal8Bit(" ...
- Oracle成长点点滴滴(2)— 权限管理
权限管理中权限包含系统权限以及对象权限.在解说权限管理之前我们先来了解用户的创建以及授权这些前提. 1. 创建用户以及授权 Ø 默认用户 既然提到了创建用户,首先必须先把用户的知识攻克了. ...
- php正则怎么使用(最全最细致)
php正则怎么使用(最全最细致) 一.总结 一句话总结: 1.正则中的行定位符是什么? 解答:(^与$) 2.正则中什么时候用行定位符? 解答:如"^de",表示以de开头的字符串 ...
- 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(1)GCC介绍及C语言编译过程
一.GCC基本介绍 GCC(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的编程语言编译器.它是一套以GPL及LGPL许可证所发布的自由软件,也是GNU计划的关 ...
- idea中建立一个OSGI项目
参考网址:http://chenjingbo.iteye.com/blog/1893597 首先我使用的是equinox作为我的osgi framework 直接在官网上解压下载即可,第一步creta ...