diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8fbbd220e3ab95fe0330c98567f642bc51a8716e --- /dev/null +++ b/lib/README.md @@ -0,0 +1,84 @@ +# Cat Client + +## Overview + +The following programming languages are supported: + +* C +* C++ +* Python +* Go +* Node.js + +And the following programming language is in our plan: + +* PHP +* C# (.net) + +## Word explanations + +### Message types + +* Transaction + +* Event + +* Heartbeat + +* Metric + +### Message properties + +* type + + Represents a category of message, like `SQL`, `RPC` or `HTTP`. + +* name + + Represents a specified action, for example + + * If the **type** is `SQL`, the **name** may be `select from user where id = `, which is the base SQL. + * If the **type** is `RPC`, the **name** may be `QueryOrderByUserId(string, int)`, which is the signature of an api. + * If the **type** is `HTTP`, the **name** may be `/api/v8/{int}/orders`, which is the base uri. + + > Detailed information should be recorded in the **data** field, like the parameters of an API. + +* status + + Represents the status of the message. + + The message will be treated as a "problem" if the status of it is not equal to "0", and will be shown in the problem report. Once a message has been treated as a "problem", whatever what type it is, the current message tree will not be aggregated, that means you can always get the structure of a "problemed" logview. + +* data + + Record the detailed information about a message. + + * If the **type** is `SQL`, the **data** may be `id=75442432` + * If the **type** is `RPC`, the **data** may be `userType=dianping&userId=9987` + * If the **type** is `HTTP`, the **data** may be `orderId=75442432` + + `data` field may also contain `error stack trace` in some cases. (like represent an exception or an error) + +* timestamp + + Represents the created time of the message, will be displayed in `logview` or `message tree`. + + The number of **milliseconds** that have elapsed since `1970-01-01 00:00:00` + +#### Transaction-only properties + +* duration + + Represents the total time in **milliseconds** that a transaction has cost. + + It is calculated while a transaction has been completed. + + > duration = currentTimestamp() - durationStart + + You can always specify the `duration` through related APIs before the transaction has been completed, and skip the calculating process. + +* durationStart + + Represents the start time of a transaction. + + It can be different with `timestamp`, the `durationStart` is only used for calculating the `duration` of a transaction, overwrite `durationStart` won't influence `timestamp`. diff --git a/lib/_/preparations.md b/lib/_/preparations.md new file mode 100644 index 0000000000000000000000000000000000000000..96ad5dd82a5a7db3b845179a2e644cef91c39230 --- /dev/null +++ b/lib/_/preparations.md @@ -0,0 +1,22 @@ +# Preparations before initializing cat client. + +1. Create `/data/appdatas/cat` directory. + + Make sure that you have **read and write (>=0644)** permission of the created directory. + +2. Create `/data/applogs/cat` directory. (optional) + + This directory is used for preserving debug logs, which can be very useful while debugging, **read and write** permission is also required. + +3. Create `/data/appdatas/cat/client.xml` with following contents. + + ```xml + + + + + + + ``` + + > Don't forget to change the **\** to your server ip address. diff --git a/lib/_/preparations.zh-CN.md b/lib/_/preparations.zh-CN.md new file mode 100644 index 0000000000000000000000000000000000000000..d4094f5409e6c482605d6ef1c9f00e53e03daeea --- /dev/null +++ b/lib/_/preparations.zh-CN.md @@ -0,0 +1,22 @@ +# 启动 cat 客户端前的准备工作 + +1. 创建 `/data/appdatas/cat` 目录 + + 确保你具有这个目录的读写权限。 + +2. 创建 `/data/applogs/cat` 目录 (可选) + + 这个目录是用于存放运行时日志的,这将会对调试提供很大帮助,同样需要读写权限。 + +3. 创建 `/data/appdatas/cat/client.xml`,内容如下 + + ```xml + + + + + + + ``` + + > 不要忘记把 **\** 替换成你自己的服务器地址哦! diff --git a/lib/_/zh-CN.md b/lib/_/zh-CN.md new file mode 100644 index 0000000000000000000000000000000000000000..015deaa8800f2f4e56d60edca06598be40c0d21c --- /dev/null +++ b/lib/_/zh-CN.md @@ -0,0 +1,84 @@ +# Cat Client + +## Overview + +我们目前支持以下编程语言: + +* C +* C++ +* Python +* Go +* Node.js + +以下编程语言在我们的支持计划中: + +* PHP +* C# (.net) + +## 名词解释 + +### 消息类型 + +* Transaction + +* Event + +* Heartbeat + +* Metric + +### 消息属性 + +* type + + 表示一种类型的消息,比如 `SQL`,`RPC` 或 `HTTP`。 + +* name + + 表示一个特定的行为,举例来说: + + * 如果 **type** 是 `SQL`, **name** 可以是 `select from user where id = `, 表示一个 SQL 模版。 + * 如果 **type** 是 `RPC`, **name** 可以是 `QueryOrderByUserId(string, int)`, 表示一个 API 的函数签名。 + * 如果 **type** 是 `HTTP`, **name** 可以是 `/api/v8/{int}/orders`, 表示基础 URI。 + + > 更详细的信息建议在 **data** 字段中记录,比如 api 的参数 + +* status + + 表示消息的状态。 + + 当消息的状态不为 "0" 时,会被标记成一个 "problem"。无论消息类型是什么,只要被标记为 "problem",它所在的消息树就不会被聚合,这也意味着你随时可以获取它完整的日志信息。 + +* data + + 记录一个消息的详细信息 + + * 如果 **type** 是 `SQL`, **data** 可以是 `id=75442432` + * 如果 **type** 是 `RPC`, **data** 可以是 `userType=dianping&userId=9987` + * 如果 **type** 是 `HTTP`, **data** 可以是 `orderId=75442432` + + 在一些情况下,`data` 字段会包含错误堆栈信息(比如代表一个 exception 或者 error) + +* timestamp + + 代表消息的创建时间,会在消息树中展示。 + + 从 `1970-01-01 00:00:00` 开始到创建时经过的毫秒数。 + +#### Transaction 特有的参数 + +* duration + + 表示一个 transaction 花费的时间,以毫秒计算。 + + 会在 transaction 被完成时计算。 + + > duration = currentTimestamp() - durationStart + + 你可以通过相关 API 在 transaction 被完成前指定 `duration` 的值,并且跳过计算过程。 + +* durationStart + + 表示 transaction 开始执行的时间。 + + 区别于 `timestamp`,`durationStart` 只用来计算 transaction 的 `duration`,修改 `durationStart` 不会影响 `timestamp`。 diff --git a/lib/c/CMakeLists.txt b/lib/c/CMakeLists.txt index 6b2b15b3f09b1df8cc30a0f93b0e7cd8e3fb9e18..472d703e435aacc56710868e1b2df5e366b11ba5 100644 --- a/lib/c/CMakeLists.txt +++ b/lib/c/CMakeLists.txt @@ -1,7 +1,11 @@ -cmake_minimum_required(VERSION 2.4) +cmake_minimum_required(VERSION 2.8) project(ccat) +set(CCAT_VERSION 3.0.0) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/ccat/version.h) + message(STATUS "Current OS: " ${CMAKE_SYSTEM_NAME}) macro(use_c99) @@ -14,19 +18,6 @@ macro(use_c99) endif () endmacro(use_c99) -macro(use_cxx11) - include(CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) - CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) - if(COMPILER_SUPPORTS_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - elseif(COMPILER_SUPPORTS_CXX0X) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") - else() - message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") - endif() -endmacro(use_cxx11) - macro(add_definitions_c def) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${def}") endmacro() @@ -36,10 +27,7 @@ macro(add_cxx_flags def) endmacro() use_c99() -use_cxx11() -# set(BUILD_TYPE CPP) -# set(BUILD_TEST 1) # set(BUILD_SCRIPT 1) add_definitions("-Wno-format-security") @@ -53,6 +41,7 @@ endif() include_directories(include) include_directories(src) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/include) set( HEADER_FILES @@ -63,7 +52,7 @@ set( src/lib/cat_thread.h src/lib/headers.h src/lib/typedef.h - include/ccat/client.h + include/client.h ) aux_source_directory(src/lib LIBRARY_FILES) @@ -82,37 +71,11 @@ elseif(UNIX) link_libraries(pthread) endif() -if (BUILD_TYPE STREQUAL CPP) - set(HEADER_FILES include/cppcat/client.h ${HEADER_FILES}) - aux_source_directory(src/cppcat CPP_SOURCE_FILES) - set(SOURCE_FILES ${SOURCE_FILES} ${CPP_SOURCE_FILES}) - add_library(catclient SHARED ${HEADER_FILES} ${SOURCE_FILES}) - install(TARGETS catclient DESTINATION lib) -else() - set(HEADER_FILES include/ccat/client.h ${HEADER_FILES}) - add_library(catclient SHARED ${HEADER_FILES} ${SOURCE_FILES}) - install(TARGETS catclient DESTINATION lib) -endif() - -if (BUILD_TEST) - link_libraries(gtest) - file( - COPY - tests/client.xml - tests/client.json - DESTINATION cat - ) - - aux_source_directory(tests/unit UNITTEST_FILES) - add_executable(unittest tests/unittest.cpp ${UNITTEST_FILES} ${SOURCE_FILES}) -endif() +set(HEADER_FILES include/client.h ${HEADER_FILES}) +add_library(catclient STATIC SHARED ${HEADER_FILES} ${SOURCE_FILES}) +install(TARGETS catclient DESTINATION lib) if (BUILD_SCRIPT) - if (BUILD_TYPE STREQUAL CPP) - add_executable(test_client ${HEADER_FILES} ${SOURCE_FILES} benchmark/cat_client_test.cpp) - add_executable(test_mpsc ${HEADER_FILES} ${SOURCE_FILES} benchmark/cat_mpsc_test.cpp) - else() - add_executable(test_client ${HEADER_FILES} ${SOURCE_FILES} benchmark/cat_client_test.c) - add_executable(test_mpsc ${HEADER_FILES} ${SOURCE_FILES} benchmark/cat_mpsc_test.c) - endif() + add_executable(test_client ${HEADER_FILES} ${SOURCE_FILES} scripts/cat_client_test.c) + add_executable(test_mpsc ${HEADER_FILES} ${SOURCE_FILES} scripts/cat_mpsc_test.c) endif() diff --git a/lib/c/README.md b/lib/c/README.md index 310c4dc54d1c891bcc699feccb42a151e2246f7e..147b83674a3ea8991ad7517f070e7a049c879cfb 100644 --- a/lib/c/README.md +++ b/lib/c/README.md @@ -1,89 +1,66 @@ -# Cat Client C & C++ +# Cat Client for C -## Building & Installation +[中文文档](./docs/zh-CN.md) -The cat client can be compiled and used on Linux (both glibc and musl-libc) and OSX. +The cat client can be compiled and used on both Linux (both glibc and musl-libc) and OSX. -You need to have a C compiler (supporting C99) and a C++ compiler (supporting C++11) if you want to run test cases or build c++ version of cat client. +The following Operating Systems are tested: -You also need to have `cmake` and `make` installed, which we use for building static or dynamic libraries, and executable binary files (like test or benchmark script). +* OSX (>=10.13) +* Alpine linux +* CentOS 6 +* CentOS 7 +* Ubuntu 14.04 LTS +* Ubuntu 16.04 LTS +* Ubuntu 18.04 LTS -Upon you get your environment ready, it's easy to build and install ccat. +We also offered a cpp version, please refer to [cppcat](../cpp) for further information. -(In the project root dir, which contains CMakeLists.txt) +## Compilation -``` -mkdir -p cmake -cd cmake -make -j 4 -``` +You need to have a C compiler (supporting C99) installed. -Build test cases if you want. ([googletest]() and a c++ compiler is required) +You also need to have `cmake` and `make` installed, which we use for building static or dynamic libraries and executable binary files. -``` -make -j 4 -DBUILD_TEST=1 -``` +Once you have your environment ready, it's easy to build and install `ccat`. -Build c++ version of cat client. +(In the project root dir, which contains CMakeLists.txt) ``` -make -j 4 -DBUILD_TYPE=CPP +mkdir -p cmake +cd cmake +make -j 4 ``` ### Installation -This command will install libcatclient.so (or .dylib in osx) to `LD_LIBRARY_PATH`, which in most cases is `/usr/local/lib` +The following command will install libcatclient.so (or .dylib in osx) to your `LD_LIBRARY_PATH`, which is `/usr/local/lib` in most cases. ``` make install ``` -and it can be used as a built-in shared library +Now it can be used as a built-in shared library. ``` gcc -lcatclient x.c ``` ## Initialization -First of all, you have to create `/data/appdatas/cat` directory, read and write permission is required (0644).`/data/applogs/cat` is also required if you'd like to preserve a debug log, it can be very useful while debugging. - -And create a config file `/data/appdatas/cat/client.xml` with the following contents. - -```xml - - - - - - -``` - -Don't forget to change the `` to your own after you copy and paste the contents. +Some [preparations](../_/preparations.md) needs to be done before initialize `ccat`. -With all the preparations have done, It's easy to initialize it in your c codes. +With all the preparations have been done, it's easy to initialize `ccat` in your c codes. ```c -#include +#include catClientInit("appkey"); ``` -> Only English characters, numbers, underscore and dash is allowed in appkey. - -If you are using our cpp version, you can initialize it by following codes. +> Only English characters (a-z, A-Z), numbers (0-9), underscore (\_) and dash (-) is allowed in appkey. -```cpp -#include - -cat::init("appkey"); -``` - -We also have many initialize options, like enable text-encoder (adapt to lower server version), disable sampling or heartbeat which is enabled by default. - -See [this link]() +Note that `sampling`, built-in `heartbeat` and `binary` encoder is enabled by default, which you may want to disable it. We also offered an API to customize your initialization, please refer to our [apidoc](./docs/api.md). ## Documentation -[ccat](./doc/api-c.md) - -[cppcat](./doc/api-cpp.md) +[API doc](./docs/api.md) diff --git a/lib/c/benchmark/cat_ip_test.c b/lib/c/benchmark/cat_ip_test.c deleted file mode 100644 index 1581850deff551c0018c53c47f8eb15f949f37f7..0000000000000000000000000000000000000000 --- a/lib/c/benchmark/cat_ip_test.c +++ /dev/null @@ -1,84 +0,0 @@ -// -// Created by Terence on 2018/8/27. -// - -#include -#include -#include -#include -#include -#include - -#define NULL 0 - -int ipAddressLevel(struct in_addr *addr, int offset) { - uint32_t a = addr->s_addr; - if ((a & 0xFF) == 10) { - return 1 + offset; - } else if (((a & 0xFF) == 172) && ((a >> 8 & 0xF0) == 16)) { - return 1 + offset; - } else if (((a & 0xFF) == 192) && ((a >> 8 & 0xFF) == 168)) { - return 1 + offset; - } else { - return 3 + offset; - } -} - -int getLocalHostIp(char *ip) { - struct ifaddrs *interfaces = NULL; - if (getifaddrs(&interfaces)) { - return -1; - } - - struct in_addr *res = NULL; - int res_level = 0, tmp_level = 0; - - struct ifaddrs *tmp; - for (tmp = interfaces; NULL != tmp; tmp = tmp->ifa_next) { - char ipbuf[64]; - char hostname[NI_MAXHOST]; - - if (tmp->ifa_addr->sa_family == AF_INET) { - - // ignore not up interface. - if (!(tmp->ifa_flags & IFF_UP)) { - continue; - } - - // ignore loopback interface. - if (tmp->ifa_flags & IFF_LOOPBACK) { - continue; - } - - // get hostname and ip - getnameinfo(tmp->ifa_addr, sizeof(struct sockaddr_in), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - struct in_addr *addr = &((struct sockaddr_in *) tmp->ifa_addr)->sin_addr; - if (NULL == inet_ntop(AF_INET, addr, ipbuf, 16)) { - continue; - } - // we put interface address with a hostname in a higher priority. - int offset = strcmp(ipbuf, hostname) == 0 ? 0 : 1; - - if (NULL == res) { - res = addr; - res_level = ipAddressLevel(res, offset); - } else if (res_level < (tmp_level = ipAddressLevel(addr, offset))) { - res = addr; - res_level = tmp_level; - } - } - } - freeifaddrs(interfaces); - - if (NULL == res) { - return -1; - } else { - return NULL == inet_ntop(AF_INET, res, ip, 16) ? -1 : 0; - } -} - -int main() { - char ip[16]; - getLocalHostIp(ip); - printf("%s", ip); -} \ No newline at end of file diff --git a/lib/c/doc/api-c.md b/lib/c/docs/api.md similarity index 60% rename from lib/c/doc/api-c.md rename to lib/c/docs/api.md index 03ca80fb0b2d39d2f229351ae2c2dc362a917afa..01c1da3744adabd1450f1050738d7bf087f93811 100644 --- a/lib/c/doc/api-c.md +++ b/lib/c/docs/api.md @@ -3,7 +3,7 @@ ## Quickstart ```c -#include "ccat/client.h" +#include "client.h" void test() { /** @@ -29,8 +29,8 @@ void test() { CatTransaction *t3 = newTransaction("foo", "bar3"); t3->setStatus(t3, CAT_SUCCESS); /** - * Upon you complete the transaction. - * The transaction will be popped from the stack and the duration will be set. + * Once you complete the transaction. + * The transaction will be popped from the stack and the duration will be calculated. */ t3->complete(t3); @@ -42,7 +42,7 @@ void test() { CatTransaction *t4 = newTransactionWithDuration("foo", "bar4-with-duration", 1000); snprintf(buf, 9, "bar%d", k); /** - * Log a event, append the event to the children of current transaction. + * Log an event, it will be added to the children list of the current transaction. */ logEvent("foo", buf, CAT_SUCCESS, NULL); t4->setStatus(t4, CAT_SUCCESS); @@ -51,8 +51,9 @@ void test() { t1->setStatus(t1, CAT_SUCCESS); /** - * When the last element of stack is popped. - * A message tree will be generated and sent to server. + * Complete the transaction and poped it from the stack. + * When the last element of the stack is popped. + * The message tree will be encoded and sent to server. */ t1->complete(t1); } @@ -67,7 +68,6 @@ int main(int argc, char **argv) { catClientDestroy(); return 0; } - ``` ## API list @@ -76,7 +76,7 @@ int main(int argc, char **argv) { #### catClientInit -initialize cat client by default configs. +initialize `ccat` by default configs. ```c int catClientInit(const char *appkey); @@ -88,15 +88,17 @@ This is equivalent to the following codes. return catClientInitWithConfig(appkey, &DEFAULT_CCAT_CONFIG); ``` -* `binary` encoder -* built-in `heartbeat` enabled -* `sampling` enabled -* `multi process mode` disabled -* `debug log` disabled +With following configs: + +* `encoderType` = CAT_ENCODER_BINARY. +* `enableHeartbeat` is true. +* `enableSampling` is true. +* `enableMultiprocessing` is false. +* `enableDebugLog` is false. #### catClientInitWithConfig -You may want to customize the cat client in some cases. +You may want to customize the `ccat` in some cases. ```c CatClientConfig config = DEFAULT_CCAT_CONFIG; @@ -107,7 +109,7 @@ catClientInitWithConfig("ccat", &config); #### catClientDestroy -Disable the cat client, shutdown `sender`, `monitor` and `aggregator` threads. +Disable the `ccat`, shutdown `sender`, `monitor` and `aggregator` threads. Also release all the resources that have been used. @@ -117,7 +119,7 @@ int catClientDestroy(); #### isCatEnabled -Represent if cat client is initialized. +Represent if `ccat` has been initialized. ```c int isCatEnabled(); @@ -125,27 +127,7 @@ int isCatEnabled(); ### Transaction -A message has the following properties. - -* `type` usually means a category of event, for example, `SQL`, `RPC`, `HTTP-GET` may be recommended types. -* `name` usually means a specified action. - * When the type is `SQL`, the name may be `select from user where user_id = ` - * When the type is `RPC`, the name may be `queryOrderByUserId` - * when the type is `HTTP-GET`, the name may be `/api/v2/order/` -* while `status` is not equal to `CAT_SUCCESS` (which is "0"), the message will be treated as a `problem`, and can be recorded in our problem report. -* `data` is used for storing some useful infomation. - * When the type is `SQL`, the data may like `user_id=194432&token=a94238` - * When the type is `RPC`, the data may like `order_id=11314152` -* `timestamp` represents the time when the message has been created, which will be shown on our log view page. - -Though transaction is inherited from a message, it also has the foregoing properties. - -And there are some transaction-only properties: - -* `duration` means the time costs of a transaction. -* `durationStart` means the start time of a transaction. - -> Note the `durationStart` may not as same as `timestamp`, they have different meanings. +You can find more information about the properties of a transaction in [Message properties](../../README.md#message-properties) #### newTransaction @@ -155,7 +137,7 @@ Create a transaction. CatTransaction *newTransaction(const char *type, const char *name); ``` -We hid the properties of a transaction (due to safety reasons), but we offered a list of APIs to help you update them. +We hide the properties of a transaction (due to safety reasons), but we offered a list of APIs to help you to edit them. * addData * addKV @@ -166,7 +148,7 @@ We hid the properties of a transaction (due to safety reasons), but we offered a * setDurationInMillis * setDurationStart -They can be easily used, for example: +These can be easily used, for example: ```c CatTransaction *t = newTransaction("Test1", "A"); @@ -183,20 +165,16 @@ t->complete(t); There is something you may want to know: 1. Duration of a transaction will be calculated while you complete the transaction. (currentTimestamp - durationStart) -1. Although `durationStart` is as same as `timestamp`, you can overwrite it. -2. `durationStart` and `timestamp` are different, the first one represents the start time of a transaction, and the second one only means created time of a message. (Transaction is one kind of message) -1. `durationStart` won't work when you specified the `duration`. -2. You can call `addData` and `addKV` several times, the added data will be connected by `&`. - -> Don't forget to complete the transaction after you new it! Or you will get corrupted message trees and memory leaks! +2. It's meaningless to specify `duration` and `durationStart` in the same transaction, although we do it in the example :) +3. Never forget to complete the transaction! Or you will get corrupted message trees and memory leaks! #### newTransactionWithDuration Create a transaction with a specified duration in milliseconds. -Due to duration has been specified, it won't be overwritten after the transaction has been completed. +Due to `duration` has been specified, it won't be recalculated after the transaction has been completed. -> Note that the transaction has not been completed, so you have to complete it manually. +> Note that the transaction has not been completed! So it is necessary to complete it manually. ```c CatTransaction *newTransactionWithDuration(const char *type, const char *name, unsigned long long duration); @@ -210,17 +188,17 @@ t->setDurationInMillis(t4, duration); return t; ``` -> Don't forget to complete the transaction. +> Once again, don't forget to complete the transaction! #### newCompletedTransactionWithDuration -Log a transaction with a specified duration in milliseconds. +Log a transaction with a specified duration in milliseconds and complete it. -Due to the transaction has been auto-completed, the `durationStart` and the `timestamp` will be turned back. +Due to the transaction has been auto-completed, the `timestamp` will be turned back. > Note that the specified duration should be less than 60,000 milliseconds. > -> Or we won't turn back the start and created time. +> Or we won't turn back the timestamp. ```c int duration = 1000; @@ -237,7 +215,6 @@ CatTransaction *t = newTransaction("type", "name"); t->setDurationInMillis(t, duration); if (duration < 60000) { t->setTimestamp(t, GetTime64() - duration); - t->setDurationStart(t, GetTime64() - duration); } t->complete(t); return; @@ -285,8 +262,6 @@ CatEvent *newEvent(const char *type, const char *name); #### logMetricForCount -log a count metric. - ```c void logMetricForCount(const char *name, int quantity); ``` @@ -301,7 +276,7 @@ For example, if you called this API 3 times in one second (can be in different t void logMetricForDuration(const char *name, unsigned long long duration); ``` -Like `logMetricForCount`, the values reported in one second will be aggregated, the only difference is we reported `averaged` value instead of `summarized` value. +Like `logMetricForCount`, the metrics reported in the same second will be aggregated, the only difference is `averaged` value is used instead of `summarized` value. ### Heartbeat @@ -309,7 +284,7 @@ Like `logMetricForCount`, the values reported in one second will be aggregated, Create a heartbeat. -> Heartbeat is reported by cat client automatically, +> Heartbeat is reported by ccat automatically, > so you don't have to use this API in most cases, > unless you want to overwrite our heartbeat message. > diff --git a/lib/c/docs/api.zh-CN.md b/lib/c/docs/api.zh-CN.md new file mode 100644 index 0000000000000000000000000000000000000000..28de0a20c1061f77640ce863b431b8fb4e86baf2 --- /dev/null +++ b/lib/c/docs/api.zh-CN.md @@ -0,0 +1,291 @@ +# cat c client + +## 快速起步 + +```c +#include "client.h" + +void test() { + /** + * 如果当前的栈为空,一个消息树会被创建 + */ + CatTransaction *t1 = newTransaction("foo", "bar1"); + + /** + * Metric 可以在任何地方记录,并不会被加到消息树中 + */ + logMetricForCount("metric-count", 1); + logMetricForDuration("metric-duration", 200); + + /** + * 记录一个给定耗时的 transaction 并立刻完成它。 + */ + newCompletedTransactionWithDuration("foo", "bar2-completed", 1000); + + /** + * Transaction can be nested. + * Transaction 可以嵌套,最新的 transaction 会被推到栈顶 + */ + CatTransaction *t3 = newTransaction("foo", "bar3"); + t3->setStatus(t3, CAT_SUCCESS); + /** + * 当你完成一个 transaction 的时候,它会被从栈里面弹出,并且 duration 会被计算。 + */ + t3->complete(t3); + + char buf[10]; + for (int k = 0; k < 3; k++) { + /** + * 创建一个给定耗时的 transaction + */ + CatTransaction *t4 = newTransactionWithDuration("foo", "bar4-with-duration", 1000); + snprintf(buf, 9, "bar%d", k); + /** + * 记录一个 event,会被添加到当前 transaction 的儿子列表中 + */ + logEvent("foo", buf, CAT_SUCCESS, NULL); + t4->setStatus(t4, CAT_SUCCESS); + t4->complete(t4); + } + + t1->setStatus(t1, CAT_SUCCESS); + /** + * 完成 transaction 并将它从栈里弹出 + * 当最后一个元素被弹出时,消息树会被序列化并发送给服务端 + */ + t1->complete(t1); +} + +int main(int argc, char **argv) { + CatClientConfig config = DEFAULT_CCAT_CONFIG; + config.enableHeartbeat = 0; + config.enableDebugLog = 1; + catClientInitWithConfig("ccat", &config); + test(); + Sleep(3000); + catClientDestroy(); + return 0; +} +``` + +## API list + +### Common apis + +#### catClientInit + +使用默认参数初始化 `ccat`。 + +```c +int catClientInit(const char *appkey); +``` + +等价于下述代码。 + +```c +return catClientInitWithConfig(appkey, &DEFAULT_CCAT_CONFIG); +``` + +采用如下配置: + +* `encoderType` = CAT_ENCODER_BINARY. +* `enableHeartbeat` is true. +* `enableSampling` is true. +* `enableMultiprocessing` is false. +* `enableDebugLog` is false. + +#### catClientInitWithConfig + +有时你会想要自定义 `ccat`。 + +```c +CatClientConfig config = DEFAULT_CCAT_CONFIG; +config.enableHeartbeat = 0; +config.enableDebugLog = 1; +catClientInitWithConfig("ccat", &config); +``` + +#### catClientDestroy + +禁用 `ccat`,退出**sender**,**monitor**,和**aggregator** 线程。 + +释放所有已申请的资源。 + +```c +int catClientDestroy(); +``` + +#### isCatEnabled + +表示 `ccat` 是否已经被初始化。 + +Represent if cat client is initialized. + +```c +int isCatEnabled(); +``` + +### Transaction + +你可以在[消息属性](../../_/zh-CN.md#消息属性)中了解 transaction 的属性信息。 + +#### newTransaction + +创建一个 Transaction + +```c +CatTransaction *newTransaction(const char *type, const char *name); +``` + +由于安全原因,我们隐藏了 transaction 的属性,但我们提供了一系列 API 可供你修改它们。 + +* addData +* addKV +* setStatus +* setTimestamp +* complete +* addChild +* setDurationInMillis +* setDurationStart + +他们用起来很简单,就像这样: + +```c +CatTransaction *t = newTransaction("Test1", "A"); +t->setStatus(t, CAT_SUCCESS); +t->setTimestamp(t, GetTime64() - 5000); +t->setDurationStart(t, GetTime64() - 5000); +t->setDurationInMillis(t, 4200); +t->addData(t, "data"); +t->addKV(t, "k1", "v1"); +t->addKV(t, "k2", "v2"); +t->complete(t); +``` + +There is something you may want to know: + +这里有一些你可能想要知道的: + +1. 你可以调用 `addData` 和 `addKV` 多次,他们会被 `&` 连接起来。 +2. 同时指定 `duration` 和 `durationStart` 是没有意义的,尽管我们在样例中这样做了。 +3. 不要忘记完成 transaction!否则你会得到一个毁坏的消息树以及内存泄漏! + +#### newTransactionWithDuration + +创建一个给定耗时(毫秒)的 transaction + +鉴于 `duration` 已经被指定了,它不会在 transaction 完成时被重算。 + +> 注意 transaction 并没有被完成!所以你还需要手动完成它。 + +```c +CatTransaction *newTransactionWithDuration(const char *type, const char *name, unsigned long long duration); +``` + +这和下面的代码是等价的: + +``` +CatTransaction *t = newTransaction("type", "name"); +t->setDurationInMillis(t4, duration); +return t; +``` + +> 强调一遍,不要忘记完成 transaction! + +#### newCompletedTransactionWithDuration + +记录一个给定耗时(毫秒)的 transaction 并完成它。 + +鉴于 transaction 已经被自动完成了,`timestamp` 会被拨回。 + +> 注意我们只会在给定的耗时小于 60,000 毫秒时才会拨回 `timestamp` + +```c +int duration = 1000; +newCompletedTransactionWithDuration("type", "name", duration); +``` + +这和下面的代码是等价的: + +```c +// return current timestamp in milliseconds. +unsigned long GetTime64(); + +CatTransaction *t = newTransaction("type", "name"); +t->setDurationInMillis(t, duration); +if (duration < 60000) { + t->setTimestamp(t, GetTime64() - duration); +} +t->complete(t); +return; +``` + +### Event + +Event 是一个简化版的 Transaction,没有耗时。 + +#### logEvent + +记录一个 Event。 + +```c +void logEvent(const char *type, const char *name, const char *status, const char *data); +``` + +#### logError + +记录一个 Error。 + +```c +void logError(const char *msg, const char *errStr); +``` + +这和下面的代码是等价的: + +```c +logEvent("Exception", msg, CAT_ERROR, errStr); +``` + +#### newEvent + +创建一个 Event。 + +通常情况下你不需要用这个 API,除非你确实需要。 + +使用 logEvent / logError 是更好的选择。 + +```c +CatEvent *newEvent(const char *type, const char *name); +``` + +### Metric + +#### logMetricForCount + +```c +void logMetricForCount(const char *name, int quantity); +``` + +指标会每秒上报一次。 + +举例来说,如果你在同一秒内调用这个 API 三次(可以在不同的线程,我们使用线程安全的 map 来缓存指标的值),只有聚合后的值(求和)会被上报到服务端。 + +#### logMetricForDuration + +```c +void logMetricForDuration(const char *name, unsigned long long duration); +``` + +就像 `logMetricForCount` 一样,同一秒上报的指标会被聚合,唯一的区别是这里使用平均值取代求和。 + +### Heartbeat + +#### newHeartBeat + +创建一个 Heartbeat。 + +> 心跳会被 ccat 自动上报, +> 因此大多数情况下你不需要使用这个 API, +> 除非你想覆盖我们的心跳信息。 +> +> 当你这么做时,不要忘记禁用我们的内置心跳信息。 diff --git a/lib/c/docs/zh-CN.md b/lib/c/docs/zh-CN.md new file mode 100644 index 0000000000000000000000000000000000000000..c4a7d10a72ac77a5807610bc4e6049f109641f89 --- /dev/null +++ b/lib/c/docs/zh-CN.md @@ -0,0 +1,65 @@ +# Cat Client for C + +`ccat` 同时支持 linux (glibc 和 musl-libc) 和 osx 两个平台。 + +下述列出的操作系统是经过测试可用的: + +* OSX (>=10.13) +* Alpine linux +* CentOS 6 +* CentOS 7 +* Ubuntu 14.04 LTS +* Ubuntu 16.04 LTS +* Ubuntu 18.04 LTS + +我们也提供了 c++ 版本的客户端,参考 [cppcat](../../cpp/docs/zh-CN.md) + +## 编译 + +你需要安装一个支持 C99 的 C 编译器。 + +你同时还需要安装 `cmake` 和 `make`,这是我们用来构建动态或静态链接库以及可执行文件的工具。 + +当你准备好你的环境之后,编译安装 `ccat` 就很容易了。 + +(在项目根目录下,就包含 CMakeList.txt 的那个) + +``` +mkdir -p cmake +cd cmake +make -j 4 +``` + +### 安装 + +下面的命令将会把 `libcatclient.so`(或者在 osx 下是 .dylib)安装到你的 `LD_LIBRARY_PATH` 目录下,大多数情况下是 `/usr/local/lib`。 + +``` +make install +``` + +然后你就可以像使用一个内置动态库一样使用 `ccat` 了。 + +``` +gcc -lcatclient x.c +``` + +## 初始化 + +一些[准备工作](../_/preparations.zh-CN.md)需要在初始化 `ccat` 之前完成。 + +当你完成这些准备工作后,在你的 c 代码中初始化 `ccat` 就很简单了。 + +```c +#include + +catClientInit("appkey"); +``` + +> appkey 只能包含英文字母 (a-z, A-Z)、数字 (0-9)、下划线 (\_) 和中划线 (-) + +注意,**采样**,内置**心跳**,**二进制**序列化在默认情况下是开启的,你可能会想要禁用他们。我们同时提供了一个 API 可以使你自定义启动参数,请参考 [API 文档](./docs/api.zh-CN.md) + +## Documentation + +[API 文档](./api.zh-CN.md) diff --git a/lib/c/include/ccat/client.h b/lib/c/include/client.h similarity index 99% rename from lib/c/include/ccat/client.h rename to lib/c/include/client.h index 948df0100936c3e7a6c186a8ba410bb713bf0694..5cae6d6afd32e1cf51cadfa697ad1cdc8d175cae 100644 --- a/lib/c/include/ccat/client.h +++ b/lib/c/include/client.h @@ -140,6 +140,8 @@ CAT_CLIENT_EXPORT int catClientInitWithConfig(const char *appkey, CatClientConfi CAT_CLIENT_EXPORT int catClientDestroy(); +CAT_CLIENT_EXPORT const char* catVersion(); + CAT_CLIENT_EXPORT int isCatEnabled(); /** diff --git a/lib/c/include/ccat/helper.h b/lib/c/include/helper.h similarity index 90% rename from lib/c/include/ccat/helper.h rename to lib/c/include/helper.h index e92f7e978f445ca5fc6d246f25c18aa6f6bf5e56..39ee3760545b75c8e965d82eb9ddae1df03ef80f 100644 --- a/lib/c/include/ccat/helper.h +++ b/lib/c/include/helper.h @@ -2,6 +2,10 @@ // Created by Terence on 2018/8/23. // +/** + * NOTE it is only a proposal, interfaces defined here has not been implemented. + */ + #ifndef CCAT_HELPER_H #define CCAT_HELPER_H diff --git a/lib/c/benchmark/cat_client_test.c b/lib/c/scripts/cat_client_test.c similarity index 84% rename from lib/c/benchmark/cat_client_test.c rename to lib/c/scripts/cat_client_test.c index 342b04373896ecf2aee77a1558976b855e812700..7c3962f2d50bde9a9fc54ee4f9adc4f53fe714d6 100644 --- a/lib/c/benchmark/cat_client_test.c +++ b/lib/c/scripts/cat_client_test.c @@ -1,6 +1,6 @@ #include -#include "ccat/client.h" +#include "client.h" #include "lib/cat_time_util.h" @@ -31,8 +31,8 @@ void test() { CatTransaction *t3 = newTransaction("foo", "bar3"); t3->setStatus(t3, CAT_SUCCESS); /** - * Upon you complete the transaction. - * The transaction will be popped from the stack and the duration will be set. + * Once you complete the transaction. + * The transaction will be popped from the stack and the duration will be calculated. */ t3->complete(t3); @@ -44,7 +44,7 @@ void test() { CatTransaction *t4 = newTransactionWithDuration("foo", "bar4-with-duration", 1000); snprintf(buf, 9, "bar%d", k); /** - * Log a event, append the event to the children of current transaction. + * Log an event, it will be added to the children list of the current transaction. */ logEvent("foo", buf, CAT_SUCCESS, NULL); t4->setStatus(t4, CAT_SUCCESS); @@ -53,8 +53,9 @@ void test() { t1->setStatus(t1, CAT_SUCCESS); /** - * When the last element of stack is popped. - * A message tree will be generated and sent to server. + * Complete the transaction and poped it from the stack. + * When the last element of the stack is popped. + * The message tree will be encoded and sent to server. */ t1->complete(t1); } @@ -91,9 +92,9 @@ int main(int argc, char **argv) { CatClientConfig config = DEFAULT_CCAT_CONFIG; config.enableHeartbeat = 0; config.enableDebugLog = 1; - catClientInitWithConfig("nodecat", &config); - test2(); - Sleep(5000); + catClientInitWithConfig("ccat", &config); + test(); + Sleep(3000); catClientDestroy(); return 0; } diff --git a/lib/c/benchmark/cat_mpsc_test.c b/lib/c/scripts/cat_mpsc_test.c similarity index 100% rename from lib/c/benchmark/cat_mpsc_test.c rename to lib/c/scripts/cat_mpsc_test.c diff --git a/lib/c/src/ccat/client.c b/lib/c/src/ccat/client.c index 4b619186b30a85879308355d5663485ef5479064..4385b68f08d289b6b8cca9761961e10dda26bec4 100644 --- a/lib/c/src/ccat/client.c +++ b/lib/c/src/ccat/client.c @@ -1,7 +1,8 @@ -#include "ccat/client.h" +#include "client.h" #include #include +#include #include "client_config.h" #include "context.h" @@ -95,6 +96,10 @@ int catClientDestroy() { return 1; } +const char* catVersion() { + return CCAT_VERSION; +} + void logError(const char *msg, const char *errStr) { getContextMessageTree()->canDiscard = 0; logEvent("Exception", msg, CAT_ERROR, errStr); diff --git a/lib/c/src/ccat/client_config.h b/lib/c/src/ccat/client_config.h index 9279de2ce59f89e834729ca1f2d2c02ffeb3ae95..92d66f3ad3f3a6dcb271a10b20b805d5ca92142b 100644 --- a/lib/c/src/ccat/client_config.h +++ b/lib/c/src/ccat/client_config.h @@ -1,7 +1,7 @@ #ifndef CAT_CLIENT_C_CLIENT_CONFIG_H #define CAT_CLIENT_C_CLIENT_CONFIG_H -#include "ccat/client.h" +#include "client.h" #include "lib/cat_sds.h" diff --git a/lib/c/src/ccat/message.h b/lib/c/src/ccat/message.h index d760364e2f951ab957b9a988bdcad0e918743d7c..9c7d17cc9bc41a386ac4339f633d57194e6af177 100644 --- a/lib/c/src/ccat/message.h +++ b/lib/c/src/ccat/message.h @@ -8,7 +8,7 @@ #include #include -#include "ccat/client.h" +#include "client.h" #include "ccat/message_helper.h" #include "lib/cat_sds.h" diff --git a/lib/c/src/ccat/message_aggregator_event.h b/lib/c/src/ccat/message_aggregator_event.h index 489b0a83879637fdeb72b44b58239ba99ba58ab2..e52296c1c0c2722f76077cd70b33132098c37161 100644 --- a/lib/c/src/ccat/message_aggregator_event.h +++ b/lib/c/src/ccat/message_aggregator_event.h @@ -5,7 +5,7 @@ #ifndef CCAT_MESSAGE_AGGREGATOR_EVENT_H #define CCAT_MESSAGE_AGGREGATOR_EVENT_H -#include +#include void addEventToAggregator(CatEvent * pEvent); diff --git a/lib/c/src/ccat/message_aggregator_trans.h b/lib/c/src/ccat/message_aggregator_trans.h index 5a8593c2df44b34881498d58a336613b8fd4888d..8199b6e32c2c3a02c9baf7bc54dea125aec78e86 100644 --- a/lib/c/src/ccat/message_aggregator_trans.h +++ b/lib/c/src/ccat/message_aggregator_trans.h @@ -5,7 +5,7 @@ #ifndef CCAT_MESSAGE_AGGREGATOR_TRANS_H #define CCAT_MESSAGE_AGGREGATOR_TRANS_H -#include +#include void addTransToAggregator(CatTransaction *pEvent); diff --git a/lib/c/src/ccat/message_tree.h b/lib/c/src/ccat/message_tree.h index 9c99dcbc5012f022e4fe3572087c7342759d32ea..cf448a37e1d46974904ad0379622f4d9c1f4e365 100644 --- a/lib/c/src/ccat/message_tree.h +++ b/lib/c/src/ccat/message_tree.h @@ -1,7 +1,7 @@ #ifndef CAT_CLIENT_C_MESSAGE_TREE_H #define CAT_CLIENT_C_MESSAGE_TREE_H -#include "ccat/client.h" +#include "client.h" #include "lib/cat_sds.h" diff --git a/lib/c/src/ccat/monitor.c b/lib/c/src/ccat/monitor.c index be6277b20c9de793aeb1459e85667066ab5354f0..c3aca8e4481c75bf5e8bd1ac8ce70931c52f9749 100644 --- a/lib/c/src/ccat/monitor.c +++ b/lib/c/src/ccat/monitor.c @@ -5,6 +5,7 @@ #include "ccat/message_manager.h" #include "ccat/monitor_collector.h" #include "ccat/server_connection_manager.h" +#include "ccat/version.h" #include "lib/cat_thread.h" #include "lib/cat_time_util.h" @@ -43,7 +44,7 @@ static PTHREAD catMonitorFun(PVOID para) { if (runCount % 60 == 1 && g_config.enableHeartbeat) { // Report ccat version. - logEvent("Cat_C_Client_Version", Cat_C_Client_Version, CAT_SUCCESS, NULL); + logEvent("Cat_C_Client_Version", CCAT_VERSION, CAT_SUCCESS, NULL); // Report vm / runtime version. (For other programming language which using ccat mixin to report heartbeat) if (strcmp(g_client_info.language, "C") != 0) { diff --git a/lib/c/src/ccat/monitor.h b/lib/c/src/ccat/monitor.h index bd76ce71101231793ed77ab13d8f9db6e4b1a730..1016b20ea1787574a2b32428f5d194e7633fcbd3 100644 --- a/lib/c/src/ccat/monitor.h +++ b/lib/c/src/ccat/monitor.h @@ -1,8 +1,6 @@ #ifndef CAT_CLIENT_C_MONITOR_H #define CAT_CLIENT_C_MONITOR_H -#define Cat_C_Client_Version "2.1.0" - #include "lib/cat_sds.h" typedef struct { diff --git a/lib/c/src/ccat/monitor_collector.c b/lib/c/src/ccat/monitor_collector.c index 8db4d6b1e88a778f77eea25b48d2399cc56ad59d..6bd64c693abeecf30a46b21d217c4a977fb1cfdf 100644 --- a/lib/c/src/ccat/monitor_collector.c +++ b/lib/c/src/ccat/monitor_collector.c @@ -3,7 +3,7 @@ // #include "monitor_collector.h" -#include "ccat/client.h" +#include "client.h" void inline add_detail(ezxml_t ext, const char *key, const char *val, int *index) { diff --git a/lib/c/src/cppcat/heartbeat.cpp b/lib/c/src/cppcat/heartbeat.cpp deleted file mode 100644 index 9a9d62c7b16c061eb32c8612c14108263ddaf77c..0000000000000000000000000000000000000000 --- a/lib/c/src/cppcat/heartbeat.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// -// Created by Terence on 2018/8/2. -// - diff --git a/lib/c/src/lib/cat_anet.c b/lib/c/src/lib/cat_anet.c index 652013a44663a32cb97e012ae465efe35c33bca3..fb0704116d67b13b0134d2b893b163d5164908b2 100644 --- a/lib/c/src/lib/cat_anet.c +++ b/lib/c/src/lib/cat_anet.c @@ -368,7 +368,7 @@ static int anetCreateSocket(char *err, int domain) { return ANET_ERR; } - /* Make sure connection-intensive things like the redis benchmark + /* Make sure connection-intensive things like the redis scripts * will be able to close/open sockets a zillion of times */ if (anetSetReuseAddr(err, s) == ANET_ERR) { close(s); diff --git a/lib/c/src/version.h.in b/lib/c/src/version.h.in new file mode 100644 index 0000000000000000000000000000000000000000..d9b834b40730ce6248a18acdfe8cfa6bd412ef49 --- /dev/null +++ b/lib/c/src/version.h.in @@ -0,0 +1,6 @@ +#ifndef _CCAT_VERSION_H_ +#define _CCAT_VERSION_H_ 1 + +#define CCAT_VERSION "@CCAT_VERSION@" + +#endif // _CCAT_VERSION_H_ \ No newline at end of file diff --git a/lib/cpp/CMakeLists.txt b/lib/cpp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..785ac996976006343825517627b6adde32375c07 --- /dev/null +++ b/lib/cpp/CMakeLists.txt @@ -0,0 +1,82 @@ +cmake_minimum_required(VERSION 2.4) + +project(cppcat) + +set(CPPCAT_VERSION 3.0.0) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/ccat/version.h) + +message(STATUS "Current OS: " ${CMAKE_SYSTEM_NAME}) + +macro(use_cxx11) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + if(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + elseif(COMPILER_SUPPORTS_CXX0X) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + else() + message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") + endif() +endmacro(use_cxx11) + +macro(add_definitions_c def) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${def}") +endmacro() + +macro(add_cxx_flags def) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${def}") +endmacro() + +use_cxx11() + +# set(BUILD_TEST 1) +# set(BUILD_SCRIPT 1) + +add_definitions("-Wno-format-security") +# add_definitions_c("-Wno-incompatible-pointer-types") + +if (APPLE) + add_definitions_c("-Wno-ignored-qualifiers") +else() + add_definitions_c("-Wno-discarded-qualifiers") +endif() + +include_directories(include) +include_directories(src) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/include) +include_directories(../c/include) + +aux_source_directory(src/lib SOURCE_FILES) +aux_source_directory(src/ccat SOURCE_FILES) +aux_source_directory(src/cppcat SOURCE_FILES) + +if (APPLE) +elseif(UNIX) + link_libraries(m) + link_libraries(rt) + link_libraries(pthread) +endif() + +set(HEADER_FILES include/client.hpp) +add_library(catclient SHARED ${HEADER_FILES} ${SOURCE_FILES}) +install(TARGETS catclient DESTINATION lib) + +if (BUILD_TEST) + link_libraries(gtest) + file( + COPY + tests/client.xml + tests/client.json + DESTINATION cat + ) + + aux_source_directory(tests/unit UNITTEST_FILES) + add_executable(unittest tests/unittest.cpp ${UNITTEST_FILES} ${SOURCE_FILES}) +endif() + +if (BUILD_SCRIPT) + add_executable(test_client ${SOURCE_FILES} scripts/cat_client_test.cpp) + add_executable(test_mpsc ${SOURCE_FILES} scripts/cat_mpsc_test.cpp) +endif() diff --git a/lib/cpp/README.md b/lib/cpp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..fd78478b73e342b0db118585af76070af001458f --- /dev/null +++ b/lib/cpp/README.md @@ -0,0 +1,72 @@ +# Cat Client for C++ + +[中文文档](./docs/zh-CN.md) + +The cat client can be compiled and used both on Linux (both glibc and musl-libc) and OSX. + +The following Operating Systems are tested: + +* OSX (>=10.13) +* Alpine linux +* CentOS 6 +* CentOS 7 +* Ubuntu 14.04 LTS +* Ubuntu 16.04 LTS +* Ubuntu 18.04 LTS + +## Compilation + +You need to have a C++ compiler (supporting C++11) installed. + +You also need to have `cmake` and `make` installed, which we use for building static or dynamic libraries and executable binary files. + +Once you have your environment ready, it's easy to build and install `cppcat`. + +(In the project root dir, which contains CMakeLists.txt) + +``` +mkdir -p cmake +cd cmake +make -j 4 +``` + +Build test cases if you want. + +Since we using [googletest](https://github.com/google/googletest) as the test framework, it has to be installed first. + +``` +make -j 4 -DBUILD_TEST=1 +``` + +### Installation + +The following command will install libcatclient.so (or .dylib in osx) to your `LD_LIBRARY_PATH`, which is `/usr/local/lib` in most cases. + +``` +make install +``` + +Now it can be used as a built-in shared library. +``` +g++ -lcatclient x.cpp +``` + +## Initialization + +Some [preparations](../_/preparations.md) needs to be done before initialize `cppcat`. + +With all the preparations have been done, it's easy to initialize `cppcat` in your c++ codes. + +```cpp +#include + +cat::init("appkey"); +``` + +> Only English characters (a-z, A-Z), numbers (0-9), underscore (\_) and dash (-) is allowed in appkey. + +Note that `sampling`, built-in `heartbeat` and `binary` encoder is enabled by default, which you may want to disable it. We also offered an API to customize your initialization, please refer to our [apidoc](./docs/api.md). + +## Documentation + +[API doc](./docs/api.md) diff --git a/lib/c/doc/api-cpp.md b/lib/cpp/docs/api.md similarity index 59% rename from lib/c/doc/api-cpp.md rename to lib/cpp/docs/api.md index 9caaeda329fbd9129cf130aa1e8ebcce462964d3..47faf50a4a015c46ac1f881743a3652a43c5cee1 100644 --- a/lib/c/doc/api-cpp.md +++ b/lib/cpp/docs/api.md @@ -64,13 +64,13 @@ int main() { ## API list -All the cpp cat client apis are in the `cat` namespace. +All the `cppcat` apis are in the `cat` namespace. ### Common apis #### cat::init -initialize cat client by default configs. +initialize `cppcat` by default configs. ```c void init(const string& domain); @@ -78,11 +78,11 @@ void init(const string& domain); Following config is used by default. -* `binary` encoder -* built-in `heartbeat` enabled -* `sampling` enabled -* `multi process mode` disabled -* `debug log` disabled +* `encoderType` = CAT_ENCODER_BINARY. +* `enableHeartbeat` is true. +* `enableSampling` is true. +* `enableMultiprocessing` is false. +* `enableDebugLog` is false. You can also customize config. (Like to use text encoder instead of binary encoder) @@ -105,37 +105,17 @@ void destroy(); ### Transaction -A message has the following properties. - -* `type` usually means a category of event, for example, `SQL`, `RPC`, `HTTP-GET` may be recommended types. -* `name` usually means a specified action. - * When the type is `SQL`, the name may be `select from user where user_id = ` - * When the type is `RPC`, the name may be `queryOrderByUserId` - * when the type is `HTTP-GET`, the name may be `/api/v2/order/` -* while `status` is not equal to `CAT_SUCCESS` (which is "0"), the message will be treated as a `problem`, and can be recorded in our problem report. -* `data` is used for storing some useful infomation. - * When the type is `SQL`, the data may like `user_id=194432&token=a94238` - * When the type is `RPC`, the data may like `order_id=11314152` -* `timestamp` represents the time when the message has been created, which will be shown on our log view page. - -Though transaction is inherited from a message, it also has the foregoing properties. - -And there are some transaction-only properties: - -* `duration` means the time costs of a transaction. -* `durationStart` means the start time of a transaction. - -> Note the `durationStart` may not as same as `timestamp`, they have different meanings. +You can find more information about the properties of a transaction in [Message properties](../../README.md#message-properties) #### cat::Transaction -Transaction can be easily created +Transaction can be easily created. ```cpp cat::Transaction t("type", "name"); ``` -Due to the properties of a transaction were mostly private, we offered a list of APIs to help you update them. +Due to the properties of a transaction were mostly private, we offered a list of APIs to help you to edit them. * AddData * SetStatus @@ -161,12 +141,8 @@ t.Complete(); There is something you may want to know: 1. Duration of a transaction will be calculated while you complete the transaction. (currentTimestamp - durationStart) -1. Although `durationStart` is as same as `timestamp`, you can overwrite it. -2. `durationStart` and `timestamp` are different, the first one represents the start time of a transaction, and the second one only means created time of a message. (Transaction is one kind of message) -1. `durationStart` won't work when you specified the `duration`. -2. You can call `addData` several times, the added data will be connected by `&`. - -> Don't forget to complete the transaction after you new it! Or you will get corrupted message trees and memory leaks! +2. It's meaningless to specify `duration` and `durationStart` in the same transaction, although we do it in the example :) +3. Never forget to complete the transaction! Or you will get corrupted message trees and memory leaks! ### Event @@ -200,4 +176,4 @@ For example, if you called this API 3 times in one second (can be in different t void logMetricForDuration(const string& key, unsigned long ms); ``` -Like `logMetricForCount`, the values reported in one second will be aggregated, the only difference is we reported `averaged` value instead of `summarized` value. +Like `logMetricForCount`, the metrics reported in the same second will be aggregated, the only difference is `averaged` value is used instead of `summarized` value. diff --git a/lib/cpp/docs/api.zh-CN.md b/lib/cpp/docs/api.zh-CN.md new file mode 100644 index 0000000000000000000000000000000000000000..39d551812991b493f154601d104a1a67a40d5a62 --- /dev/null +++ b/lib/cpp/docs/api.zh-CN.md @@ -0,0 +1,178 @@ +# cat c++ client + +## Quickstart + +```cpp +#include +#include + +#include "cppcat/client.h" + +using namespace std; + +unsigned long long GetTime64() { + return static_cast(std::chrono::system_clock::now().time_since_epoch().count() / 1000); +} + +void transaction() { + cat::Transaction t("foo", "bar"); + t.AddData("foo", "1"); + t.AddData("bar", "2"); + t.AddData("foo is a bar"); + t.SetDurationStart(GetTime64() - 1000); + t.SetTimestamp(GetTime64() - 1000); + t.SetDurationInMillis(150); + t.SetStatus(cat::FAIL); + t.Complete(); +} + +void event() { + cat::Event e("foo", "bar"); + e.AddData("foo", "1"); + e.AddData("bar", "2"); + e.AddData("foo is a bar"); + e.SetStatus(cat::SUCCESS); + e.Complete(); + + cat::logEvent("foo", "bar1"); + cat::logEvent("foo", "bar2", "failed"); + cat::logEvent("foo", "bar3", "failed", "k=v"); +} + +void metric() { + cat::logMetricForCount("count"); + cat::logMetricForCount("count", 3); + cat::logMetricForDuration("duration", 100); +} + +int main() { + cat::Config c = cat::Config(); + c.enableDebugLog = true; + c.encoderType = cat::ENCODER_TEXT; + cat::init("cppcat", c); + + for (int i = 0; i < 100; i++) { + transaction(); + event(); + metric(); + usleep(10000); + } + usleep(1000000); + cat::destroy(); +} +``` + +## API list + +所有的 `cppcat` API 都在 `cat` 这个命名空间下。 + +### Common apis + +#### cat::init + +使用默认配置初始化 `cppcat` + +```c +void init(const string& domain); +``` + +默认采用如下配置: + +* `encoderType` = CAT_ENCODER_BINARY. +* `enableHeartbeat` is true. +* `enableSampling` is true. +* `enableMultiprocessing` is false. +* `enableDebugLog` is false. + +你也可以自定义配置(比如使用文本序列化取代二进制序列化) + +```cpp +cat::Config c = cat::Config(); +c.enableDebugLog = true; +c.encoderType = cat::ENCODER_TEXT; +cat::init("cppcat", c); +``` + +#### cat::destory + +禁用 `cppcat`,退出**sender**,**monitor**,和**aggregator** 线程。 + +释放所有已申请的资源。 + +```cpp +void destroy(); +``` + +### Transaction + +你可以在[消息属性](../../_/zh-CN.md#消息属性)中了解 transaction 的属性信息。 + +#### cat::Transaction + +Transaction 可以很方便的被创建。 + +```cpp +cat::Transaction t("type", "name"); +``` + +由于大多数的 transaction 属性都是私有的,我们提供了一系列 API 可供你修改它们。 + +* AddData +* SetStatus +* SetDurationStart +* SetDurationInMillis +* SetTimestamp +* Complete + +他们用起来很简单,就像这样: + +```cpp +cat::Transaction t("foo", "bar"); +t.AddData("foo", "1"); +t.AddData("bar", "2"); +t.AddData("foo is a bar"); +t.SetDurationStart(GetTime64() - 1000); +t.SetTimestamp(GetTime64() - 1000); +t.SetDurationInMillis(150); +t.SetStatus(cat::FAIL); +t.Complete(); +``` + +这里有一些你可能想要知道的: + +1. 你可以调用 `addData` 和 `addKV` 多次,他们会被 `&` 连接起来。 +2. 同时指定 `duration` 和 `durationStart` 是没有意义的,尽管我们在样例中这样做了。 +3. 不要忘记完成 transaction!否则你会得到一个毁坏的消息树以及内存泄漏! + +### Event + +Event 是一个简化版的 Transaction,没有耗时。 + +#### cat::logEvent + +记录一个 Event。 + +```cpp +void logEvent(const string& type, const string& name, const string& status = SUCCESS, const string& data = ""); +``` + +### Metric + +#### logMetricForCount + +```cpp +void logMetricForCount(const string& key, unsigned int count = 1); +``` + +指标会每秒上报一次。 + +举例来说,如果你在同一秒内调用这个 API 三次(可以在不同的线程,我们使用线程安全的 map 来缓存指标的值),只有聚合后的值(求和)会被上报到服务端。 + + +#### logMetricForDuration + +```cpp +void logMetricForDuration(const string& key, unsigned long ms); +``` + +就像 `logMetricForCount` 一样,同一秒上报的指标会被聚合,唯一的区别是这里使用平均值取代求和。 diff --git a/lib/cpp/docs/zh-CN.md b/lib/cpp/docs/zh-CN.md new file mode 100644 index 0000000000000000000000000000000000000000..5b1d504cb8c0df9882e309bf446b9642d9ac5e33 --- /dev/null +++ b/lib/cpp/docs/zh-CN.md @@ -0,0 +1,67 @@ +# Cat Client for C++ + +`cppcat` 同时支持 linux (glibc 和 musl-libc) 和 osx 两个平台。 + +下述列出的操作系统是经过测试可用的: + +* OSX (>=10.13) +* Alpine linux +* CentOS 6 +* CentOS 7 +* Ubuntu 14.04 LTS +* Ubuntu 16.04 LTS +* Ubuntu 18.04 LTS + +## 编译 + +你需要安装一个支持 C++11 的 C++ 编译器。 + +你同时还需要安装 `cmake` 和 `make`,这是我们用来构建动态或静态链接库以及可执行文件的工具。 + +当你准备好你的环境之后,编译安装 `cppcat` 就很容易了。 + +(在项目根目录下,就包含 CMakeList.txt 的那个) + +``` +mkdir -p cmake +cd cmake +make -j 4 +``` + +你也可以尝试构建我们的测试用例。 + +由于我们使用了 [googletest](https://github.com/google/googletest) 作为我们的测试框架,你需要先安装它。 + +### 安装 + +下面的命令将会把 `libcatclient.so`(或者在 osx 下是 .dylib)安装到你的 `LD_LIBRARY_PATH` 目录下,大多数情况下是 `/usr/local/lib`。 + +``` +make install +``` + +然后你就可以像使用一个内置动态库一样使用 `cppcat` 了。 + +``` +gcc -lcatclient x.cpp +``` + +## 初始化 + +一些[准备工作](../../_/preparations.zh-CN.md)需要在初始化 `cppcat` 之前完成。 + +当你完成这些准备工作后,在你的 c++ 代码中初始化 `cppcat` 就很简单了。 + +```c +#include + +cat::init("appkey"); +``` + +> appkey 只能包含英文字母 (a-z, A-Z)、数字 (0-9)、下划线 (\_) 和中划线 (-) + +注意,**采样**,内置**心跳**,**二进制**序列化在默认情况下是开启的,你可能会想要禁用他们。我们同时提供了一个 API 可以使你自定义启动参数,请参考 [API 文档](./api.zh-CN.md) + +## Documentation + +[API 文档](./api.zh-CN.md) diff --git a/lib/c/include/cppcat/client.h b/lib/cpp/include/client.hpp similarity index 95% rename from lib/c/include/cppcat/client.h rename to lib/cpp/include/client.hpp index fe8bba694cdecd35b40dd6c386d6f37f8c601b2a..c3f1784bba74e94da7db7a97e81a47444dab5da9 100644 --- a/lib/c/include/cppcat/client.h +++ b/lib/cpp/include/client.hpp @@ -2,8 +2,8 @@ // Created by Terence on 2018/8/2. // -#ifndef CCAT_CLIENT_H -#define CCAT_CLIENT_H +#ifndef CPPCAT_CLIENT_H +#define CPPCAT_CLIENT_H #include @@ -83,6 +83,8 @@ namespace cat { void init(const string& domain, const Config& config); + string version(); + void destroy(); void logEvent(const string& type, const string& name, const string& status = SUCCESS, const string& data = ""); @@ -92,4 +94,4 @@ namespace cat { void logMetricForDuration(const string& key, unsigned long ms); }; -#endif //CCAT_CLIENT_H +#endif // CPPCAT_CLIENT_H diff --git a/lib/c/benchmark/cat_client_test.cpp b/lib/cpp/scripts/cat_client_test.cpp similarity index 91% rename from lib/c/benchmark/cat_client_test.cpp rename to lib/cpp/scripts/cat_client_test.cpp index b9dc698bb4bc0efcb0f902f9b34af5a989d020df..02e5842fb809ba29182f1c83fbcbe1fd8a608f79 100644 --- a/lib/c/benchmark/cat_client_test.cpp +++ b/lib/cpp/scripts/cat_client_test.cpp @@ -5,7 +5,7 @@ #include #include -#include "cppcat/client.h" +#include "client.hpp" using namespace std; @@ -45,9 +45,10 @@ void metric() { } int main() { + cout << "cppcat version: " << cat::version() << endl; + cat::Config c = cat::Config(); - c.enableDebugLog = true; - c.encoderType = cat::ENCODER_TEXT; + c.encoderType = cat::ENCODER_BINARY; cat::init("cppcat", c); for (int i = 0; i < 100; i++) { diff --git a/lib/c/benchmark/cat_mpsc_test.cpp b/lib/cpp/scripts/cat_mpsc_test.cpp similarity index 100% rename from lib/c/benchmark/cat_mpsc_test.cpp rename to lib/cpp/scripts/cat_mpsc_test.cpp diff --git a/lib/cpp/src/ccat b/lib/cpp/src/ccat new file mode 120000 index 0000000000000000000000000000000000000000..750a184a10b330f162be866dad54f0d6db8499c8 --- /dev/null +++ b/lib/cpp/src/ccat @@ -0,0 +1 @@ +../../c/src/ccat \ No newline at end of file diff --git a/lib/c/src/cppcat/client.cpp b/lib/cpp/src/cppcat/client.cpp similarity index 83% rename from lib/c/src/cppcat/client.cpp rename to lib/cpp/src/cppcat/client.cpp index 003fc3a989e3099736ef983ea6c61f22786ef2cb..766a7c275519ad5a47cec10506026be8fa747f99 100644 --- a/lib/c/src/cppcat/client.cpp +++ b/lib/cpp/src/cppcat/client.cpp @@ -2,9 +2,10 @@ // Created by Terence on 2018/8/2. // -#include "cppcat/client.h" +#include "client.hpp" -#include +#include +#include using namespace std; @@ -23,6 +24,10 @@ namespace cat { catClientInitWithConfig(domain.c_str(), &conf); } + string version() { + return string(CPPCAT_VERSION); + } + void destroy() { catClientDestroy(); } diff --git a/lib/c/src/cppcat/event.cpp b/lib/cpp/src/cppcat/event.cpp similarity index 95% rename from lib/c/src/cppcat/event.cpp rename to lib/cpp/src/cppcat/event.cpp index df0e50fbae07658cfc0946948396366e80fcbf29..54252fb5501e61d6b08f56f3ecca75110ff35338 100644 --- a/lib/c/src/cppcat/event.cpp +++ b/lib/cpp/src/cppcat/event.cpp @@ -2,11 +2,9 @@ // Created by Terence on 2018/8/2. // -#include -#include -#include +#include "client.hpp" -#include "cppcat/client.h" +#include #define E(e) ((CatEvent*)(e)) diff --git a/lib/c/src/cppcat/metric.cpp b/lib/cpp/src/cppcat/metric.cpp similarity index 82% rename from lib/c/src/cppcat/metric.cpp rename to lib/cpp/src/cppcat/metric.cpp index e79bd7211371b83461e2f44da110c4a54db20600..753b2e1e7ba9ce7acc9ed96982704258f04e5ee8 100644 --- a/lib/c/src/cppcat/metric.cpp +++ b/lib/cpp/src/cppcat/metric.cpp @@ -2,10 +2,9 @@ // Created by Terence on 2018/8/2. // -#include -#include +#include "client.hpp" -#include "cppcat/client.h" +#include using namespace std; diff --git a/lib/c/src/cppcat/transaction.cpp b/lib/cpp/src/cppcat/transaction.cpp similarity index 95% rename from lib/c/src/cppcat/transaction.cpp rename to lib/cpp/src/cppcat/transaction.cpp index 957eca305f936b11b466cc1f2b7c7e2f94ad11d6..974ee457d82a87fa3b8498feb3a0994de8b30b11 100644 --- a/lib/c/src/cppcat/transaction.cpp +++ b/lib/cpp/src/cppcat/transaction.cpp @@ -2,11 +2,9 @@ // Created by Terence on 2018/8/2. // -#include -#include -#include +#include "client.hpp" -#include "cppcat/client.h" +#include using namespace std; diff --git a/lib/cpp/src/lib b/lib/cpp/src/lib new file mode 120000 index 0000000000000000000000000000000000000000..818089e5b9a59e63dbfa26b33d9f7f8949d67d36 --- /dev/null +++ b/lib/cpp/src/lib @@ -0,0 +1 @@ +../../c/src/lib \ No newline at end of file diff --git a/lib/cpp/src/version.h.in b/lib/cpp/src/version.h.in new file mode 100644 index 0000000000000000000000000000000000000000..046dd453f7abecd50879bb5a27b7cec5938ad0e4 --- /dev/null +++ b/lib/cpp/src/version.h.in @@ -0,0 +1,13 @@ +#ifndef _CCAT_VERSION_H_ +#define _CCAT_VERSION_H_ 1 + +#define CCAT_VERSION "@CPPCAT_VERSION@" + +#endif // _CCAT_VERSION_H_ + +#ifndef _CPPCAT_VERSION_H_ +#define _CPPCAT_VERSION_H_ 1 + +#define CPPCAT_VERSION "@CPPCAT_VERSION@" + +#endif // _CPPCAT_VERSION_H_ diff --git a/lib/c/tests/client.json b/lib/cpp/tests/client.json similarity index 100% rename from lib/c/tests/client.json rename to lib/cpp/tests/client.json diff --git a/lib/c/tests/client.xml b/lib/cpp/tests/client.xml similarity index 100% rename from lib/c/tests/client.xml rename to lib/cpp/tests/client.xml diff --git a/lib/c/tests/unit/test_client.cpp b/lib/cpp/tests/unit/test_client.cpp similarity index 100% rename from lib/c/tests/unit/test_client.cpp rename to lib/cpp/tests/unit/test_client.cpp diff --git a/lib/c/tests/unit/test_concurrent.cpp b/lib/cpp/tests/unit/test_concurrent.cpp similarity index 100% rename from lib/c/tests/unit/test_concurrent.cpp rename to lib/cpp/tests/unit/test_concurrent.cpp diff --git a/lib/c/tests/unit/test_json.cpp b/lib/cpp/tests/unit/test_json.cpp similarity index 100% rename from lib/c/tests/unit/test_json.cpp rename to lib/cpp/tests/unit/test_json.cpp diff --git a/lib/c/tests/unit/test_mpsc.cpp b/lib/cpp/tests/unit/test_mpsc.cpp similarity index 100% rename from lib/c/tests/unit/test_mpsc.cpp rename to lib/cpp/tests/unit/test_mpsc.cpp diff --git a/lib/c/tests/unit/test_sds.cpp b/lib/cpp/tests/unit/test_sds.cpp similarity index 100% rename from lib/c/tests/unit/test_sds.cpp rename to lib/cpp/tests/unit/test_sds.cpp diff --git a/lib/c/tests/unit/test_xml.cpp b/lib/cpp/tests/unit/test_xml.cpp similarity index 100% rename from lib/c/tests/unit/test_xml.cpp rename to lib/cpp/tests/unit/test_xml.cpp diff --git a/lib/c/tests/unittest.cpp b/lib/cpp/tests/unittest.cpp similarity index 100% rename from lib/c/tests/unittest.cpp rename to lib/cpp/tests/unittest.cpp