未验证 提交 cf0a696e 编写于 作者: Y youyong205 提交者: GitHub

Merge pull request #1315 from stdrickforce/nodejs

nodecat initialization
......@@ -38,7 +38,7 @@ endmacro()
use_c99()
use_cxx11()
set(BUILD_TYPE CPP)
# set(BUILD_TYPE CPP)
# set(BUILD_TEST 1)
# set(BUILD_SCRIPT 1)
......
......@@ -74,17 +74,8 @@ void test1() {
}
void test2() {
for (int i = 0; i < 1000; i++) {
CatTransaction *t1 = newTransaction("Test2", "A");
CatTransaction *t2 = newTransaction("Test2", "B");
t2->setStatus(t2, CAT_SUCCESS);
t2->addData(t2, "Body");
t2->complete(t2);
t1->setStatus(t1, CAT_SUCCESS);
t1->complete(t1);
}
CatTransaction *t1 = newTransaction("Test2", "A");
t1->complete(t1);
}
void test3() {
......@@ -100,9 +91,9 @@ int main(int argc, char **argv) {
CatClientConfig config = DEFAULT_CCAT_CONFIG;
config.enableHeartbeat = 0;
config.enableDebugLog = 1;
catClientInitWithConfig("ccat", &config);
test();
Sleep(3000);
catClientInitWithConfig("nodecat", &config);
test2();
Sleep(5000);
catClientDestroy();
return 0;
}
......@@ -106,7 +106,7 @@ int sendRootMessage(CatMessageTree *tree) {
if (!tree->canDiscard) {
return mqOffer(tree);
} else if (g_config.enableSampling && !hitSample()) {
} else if (g_config.enableSampling && hitSample()) {
return mqOffer(tree);
} else {
sendToAggregator(tree);
......
project(nodecat)
# This CMakeLists.txt is only for development.
# Please input following commands to build a runnable version:
# node-gyp configure
# node-gyp build
cmake_minimum_required(VERSION 2.4)
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)
use_cxx11()
if (APPLE)
set(CMAKE_MACOSX_RPATH 1)
endif()
include(NodeJS.cmake)
nodejs_init()
include_directories(include)
set(
SOURCE_FILES
src/nodecat.cc
)
set(
HEADER_FILES
include/client.h
src/debug.h
)
link_libraries(catclient)
add_nodejs_module(nodecat ${SOURCE_FILES} ${HEADER_FILES})
\ No newline at end of file
build:
node-gyp configure
node-gyp build
# Defaults for standard Node.js builds
set(NODEJS_DEFAULT_URL https://nodejs.org/download/release)
set(NODEJS_DEFAULT_VERSION installed)
set(NODEJS_VERSION_FALLBACK latest)
set(NODEJS_DEFAULT_NAME node)
set(NODEJS_DEFAULT_CHECKSUM SHASUMS256.txt)
set(NODEJS_DEFAULT_CHECKTYPE SHA256)
include(CMakeParseArguments)
# Find a path by walking upward from a base directory until the path is
# found. Sets the variable ${PATH} to False if the path can't
# be determined
function(find_path_parent NAME BASE PATH)
set(ROOT ${BASE})
set(${PATH} ${ROOT}/${NAME} PARENT_SCOPE)
set(DRIVE "^[A-Za-z]?:?/$")
while(NOT ROOT MATCHES ${DRIVE} AND NOT EXISTS ${ROOT}/${NAME})
get_filename_component(ROOT ${ROOT} DIRECTORY)
set(${PATH} ${ROOT}/${NAME} PARENT_SCOPE)
endwhile()
if(ROOT MATCHES ${DRIVE})
set(${PATH} False PARENT_SCOPE)
endif()
endfunction()
# Shortcut for finding standard node module locations
macro(find_nodejs_module NAME BASE PATH)
find_path_parent(node_modules/${NAME} ${BASE} ${PATH})
endmacro()
# Download with a bit of nice output (without spewing progress)
function(download_file URL)
message(STATUS "Downloading: ${URL}")
file(APPEND ${TEMP}/download.log "Downloading: ${URL}\n")
file(APPEND ${TEMP}/download.log "----------------------------------------\n")
file(DOWNLOAD
${URL}
${ARGN}
LOG DOWNLOAD_LOG
)
file(APPEND ${TEMP}/download.log ${DOWNLOAD_LOG})
file(APPEND ${TEMP}/download.log "----------------------------------------\n")
endfunction()
# Embedded win_delay_load_hook file so that this file can be copied
# into projects directly (recommended practice)
function(nodejs_generate_delayload_hook OUTPUT)
file(WRITE ${OUTPUT} "")
file(APPEND ${OUTPUT} "/*\n")
file(APPEND ${OUTPUT} " * When this file is linked to a DLL, it sets up a delay-load hook that\n")
file(APPEND ${OUTPUT} " * intervenes when the DLL is trying to load the main node binary\n")
file(APPEND ${OUTPUT} " * dynamically. Instead of trying to locate the .exe file it'll just return\n")
file(APPEND ${OUTPUT} " * a handle to the process image.\n")
file(APPEND ${OUTPUT} " *\n")
file(APPEND ${OUTPUT} " * This allows compiled addons to work when node.exe or iojs.exe is renamed.\n")
file(APPEND ${OUTPUT} " */\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} "#ifdef _MSC_VER\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} "#ifndef DELAYIMP_INSECURE_WRITABLE_HOOKS\n")
file(APPEND ${OUTPUT} "#define DELAYIMP_INSECURE_WRITABLE_HOOKS\n")
file(APPEND ${OUTPUT} "#endif\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} "#ifndef WIN32_LEAN_AND_MEAN\n")
file(APPEND ${OUTPUT} "#define WIN32_LEAN_AND_MEAN\n")
file(APPEND ${OUTPUT} "#endif\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} "#include <windows.h>\n")
file(APPEND ${OUTPUT} "#include <Shlwapi.h>\n")
file(APPEND ${OUTPUT} "#include <delayimp.h>\n")
file(APPEND ${OUTPUT} "#include <string.h>\n")
file(APPEND ${OUTPUT} "#include <tchar.h>\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} "static FARPROC WINAPI load_exe_hook(unsigned int event, DelayLoadInfo* info) {\n")
file(APPEND ${OUTPUT} " if (event != dliNotePreLoadLibrary) return NULL;\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} " if (_stricmp(info->szDll, \"iojs.exe\") != 0 &&\n")
file(APPEND ${OUTPUT} " _stricmp(info->szDll, \"node.exe\") != 0 &&\n")
file(APPEND ${OUTPUT} " _stricmp(info->szDll, \"node.dll\") != 0)\n")
file(APPEND ${OUTPUT} " return NULL;\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} " // Get a handle to the current process executable.\n")
file(APPEND ${OUTPUT} " HMODULE processModule = GetModuleHandle(NULL);\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} " // Get the path to the executable.\n")
file(APPEND ${OUTPUT} " TCHAR processPath[_MAX_PATH];\n")
file(APPEND ${OUTPUT} " GetModuleFileName(processModule, processPath, _MAX_PATH);\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} " // Get the name of the current executable.\n")
file(APPEND ${OUTPUT} " LPTSTR processName = PathFindFileName(processPath);\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} " // If the current process is node or iojs, then just return the proccess \n")
file(APPEND ${OUTPUT} " // module.\n")
file(APPEND ${OUTPUT} " if (_tcsicmp(processName, TEXT(\"node.exe\")) == 0 ||\n")
file(APPEND ${OUTPUT} " _tcsicmp(processName, TEXT(\"iojs.exe\")) == 0) {\n")
file(APPEND ${OUTPUT} " return (FARPROC) processModule;\n")
file(APPEND ${OUTPUT} " }\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} " // If it is another process, attempt to load 'node.dll' from the same \n")
file(APPEND ${OUTPUT} " // directory.\n")
file(APPEND ${OUTPUT} " PathRemoveFileSpec(processPath);\n")
file(APPEND ${OUTPUT} " PathAppend(processPath, TEXT(\"node.dll\"));\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} " HMODULE nodeDllModule = GetModuleHandle(processPath);\n")
file(APPEND ${OUTPUT} " if(nodeDllModule != NULL) {\n")
file(APPEND ${OUTPUT} " // This application has a node.dll in the same directory as the executable,\n")
file(APPEND ${OUTPUT} " // use that.\n")
file(APPEND ${OUTPUT} " return (FARPROC) nodeDllModule;\n")
file(APPEND ${OUTPUT} " }\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} " // Fallback to the current executable, which must statically link to \n")
file(APPEND ${OUTPUT} " // node.lib\n")
file(APPEND ${OUTPUT} " return (FARPROC) processModule;\n")
file(APPEND ${OUTPUT} "}\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} "PfnDliHook __pfnDliNotifyHook2 = load_exe_hook;\n")
file(APPEND ${OUTPUT} "\n")
file(APPEND ${OUTPUT} "#endif\n")
endfunction()
# Sets up a project to build Node.js native modules
# - Downloads required dependencies and unpacks them to the build directory.
# Internet access is required the first invocation but not after (
# provided the download is successful)
# - Sets up several variables for building against the downloaded
# dependencies
# - Guarded to prevent multiple executions, so a single project hierarchy
# will only call this once
function(nodejs_init)
# Prevents this function from executing more than once
if(NODEJS_INIT)
return()
endif()
# Regex patterns used by the init function for component extraction
set(HEADERS_MATCH "^([A-Fa-f0-9]+)[ \t]+([^-]+)-(headers|v?[0-9.]+)-(headers|v?[0-9.]+)([.]tar[.]gz)$")
set(LIB32_MATCH "(^[0-9A-Fa-f]+)[\t ]+(win-x86)?(/)?([^/]*)(.lib)$")
set(LIB64_MATCH "(^[0-9A-Fa-f]+)[\t ]+(win-)?(x64/)(.*)(.lib)$")
# Parse function arguments
cmake_parse_arguments(nodejs_init
"" "URL;NAME;VERSION;CHECKSUM;CHECKTYPE" "" ${ARGN}
)
# Allow the download URL to be overridden by command line argument
# NODEJS_URL
if(NODEJS_URL)
set(URL ${NODEJS_URL})
else()
# Use the argument if specified, falling back to the default
set(URL ${NODEJS_DEFAULT_URL})
if(nodejs_init_URL)
set(URL ${nodejs_init_URL})
endif()
endif()
# Allow name to be overridden by command line argument NODEJS_NAME
if(NODEJS_NAME)
set(NAME ${NODEJS_NAME})
else()
# Use the argument if specified, falling back to the default
set(NAME ${NODEJS_DEFAULT_NAME})
if(nodejs_init_NAME)
set(NAME ${nodejs_init_NAME})
endif()
endif()
# Allow the checksum file to be overridden by command line argument
# NODEJS_CHECKSUM
if(NODEJS_CHECKSUM)
set(CHECKSUM ${NODEJS_CHECKSUM})
else()
# Use the argument if specified, falling back to the default
set(CHECKSUM ${NODEJS_DEFAULT_CHECKSUM})
if(nodejs_init_CHECKSUM)
set(CHECKSUM ${nodejs_init_CHECKSUM})
endif()
endif()
# Allow the checksum type to be overriden by the command line argument
# NODEJS_CHECKTYPE
if(NODEJS_CHECKTYPE)
set(CHECKTYPE ${NODEJS_CHECKTYPE})
else()
# Use the argument if specified, falling back to the default
set(CHECKTYPE ${NODEJS_DEFAULT_CHECKTYPE})
if(nodejs_init_CHECKTYPE)
set(CHECKTYPE ${nodejs_init_CHECKTYPE})
endif()
endif()
# Allow the version to be overridden by the command line argument
# NODEJS_VERSION
if(NODEJS_VERSION)
set(VERSION ${NODEJS_VERSION})
else()
# Use the argument if specified, falling back to the default
set(VERSION ${NODEJS_DEFAULT_VERSION})
if(nodejs_init_VERSION)
set(VERSION ${nodejs_init_VERSION})
endif()
endif()
# "installed" is a special version that tries to use the currently
# installed version (determined by running node)
set(NODEJS_INSTALLED False CACHE BOOL "Node.js install status" FORCE)
if(VERSION STREQUAL "installed")
if(NOT NAME STREQUAL ${NODEJS_DEFAULT_NAME})
message(FATAL_ERROR
"'Installed' version identifier can only be used with"
"the core Node.js library"
)
endif()
# Fall back to the "latest" version if node isn't installed
set(VERSION ${NODEJS_VERSION_FALLBACK})
# This has all of the implications of why the binary is called nodejs in the first place
# https://lists.debian.org/debian-devel-announce/2012/07/msg00002.html
# However, with nvm/n, its nearly standard to have a proper 'node' binary now (since the
# apt-based one is so out of date), so for now just assume that this rare binary conflict
# case is the degenerate case. May need a more complicated solution later.
find_program(NODEJS_BINARY NAMES node nodejs)
if(NODEJS_BINARY)
execute_process(
COMMAND ${NODEJS_BINARY} --version
RESULT_VARIABLE INSTALLED_VERSION_RESULT
OUTPUT_VARIABLE INSTALLED_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(INSTALLED_VERSION_RESULT STREQUAL "0")
set(NODEJS_INSTALLED True CACHE BOOL
"Node.js install status" FORCE
)
set(VERSION ${INSTALLED_VERSION})
endif()
endif()
endif()
# Create a temporary download directory
set(TEMP ${CMAKE_CURRENT_BINARY_DIR}/temp)
if(EXISTS ${TEMP})
file(REMOVE_RECURSE ${TEMP})
endif()
file(MAKE_DIRECTORY ${TEMP})
# Unless the target is special version "latest", the parameters
# necessary to construct the root path are known
if(NOT VERSION STREQUAL "latest")
set(ROOT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}/${VERSION})
# Extract checksums from the existing checksum file
set(CHECKSUM_TARGET ${ROOT}/CHECKSUM)
endif()
# If we're trying to determine the version or we haven't saved the
# checksum file for this version, download it from the specified server
if(VERSION STREQUAL "latest" OR
(DEFINED ROOT AND NOT EXISTS ${ROOT}/CHECKSUM))
if(DEFINED ROOT)
# Clear away the old checksum in case the new one is different
# and/or it fails to download
file(REMOVE ${ROOT}/CHECKSUM)
endif()
file(REMOVE ${TEMP}/CHECKSUM)
download_file(
${URL}/${VERSION}/${CHECKSUM}
${TEMP}/CHECKSUM
INACTIVITY_TIMEOUT 10
STATUS CHECKSUM_STATUS
)
list(GET CHECKSUM_STATUS 0 CHECKSUM_STATUS)
if(CHECKSUM_STATUS GREATER 0)
file(REMOVE ${TEMP}/CHECKSUM)
message(FATAL_ERROR
"Unable to download checksum file"
)
endif()
# Extract checksums from the temporary file
set(CHECKSUM_TARGET ${TEMP}/CHECKSUM)
endif()
# Extract the version, name, header archive and archive checksum
# from the file. This first extract is what defines / specifies the
# actual version number and name.
file(STRINGS
${CHECKSUM_TARGET} HEADERS_CHECKSUM
REGEX ${HEADERS_MATCH}
LIMIT_COUNT 1
)
if(NOT HEADERS_CHECKSUM)
file(REMOVE ${TEMP}/CHECKSUM)
if(DEFINED ROOT)
file(REMOVE ${ROOT}/CHECKSUM)
endif()
message(FATAL_ERROR "Unable to extract header archive checksum")
endif()
string(REGEX MATCH ${HEADERS_MATCH} HEADERS_CHECKSUM ${HEADERS_CHECKSUM})
set(HEADERS_CHECKSUM ${CMAKE_MATCH_1})
set(NAME ${CMAKE_MATCH_2})
if(CMAKE_MATCH_3 STREQUAL "headers")
set(VERSION ${CMAKE_MATCH_4})
else()
set(VERSION ${CMAKE_MATCH_3})
endif()
set(HEADERS_ARCHIVE
${CMAKE_MATCH_2}-${CMAKE_MATCH_3}-${CMAKE_MATCH_4}${CMAKE_MATCH_5}
)
# Make sure that the root directory exists, and that the checksum
# file has been moved over from temp
if(DEFINED ROOT)
set(OLD_ROOT ${ROOT})
endif()
set(ROOT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}/${VERSION})
if(DEFINED OLD_ROOT AND NOT ROOT STREQUAL "${OLD_ROOT}")
file(REMOVE ${TEMP}/CHECKSUM)
file(REMOVE ${ROOT}/CHECKSUM)
message(FATAL_ERROR "Version/Name mismatch")
endif()
file(MAKE_DIRECTORY ${ROOT})
if(EXISTS ${TEMP}/CHECKSUM)
file(REMOVE ${ROOT}/CHECKSUM)
file(RENAME ${TEMP}/CHECKSUM ${ROOT}/CHECKSUM)
endif()
# Now that its fully resolved, report the name and version of Node.js being
# used
message(STATUS "NodeJS: Using ${NAME}, version ${VERSION}")
# Download the headers for the version being used
# Theoretically, these could be found by searching the installed
# system, but in practice, this can be error prone. They're provided
# on the download servers, so just use the ones there.
if(NOT EXISTS ${ROOT}/include)
file(REMOVE ${TEMP}/${HEADERS_ARCHIVE})
download_file(
${URL}/${VERSION}/${HEADERS_ARCHIVE}
${TEMP}/${HEADERS_ARCHIVE}
INACTIVITY_TIMEOUT 10
EXPECTED_HASH ${CHECKTYPE}=${HEADERS_CHECKSUM}
STATUS HEADERS_STATUS
)
list(GET HEADERS_STATUS 0 HEADERS_STATUS)
if(HEADER_STATUS GREATER 0)
file(REMOVE ${TEMP}/${HEADERS_ARCHIVE})
message(FATAL_ERROR "Unable to download Node.js headers")
endif()
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xfz ${TEMP}/${HEADERS_ARCHIVE}
WORKING_DIRECTORY ${TEMP}
)
# This adapts the header extraction to support a number of different
# header archive contents in addition to the one used by the
# default Node.js library
unset(NODEJS_HEADERS_PATH CACHE)
find_path(NODEJS_HEADERS_PATH
NAMES src include
PATHS
${TEMP}/${NAME}-${VERSION}-headers
${TEMP}/${NAME}-${VERSION}
${TEMP}/${NODEJS_DEFAULT_NAME}-${VERSION}-headers
${TEMP}/${NODEJS_DEFAULT_NAME}-${VERSION}
${TEMP}/${NODEJS_DEFAULT_NAME}
${TEMP}
NO_DEFAULT_PATH
)
if(NOT NODEJS_HEADERS_PATH)
message(FATAL_ERROR "Unable to find extracted headers folder")
endif()
# Move the headers into a standard location with a standard layout
file(REMOVE ${TEMP}/${HEADERS_ARCHIVE})
file(REMOVE_RECURSE ${ROOT}/include)
if(EXISTS ${NODEJS_HEADERS_PATH}/include/node)
file(RENAME ${NODEJS_HEADERS_PATH}/include/node ${ROOT}/include)
elseif(EXISTS ${NODEJS_HEADERS_PATH}/src)
file(MAKE_DIRECTORY ${ROOT}/include)
if(NOT EXISTS ${NODEJS_HEADERS_PATH}/src)
file(REMOVE_RECURSE ${ROOT}/include)
message(FATAL_ERROR "Unable to find core headers")
endif()
file(COPY ${NODEJS_HEADERS_PATH}/src/
DESTINATION ${ROOT}/include
)
if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/uv/include)
file(REMOVE_RECURSE ${ROOT}/include)
message(FATAL_ERROR "Unable to find libuv headers")
endif()
file(COPY ${NODEJS_HEADERS_PATH}/deps/uv/include/
DESTINATION ${ROOT}/include
)
if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/v8/include)
file(REMOVE_RECURSE ${ROOT}/include)
message(FATAL_ERROR "Unable to find v8 headers")
endif()
file(COPY ${NODEJS_HEADERS_PATH}/deps/v8/include/
DESTINATION ${ROOT}/include
)
if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/zlib)
file(REMOVE_RECURSE ${ROOT}/include)
message(FATAL_ERROR "Unable to find zlib headers")
endif()
file(COPY ${NODEJS_HEADERS_PATH}/deps/zlib/
DESTINATION ${ROOT}/include
)
endif()
file(REMOVE_RECURSE ${NODEJS_HEADERS_PATH})
unset(NODEJS_HEADERS_PATH CACHE)
endif()
# Only download the libraries on windows, since its the only place
# its necessary. Note, this requires rerunning CMake if moving
# a module from one platform to another (should happen automatically
# with most generators)
if(WIN32)
# Download the win32 library for linking
file(STRINGS
${ROOT}/CHECKSUM LIB32_CHECKSUM
LIMIT_COUNT 1
REGEX ${LIB32_MATCH}
)
if(NOT LIB32_CHECKSUM)
message(FATAL_ERROR "Unable to extract x86 library checksum")
endif()
string(REGEX MATCH ${LIB32_MATCH} LIB32_CHECKSUM ${LIB32_CHECKSUM})
set(LIB32_CHECKSUM ${CMAKE_MATCH_1})
set(LIB32_PATH win-x86)
set(LIB32_NAME ${CMAKE_MATCH_4}${CMAKE_MATCH_5})
set(LIB32_TARGET ${CMAKE_MATCH_2}${CMAKE_MATCH_3}${LIB32_NAME})
if(NOT EXISTS ${ROOT}/${LIB32_PATH})
file(REMOVE_RECURSE ${TEMP}/${LIB32_PATH})
download_file(
${URL}/${VERSION}/${LIB32_TARGET}
${TEMP}/${LIB32_PATH}/${LIB32_NAME}
INACTIVITY_TIMEOUT 10
EXPECTED_HASH ${CHECKTYPE}=${LIB32_CHECKSUM}
STATUS LIB32_STATUS
)
list(GET LIB32_STATUS 0 LIB32_STATUS)
if(LIB32_STATUS GREATER 0)
message(FATAL_ERROR
"Unable to download Node.js windows library (32-bit)"
)
endif()
file(REMOVE_RECURSE ${ROOT}/${LIB32_PATH})
file(MAKE_DIRECTORY ${ROOT}/${LIB32_PATH})
file(RENAME
${TEMP}/${LIB32_PATH}/${LIB32_NAME}
${ROOT}/${LIB32_PATH}/${LIB32_NAME}
)
file(REMOVE_RECURSE ${TEMP}/${LIB32_PATH})
endif()
# Download the win64 library for linking
file(STRINGS
${ROOT}/CHECKSUM LIB64_CHECKSUM
LIMIT_COUNT 1
REGEX ${LIB64_MATCH}
)
if(NOT LIB64_CHECKSUM)
message(FATAL_ERROR "Unable to extract x64 library checksum")
endif()
string(REGEX MATCH ${LIB64_MATCH} LIB64_CHECKSUM ${LIB64_CHECKSUM})
set(LIB64_CHECKSUM ${CMAKE_MATCH_1})
set(LIB64_PATH win-x64)
set(LIB64_NAME ${CMAKE_MATCH_4}${CMAKE_MATCH_5})
set(LIB64_TARGET ${CMAKE_MATCH_2}${CMAKE_MATCH_3}${LIB64_NAME})
if(NOT EXISTS ${ROOT}/${LIB64_PATH})
file(REMOVE_RECURSE ${TEMP}/${LIB64_PATH})
download_file(
${URL}/${VERSION}/${LIB64_TARGET}
${TEMP}/${LIB64_PATH}/${LIB64_NAME}
INACTIVITY_TIMEOUT 10
EXPECTED_HASH ${CHECKTYPE}=${LIB64_CHECKSUM}
STATUS LIB64_STATUS
)
list(GET LIB64_STATUS 0 LIB64_STATUS)
if(LIB64_STATUS GREATER 0)
message(FATAL_ERROR
"Unable to download Node.js windows library (64-bit)"
)
endif()
file(REMOVE_RECURSE ${ROOT}/${LIB64_PATH})
file(MAKE_DIRECTORY ${ROOT}/${LIB64_PATH})
file(RENAME
${TEMP}/${LIB64_PATH}/${LIB64_NAME}
${ROOT}/${LIB64_PATH}/${LIB64_NAME}
)
file(REMOVE_RECURSE ${TEMP}/${LIB64_PATH})
endif()
endif()
# The downloaded headers should always be set for inclusion
list(APPEND INCLUDE_DIRS ${ROOT}/include)
# Look for the NAN module, and add it to the includes
find_nodejs_module(
nan
${CMAKE_CURRENT_SOURCE_DIR}
NODEJS_NAN_DIR
)
if(NODEJS_NAN_DIR)
list(APPEND INCLUDE_DIRS ${NODEJS_NAN_DIR})
endif()
# Under windows, we need a bunch of libraries (due to the way
# dynamic linking works)
if(WIN32)
# Generate and use a delay load hook to allow the node binary
# name to be changed while still loading native modules
set(DELAY_LOAD_HOOK ${CMAKE_CURRENT_BINARY_DIR}/win_delay_load_hook.c)
nodejs_generate_delayload_hook(${DELAY_LOAD_HOOK})
set(SOURCES ${DELAY_LOAD_HOOK})
# Necessary flags to get delayload working correctly
list(APPEND LINK_FLAGS
"-IGNORE:4199"
"-DELAYLOAD:iojs.exe"
"-DELAYLOAD:node.exe"
"-DELAYLOAD:node.dll"
)
# Core system libraries used by node
list(APPEND LIBRARIES
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib
advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib
odbc32.lib Shlwapi.lib DelayImp.lib
)
# Also link to the node stub itself (downloaded above)
if(CMAKE_CL_64)
list(APPEND LIBRARIES ${ROOT}/${LIB64_PATH}/${LIB64_NAME})
else()
list(APPEND LIBRARIES ${ROOT}/${LIB32_PATH}/${LIB32_NAME})
endif()
else()
# Non-windows platforms should use these flags
list(APPEND DEFINITIONS _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64)
endif()
# Special handling for OSX / clang to allow undefined symbols
# Define is required by node on OSX
if(APPLE)
list(APPEND LINK_FLAGS "-undefined dynamic_lookup")
list(APPEND DEFINITIONS _DARWIN_USE_64_BIT_INODE=1)
endif()
# Export all settings for use as arguments in the rest of the build
set(NODEJS_VERSION ${VERSION} PARENT_SCOPE)
set(NODEJS_SOURCES ${SOURCES} PARENT_SCOPE)
set(NODEJS_INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE)
set(NODEJS_LIBRARIES ${LIBRARIES} PARENT_SCOPE)
set(NODEJS_LINK_FLAGS ${LINK_FLAGS} PARENT_SCOPE)
set(NODEJS_DEFINITIONS ${DEFINITIONS} PARENT_SCOPE)
# Prevents this function from executing more than once
set(NODEJS_INIT TRUE PARENT_SCOPE)
endfunction()
# Helper function for defining a node module
# After nodejs_init, all of the settings and dependencies necessary to do
# this yourself are defined, but this helps make sure everything is configured
# correctly. Feel free to use it as a model to do this by hand (or to
# tweak this configuration if you need something custom).
function(add_nodejs_module NAME)
# Validate name parameter (must be a valid C identifier)
string(MAKE_C_IDENTIFIER ${NAME} ${NAME}_SYMBOL_CHECK)
if(NOT "${NAME}" STREQUAL "${${NAME}_SYMBOL_CHECK}")
message(FATAL_ERROR
"Module name must be a valid C identifier. "
"Suggested alternative: '${${NAME}_SYMBOL_CHECK}'"
)
endif()
# Make sure node is initialized (variables set) before defining the module
if(NOT NODEJS_INIT)
message(FATAL_ERROR
"Node.js has not been initialized. "
"Call nodejs_init before adding any modules"
)
endif()
# In order to match node-gyp, we need to build into type specific folders
# ncmake takes care of this, but be sure to set CMAKE_BUILD_TYPE yourself
# if invoking CMake directly
if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
message(FATAL_ERROR
"Configuration type must be specified. "
"Set CMAKE_BUILD_TYPE or use a different generator"
)
endif()
# A node module is a shared library
add_library(${NAME} SHARED ${NODEJS_SOURCES} ${ARGN})
# Add compiler defines for the module
# Two helpful ones:
# MODULE_NAME must match the name of the build library, define that here
# ${NAME}_BUILD is for symbol visibility under windows
string(TOUPPER "${NAME}_BUILD" ${NAME}_BUILD_DEF)
target_compile_definitions(${NAME}
PRIVATE MODULE_NAME=${NAME}
PRIVATE ${${NAME}_BUILD_DEF}
PUBLIC ${NODEJS_DEFINITIONS}
)
# This properly defines includes for the module
target_include_directories(${NAME} PUBLIC ${NODEJS_INCLUDE_DIRS})
# Add link flags to the module
target_link_libraries(${NAME} ${NODEJS_LIBRARIES})
# Set required properties for the module to build properly
# Correct naming, symbol visiblity and C++ standard
set_target_properties(${NAME} PROPERTIES
OUTPUT_NAME ${NAME}
PREFIX ""
SUFFIX ".node"
MACOSX_RPATH ON
C_VISIBILITY_PRESET hidden
CXX_VISIBILITY_PRESET hidden
POSITION_INDEPENDENT_CODE TRUE
CMAKE_CXX_STANDARD_REQUIRED TRUE
CXX_STANDARD 11
)
# Handle link flag cases properly
# When there are link flags, they should be appended to LINK_FLAGS with space separation
# If the list is emtpy (true for most *NIX platforms), this is a no-op
foreach(NODEJS_LINK_FLAG IN LISTS NODEJS_LINK_FLAGS)
set_property(TARGET ${NAME} APPEND_STRING PROPERTY LINK_FLAGS " ${NODEJS_LINK_FLAG}")
endforeach()
# Make sure we're buiilding in a build specific output directory
# Only necessary on single-target generators (Make, Ninja)
# Multi-target generators do this automatically
# This (luckily) mirrors node-gyp conventions
if(NOT CMAKE_CONFIGURATION_TYPES)
set_property(TARGET ${NAME} PROPERTY
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BUILD_TYPE}
)
endif()
endfunction()
# Cat client for Node.js
`nodecat` supports node v8+.
## Requirements
The `nodecat` required `libcatclient.so` installed in `LD_LIBRARY_PATH`.
Please refer to [ccat installation](../c/README.md) for further information.
## Installation
### via npm
```bash
npm install nodecat
```
## 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
<?xml version="1.0" encoding="utf-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
<servers>
<server ip="<cat server ip address>" port="2280" http-port="8080" />
</servers>
</config>
```
Don't forget to change the `<cat server IP address>` to your own after you copy and paste the contents.
After you've done all things above, `nodecat` can be initialized with following codes:
```js
var cat = require('nodecat')
cat.init({
appkey: 'your-appkey'
})
```
> Only English characters, numbers, underscore and dash is allowed in appkey.
## Documentation
### Transaction
```js
let t = cat.newTransaction('foo', 'bar')
setTimeout(() => t.complete(), 3000)
```
#### Transaction apis
We offered a list of APIs to modify the transaction.
* addData
* setStatus
* complete
> You can call `addData` several times, the added data will be connected by `&`.
Here is an exapmle:
```js
let t = cat.newTransaction('foo', 'bar')
t.addData("key", "val")
t.addData("context")
t.setStatus(cat.STATUS.SUCCESS)
setTimeout(() => t.complete(), 3000)
```
### Event
#### logEvent
Log an event.
```js
// Log a event with success status and empty data.
cat.logEvent("Event", "E1")
// The third parameter (status) is optional, default by "0".
// It can be any of string value.
// The event will be treated as "problem" unless the given status == cat.STATUS.SUCCESS ("0")
// which will be recorded in our problem report.
cat.logEvent("Event", "E2", cat.STATUS.FAIL)
cat.logEvent("Event", "E3", "failed")
// The fourth parameter (data) is optional, default by "".
// It can be any of string value.
cat.logEvent("Event", "E4", "failed", "some debug info")
// The fourth parameter (data) can also be an object
// In this case, the object will be dumped into json.
cat.logEvent("Event", "E5", "failed", {a: 1, b: 2})
```
#### logError
Log an error with error stack.
Error is a special event, with `type = Exception` and `name` is given by the 1st parameter.
And error stacktrace will be added to `data`, which is always useful in debugging.
```js
cat.logError('ErrorInTransaction', new Error())
```
\ No newline at end of file
{
"targets": [
{
"target_name": "nodecat",
"sources": [
"src/nodecat.cc",
],
"include_dirs": [
"include"
],
"libraries": [
"-lcatclient"
]
}
]
}
var cat = require('../lib')
cat.init({
appkey: 'nodecat'
})
for (var i = 0; i < 10; i++) {
// Log a event with success status and empty data.
cat.logEvent("Event", "E1")
// The third parameter (status) is optional, default by "0".
// It can be any of string value.
// The event will be treated as "problem" unless the given status == cat.STATUS.SUCCESS ("0")
// which will be recorded in our problem report.
cat.logEvent("Event", "E2", cat.STATUS.FAIL)
cat.logEvent("Event", "E3", "failed")
// The fourth parameter (data) is optional, default by "".
// It can be any of string value.
cat.logEvent("Event", "E4", "failed", "some debug info")
// The fourth parameter (data) can also be an object
// In this case, the object will be dumped into json.
cat.logEvent("Event", "E5", "failed", {a: 1, b: 2})
}
let cat = require('../lib')
cat.init({
appkey: 'nodecat'
})
let a = cat.newTransaction('Trans', 'A')
let b = cat.newTransaction('Trans', 'B')
let c = cat.newTransaction('Trans', 'C')
setTimeout(function() {
a.complete()
}, 1000)
setTimeout(function() {
b.complete()
}, 2000)
setTimeout(function() {
c.complete()
}, 3000)
'use strict'
let cat = require('../lib')
cat.init({
appkey: 'nodecat'
})
let t = cat.newTransaction('foo', 'bar')
cat.logEvent('EventInTransaction', 'T1')
cat.logError('ErrorInTransaction', new Error())
t.addData("key", "val")
t.addData("context")
t.setStatus(cat.STATUS.SUCCESS)
t.logEvent('childEvent', '1')
t.logError('childError', new Error())
setTimeout(() => t.complete(), 1000)
#ifndef CAT_CLIENT_C_CLIENT_H
#define CAT_CLIENT_C_CLIENT_H
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/**
* Constants
*/
#define CAT_CLIENT_EXPORT
#define CAT_SUCCESS "0"
#define CAT_FAIL "-1"
#define CAT_ERROR "ERROR"
#define CAT_ENCODER_TEXT 0
#define CAT_ENCODER_BINARY 1
/**
* C Struct Definitions
*/
typedef struct _CatMessage CatMessage;
typedef struct _CatMessage CatEvent;
typedef struct _CatMessage CatMetric;
typedef struct _CatMessage CatHeartBeat;
typedef struct _CatTransaction CatTransaction;
struct _CatMessage {
/**
* Add some debug data to a message.
* It will be shown in the log view page.
* Note not all the data will be preserved. (If sampling is enabled)
*/
void (*addData)(CatMessage *message, const char *data);
/**
* Add some debug data to a message, in key-value format.
*/
void (*addKV)(CatMessage *message, const char *dataKey, const char *dataValue);
/**
* Set the status of a message
* Note that any status not equal to "0" (CAT_SUCCESS) will be treated as a "problem".
* And can be recorded in our problem report.
* A message tree which contains a "problem" message won't be sampling.
* In some cases, especially in high concurrency situations, it may cause network problems.
*/
void (*setStatus)(CatMessage *message, const char *status);
/**
* Set the created timestamp of a transaction
*/
void (*setTimestamp)(CatMessage *message, unsigned long long timestamp);
/**
* Complete the message.
* Meaningless in most cases, only transaction has to be completed manually.
*/
void (*complete)(CatMessage *message);
};
struct _CatTransaction {
/**
* Add some debug data to a transaction.
* It will be shown in the log view page.
* Note not all the data will be preserved. (If sampling is enabled)
*/
void (*addData)(CatTransaction *transaction, const char *data);
/**
* Add some debug data to a transaction, in key-value format.
*/
void (*addKV)(CatTransaction *transaction, const char *dataKey, const char *dataValue);
/**
* Set the status of a transaction
* Note that any status not equal to "0" (CAT_SUCCESS) will be treated as a "problem".
* And can be recorded in our problem report.
* A message tree which contains a "problem" transaction won't be sampling.
* In some cases, especially in high concurrency situations, it may cause network problems.
*/
void (*setStatus)(CatTransaction *transaction, const char *status);
/**
* Set the created timestamp of a transaction
*/
void (*setTimestamp)(CatTransaction *transaction, unsigned long long timestamp);
/**
* Complete the transaction
*/
void (*complete)(CatTransaction *transaction);
/**
* Add a child directly to a transaction.
* Avoid of using this api unless you really have to do so.
*/
void (*addChild)(CatTransaction *transaction, CatMessage *message);
/**
* Set the duration of a transaction.
* The duration will be calculated when the transaction has been completed.
* You can prevent it and specified the duration through this api.
*/
void (*setDurationInMillis)(CatTransaction* transaction, unsigned long long duration);
/**
* Set the duration start of a transaction.
* The duration start is same as timestamp by default.
* You can overwrite it through this api, which can influence the calculated duration.
* When a transaction is completed, the duration will be set to (currentTimestamp - durationStart)
* Note that it only works when duration has not been specified.
*/
void (*setDurationStart)(CatTransaction* transaction, unsigned long long durationStart);
};
typedef struct _CatClientConfig {
int encoderType;
int enableHeartbeat;
int enableSampling;
int enableMultiprocessing;
int enableDebugLog;
} CatClientConfig;
extern CatClientConfig DEFAULT_CCAT_CONFIG;
#ifdef __cplusplus
extern "C" {
#endif
/**
* Common Apis
*/
CAT_CLIENT_EXPORT int catClientInit(const char *appkey);
CAT_CLIENT_EXPORT int catClientInitWithConfig(const char *appkey, CatClientConfig* config);
CAT_CLIENT_EXPORT int catClientDestroy();
CAT_CLIENT_EXPORT int isCatEnabled();
/**
* Transaction Apis
*/
CAT_CLIENT_EXPORT CatTransaction *newTransaction(const char *type, const char *name);
/**
* Create a transaction with specified duration in milliseconds.
*
* This is equivalent to
*
* CatTransaction *t = newTransaction("type", "name");
* t->setDurationInMillis(t4, 1000);
* return t;
*/
CAT_CLIENT_EXPORT CatTransaction *newTransactionWithDuration(const char *type, const char *name, unsigned long long duration);
/**
* Log a transaction with specified duration in milliseconds.
*
* Due to the transaction has been auto completed,
* the duration start and the created timestamp will be turn back.
*
* This is equivalent to
*
* CatTransaction *t = newTransaction("foo", "bar2-completed");
* t->setTimestamp(t, GetTime64() - 1000);
* t->setDurationStart(t, GetTime64() - 1000);
* t->setDurationInMillis(t, 1000);
* t->complete(t);
* return;
*/
CAT_CLIENT_EXPORT void newCompletedTransactionWithDuration(const char *type, const char *name, unsigned long long duration);
/**
* Event Apis
*/
CAT_CLIENT_EXPORT void logEvent(const char *type, const char *name, const char *status, const char *data);
/**
* Log a error message.
*
* This is equivalent to
*
* logEvent("Exception", msg, CAT_ERROR, errStr);
*/
CAT_CLIENT_EXPORT void logError(const char *msg, const char *errStr);
/**
* Create a event message manually.
*
* Avoid using this api unless you really have to.
*/
CAT_CLIENT_EXPORT CatEvent *newEvent(const char *type, const char *name);
/**
* Heartbeat Apis
*/
/**
* Create a heartbeat message manually.
*
* Heartbeat is reported by cat client automatically,
* so you don't have to use this api in most cases,
* unless you want to overwrite our heartbeat message.
*
* Don't forget to disable our built-in heartbeat if you do so.
*/
CAT_CLIENT_EXPORT CatHeartBeat *newHeartBeat(const char *type, const char *name);
/**
* Metric Apis
*/
CAT_CLIENT_EXPORT void logMetricForCount(const char *name, int quantity);
CAT_CLIENT_EXPORT void logMetricForDuration(const char *name, unsigned long long duration);
/**
* MessageId Apis
*/
CAT_CLIENT_EXPORT char *createMessageId();
CAT_CLIENT_EXPORT char *createRemoteServerMessageId(const char *appkey);
CAT_CLIENT_EXPORT char *getThreadLocalMessageTreeId();
CAT_CLIENT_EXPORT char *getThreadLocalMessageTreeRootId();
CAT_CLIENT_EXPORT char *getThreadLocalMessageTreeParentId();
CAT_CLIENT_EXPORT void setThreadLocalMessageTreeId(char *messageId);
CAT_CLIENT_EXPORT void setThreadLocalMessageTreeRootId(char *messageId);
CAT_CLIENT_EXPORT void setThreadLocalMessageTreeParentId(char *messageId);
#ifdef __cplusplus
}
#endif
#endif //CAT_CLIENT_C_CLIENT_H
'use strict'
exports.implement = require('./implements/ccat')
'use strict'
const implement = require('./adapter').implement
const EventMessage = require('./message/event')
const TransactionMessage = require('./message/transaction')
const TreeManager = require('./message/org/tree-manager')
const STATUS = require('./constant').STATUS
const system = require('./system')
let isInitialized = false;
class TransactionHandler {
constructor(transactionMessage, catInstance) {
this.message = transactionMessage
this.cat = catInstance
this.treeManager = catInstance.treeManager
this.type = transactionMessage.type
this.name = transactionMessage.name
}
/**
* 设置transaction状态
* @param {string} status , STATUS
*/
setStatus(status) {
this.message.status = '' + status
}
/**
* Add data to a transaction.
* 序列化成 query的形式 &key=value
* 允许多次addData
* @param {string} key , 如果value存在则作为key,否则作为完整的data
* @param {string} [value]
*/
addData(key, value) {
let data = stringifyData(key)
if (value !== undefined) {
data = data + '=' + stringifyData(value)
}
if (this.message.data) {
this.message.data += '&' + data
} else {
this.message.data += data
}
}
/**
* end a transaction.
* @param {number} maxTime
*/
complete(maxTime) {
this.treeManager.endMessage(this.message, maxTime)
}
/**
* logEvent , 同cat.logEvent , 但确保挂在此transaction下
*/
logEvent(type, name, status, data) {
let message = this.message
if (message.isEnd) {
return this.cat.logEvent(type, name, status, data)
} else {
message.addChild(createEvent(type, name, status, data))
}
}
/**
* logError , 同cat.logError , 但确保挂在此transaction下
*/
logError(name, error) {
let message = this.message
if (message.isEnd) {
return this.cat.logError(name, error)
} else {
message.addChild(createError(name, error))
}
}
}
/**
* Class Cat
* 暴露给用户的API在这边,以这里的参数说明为准
* */
class Cat {
constructor() {
this.STATUS = STATUS
this.treeManager = new TreeManager(implement)
}
/**
* @param {object} options
* {string} options.appkey
*/
init(options) {
if (isInitialized) {
return
}
isInitialized = true
let logger = require('./logger')('index')
options = options || {}
logger.info('Cat Version : ' + require('../package').version)
let appkey = options.appkey
if (!appkey) {
logger.info('Appkey is required')
return
}
logger.info('Appkey has been set to ' + appkey)
implement.init(appkey)
system.collectStart(this)
}
/**
* @param {string} type 一级名称
* @param {string} name 二级名称
* @param {string} [status] 状态, 参见 STATUS
* @param {string} [data] 数据
*/
logEvent(type, name, status, data) {
this.treeManager.addMessage(createEvent(type, name, status, data))
}
/**
* @param {string} [name]
* @param {Error} error
*/
logError(name, error) {
this.treeManager.addMessage(createError(name, error))
}
/**
* 同CatInterface
* @param {string} type
* @param {string} name
*/
newTransaction(type, name) {
type = '' + type
name = '' + name
var message = new TransactionMessage({
type: type,
name: name
})
this.treeManager.addMessage(message)
let t = new TransactionHandler(message, this)
return t
}
}
function createEvent(type, name, status, data) {
type = '' + type
name = '' + name
data = stringifyData(data)
status = status ? ('' + status) : STATUS.SUCCESS
return new EventMessage({
type: type,
name: name,
status: status,
data: data
})
}
function createError(name, error) {
if (name instanceof Error) {
error = name
name = null
}
name = name || (error && error.name) || 'Error'
let stack = error ? error.stack : ''
let errStr = name ? (name + ' ' + stack) : stack
return createEvent('Error', name, 'ERROR', errStr)
}
function stringifyData(data) {
if (data === undefined) {
data = ''
}
if (typeof data !== 'string') {
// 有data参数,但不是字符串
try {
data = JSON.stringify(data)
} catch (e) {
data = data.toString ? data.toString() : ''
}
}
return data
}
module.exports = Cat
'use strict'
const os = require('os')
const logger = require('./logger.js')('config')
const getLocalIP = () => {
var ip = process.env.HOST_IP
if (!ip) {
var interfaces = os.networkInterfaces()
var addresses = []
for (var k in interfaces) {
for (var k2 in interfaces[k]) {
var address = interfaces[k][k2]
if (address.family === 'IPv4' && !address.internal) {
addresses.push(address.address)
}
}
}
addresses.length && (ip = addresses[0])
}
logger.info('Get local ip ' + ip)
return ip
}
// exports
var config = {
maxMessageLength: 2000,
hostname: os.hostname(),
domain: 'node-cat',
ip: getLocalIP()
}
module.exports = function () {
return config
}
module.exports.setDomain = function (domain) {
config.domain = domain
}
'use strict'
exports.STATUS = {
SUCCESS: '0',
FAIL: '1'
}
'use strict'
const CCatApi = require('../../build/Release/nodecat')
const Event = require('../message/event')
exports.init = (appKey) => {
CCatApi.init(appKey)
process.on('exit', (code) => {
try {
CCatApi.destroy()
} catch (e) {
}
})
}
function countTree(message) {
let count = 1
message.children.forEach(child => (count += countTree(child)))
return count
}
function position(count) {
const array = [200, 500, 1000]
for (let i = 0; i < array.length; i++) {
if (count < array[i]) {
return '<' + array[i]
}
}
return '>' + array[array.length - 1]
}
exports.sendTree = (tree) => {
if (tree.root.messageType === 'transaction') {
let count = countTree(tree.root)
let event = new Event({
type: 'TreeCount',
name: position(count),
data: '' + count,
status: '0'
})
tree.root.addChild(event)
}
CCatApi.sendTree(tree)
}
const Cat = require('./cat')
/**
* exports 一个默认Cat的实例 , 方便直接使用
*/
module.exports = new Cat()
module.exports.Cat = Cat
module.exports = function (filename) {
return {
error: function (msg) {
console.log(msg);
},
info: function (msg) {
console.log(msg);
},
warn: function (msg) {
console.log(msg);
}
}
}
\ No newline at end of file
'use strict'
var Message = require('./message')
/**
* Get an instance of an event
* CatEvent is inherited from the Message.
*
* @param {object} event options.
*/
class CatEvent extends Message {
constructor(options) {
super(options)
this.messageType = 'event'
this.begin()
this.end()
}
}
module.exports = CatEvent
'use strict'
var Message = require('./message')
var config = require('../config')()
class Heartbeat extends Message {
constructor(options) {
super(options)
this.type = 'Heartbeat'
this.name = config.ip
this.status = '0'
this.messageType = 'heartbeat'
this.begin()
this.end()
}
}
module.exports = Heartbeat
'use strict'
var HOUR = 3600 * 1000
var cluster = require('cluster')
var config = require('../config')()
var assert = require('assert')
var os = require('os')
var cpuCount = os.cpus().length
var seq = initialSeq()
var hourTS
var defaultIpHex
if (config.ip) {
var ips = config.ip.split('.')
assert.equal(ips.length, 4, 'ip must contains 4 groups')
var buffer = new Buffer(4)
for (var i = 0; i < 4; ++i) {
buffer.writeUInt8(parseInt(ips[i]), i)
}
defaultIpHex = ''
for (var j = 0; j < buffer.length; j++) {
var b = buffer.readUInt8(j)
defaultIpHex += ((b >> 4) & 0x0f).toString(16)
defaultIpHex += (b & 0x0f).toString(16)
}
}
module.exports.nextId = function (domain) {
var ts = Math.floor(Date.now() / HOUR)
if (ts != hourTS) {
seq = initialSeq()
hourTS = ts
}
seq += cpuCount
// first character is clusterId, will be different between cluster processes
return [domain || config.domain, defaultIpHex, ts, '' + seq].join('-')
}
module.exports.MessageId = MessageId
function MessageId(domain, hexIp, timestamp, index) {
this.domain = domain
this.hexIp = hexIp
this.timestamp = timestamp
this.index = index
}
module.exports.parse = function (messageId) {
if (!messageId) return null
var list = messageId.split('-')
var len = list.length
if (len >= 4) {
var ipAddressInHex = list[len - 3]
var timestamp = parseInt(list[len - 2])
var index = parseInt(list[len - 1])
var domain = list.splice(0, len - 3).join('-')
return new MessageId(domain, ipAddressInHex, timestamp, index)
}
return null
}
module.exports.getIpAddress = function () {
var local = this.hexIp
var i = []
for (var i = 0, len = local.length; i < len; i += 2) {
var first = local.charAt(i)
var next = local.charAt(i + 1)
var temp = 0
if (first >= '0' && first <= '9') {
temp += (first - '0') << 4
} else {
temp += ((first - 'a') + 10) << 4
}
if (next >= '0' && next <= '9') {
temp += next - '0'
} else {
temp += (next - 'a') + 10
}
i.push(temp)
}
return i.join('.')
}
function initialSeq() {
let seq = 0
if (process.env && process.env.pm_id) {
seq = process.env.pm_id % cpuCount
} else {
if (cluster.isWorker) {
seq = cluster.worker.id % cpuCount
}
}
return seq
}
'use strict'
/**
* Get an instance of a basic message.
*
* @param {object} options Message options for initialization.
*/
class Message {
constructor(options) {
options = options || {}
this.type = options.type || undefined
this.name = options.name || undefined
this.status = options.status || '0'
this.beginTime = options.beginTime || new Date()
this.beginTimestamp = +this.beginTime
this.endTime = options.endTime || new Date()
this.endTimestamp = +this.endTime
this.data = options.data || ''
this.children = options.children || []
this.parent = null
// this.uid = options.uid || undefined;
// this.uid = rand.generate();
this.isBegin = options.isBegin || false
this.isEnd = options.isEnd || false
this.allEnd = false
this.tree = null // 如果作为tree的根节点,这个属性作为索引
// this.puid = options.puid || undefined;
this.messageType = 'message' // 子类复写
}
addOptions(options) {
Object.keys(options).forEach(prop => {
if (prop === 'data') {
if (this[prop] === undefined || this[prop] === null) {
this[prop] = options[prop]
} else {
this[prop] = this[prop] + options[prop]
}
} else {
this[prop] = options[prop]
}
})
}
begin() {
if (this.isBegin) {
return
}
this.isBegin = true
this.beginTime = new Date()
this.beginTimestamp = +this.beginTime
}
end(maxTime) {
if (this.isEnd) {
return
}
this.isEnd = true
let now = new Date()
if (maxTime) {
if (now - this.beginTime > maxTime) {
this.endTime = new Date(+this.beginTime + maxTime)
} else {
this.endTime = now
}
} else {
this.endTime = now
}
this.endTimestamp = +this.endTime
}
addChild(message) {
var self = this
Array.prototype.forEach.call(arguments, message => {
self.children.push(message)
message.parent = self
// 如果当前的已经结束,但是加进来的节点没有end
if (self.allEnd && !message.isAllEnd()) {
self.allEnd = false
}
})
}
removeChild() {
var children = this.children
Array.prototype.forEach.call(arguments, message => {
var index = children.indexOf(message)
if (index > -1) {
children.splice(index, 1)
message.parent = null
}
})
this.isAllEnd()
}
/**
* 是否自己和子节点全部都已经End
*/
isAllEnd() {
if (this.allEnd) {
return true
}
if (!this.children.length) {
return this.allEnd = this.isEnd
}
this.allEnd = (this.isEnd && this.children.every(child => child.isAllEnd()))
return this.allEnd
}
}
module.exports = Message
'use strict'
var Tree = require('./tree')
var Event = require('../event')
var Transaction = require('../transaction')
var Heartbeat = require('../heartbeat')
class TreeManager {
constructor(sender) {
// 会出现多个tree的情况
this.trees = []
// 最近一个挂上去的transaction message
this.lastNode = null
this.sender = sender
}
/**
* 添加一个message到tree中,
* 如果是transaction , 构建树结构
* 如果是Event,挂到transaction下面或者直接发送
*/
addMessage(message) {
let lastNode = this._findLastNode()
// Transaction
if (message instanceof Transaction) {
if (!lastNode) {
// 没有树或者已经发送掉了
this.createTree(message)
this.lastNode = message
} else {
// 已经构建过 tree 了,加入到最后一个transaction的子节点
lastNode.addChild(message)
this.lastNode = message
}
message.begin()
} else if (message instanceof Event || message instanceof Heartbeat) {
// Event or Heartbeat
if (!lastNode) {
// 没有构建过树的时候,直接把消息发出去
this.sendTree(new Tree({
root: message
}))
} else {
lastNode.addChild(message)
}
}
}
/**
* 某个transaction结束
* 如果是叶子节点:
* 修改状态
* 通知父节点
* 如果是父节点:
* 判断子节点的transaction是否end,
* 如果没结束,说明add的时候挂的节点是不对的,需要修改树结构
* 如果全都结束,通知父节点
*/
endMessage(message, maxTime) {
// 先end自己
message.end(maxTime)
if (!(message instanceof Transaction)) {
return
}
if (message.isAllEnd()) {
// 如果整个子节点都结束了
this.notifyParentEnd(message)
} else {
// 如果自己结束了,但是子节点没结束,说明子节点中有挂的不对的,不应该挂在自己下面,提到自己并列
let unEndChildren = message.children.filter(child => !child.isAllEnd())
message.removeChild.apply(message, unEndChildren)
if (message.parent) {
message.parent.addChild.apply(message.parent, unEndChildren)
} else {
// 根节点自己结束了,但是有子节点没结束的,为子节点单独创建树
this.sendTree(message.tree)
unEndChildren.forEach(msg => {
this.createTree(msg)
})
}
}
}
notifyParentEnd(message) {
if (message.parent) {
if (message.parent.isEnd) {
// 如果父节点自己已经结束,再end一次,让父节点判断是否全都结束
this.endMessage(message.parent)
} else {
// 什么都不干,等父节点end
}
} else {
// 自己就是根节点
this.sendTree(message.tree)
}
}
sendTree(tree) {
if (!tree) {
return
}
var index = this.trees.indexOf(tree)
if (index > -1) {
this.trees.splice(index, 1)
}
this.sender.sendTree(tree)
}
// 找到最后一个节点,如果this.lastNode是end的,就一直往父节点找
_findLastNode() {
if (!this.lastNode) {
return null
}
var last = this.lastNode
while (last && last.isEnd) {
last = last.parent
}
return last
}
createTree(rootMessage) {
var tree = new Tree({
root: rootMessage
})
this.trees.push(tree)
return tree
}
}
module.exports = TreeManager
'use strict'
var config = require('../../config')()
/**
* Get an instance of a message tree.
*
* @param {object} options Tree options for initialization.
*/
class Tree {
constructor(options) {
this.domain = options.domain || config.domain
this.hostName = options.hostName || config.hostname
this.ip = options.ip || config.ip
this.groupName = options.groupName || config.groupName
this.clusterId = options.clusterId || config.clusterId
this.clusterName = options.clusterName || config.clusterName
this.messageId = options.messageId // set later
this.parentMessageId = options.parentMessageId || this.messageId
this.rootMessageId = options.rootMessageId || this.messageId
this.sessionToken = options.sessionToken || config.sessionToken
this.root = options.root || undefined
if (this.root) {
this.root.tree = this
}
}
}
module.exports = Tree
'use strict'
var Message = require('./message')
/**
* Get an instance of a transaction.
* Transaction is inherited from the message.
*
* @param {object} options.
*/
class Transaction extends Message {
constructor(options) {
super(options)
this.messageType = 'transaction'
}
}
module.exports = Transaction
'use strict'
var moment = require('moment')
function date2str(date) {
return moment(date).format('YYYY-MM-DD HH:mm:ss.SSS')
};
function durationInMillis(start, end) {
return (end - start)
};
function durationInMicros(start, end) {
return (end - start) * 1000
};
module.exports = {
date2str: date2str,
durationInMillis: durationInMillis,
durationInMicros: durationInMicros
}
/**
* user child process to get disk space
* */
'use strict'
const exec = require('../util/shell').exec
const fs = require('fs')
if (process.platform === 'linux' ||
process.platform === 'freebsd' ||
process.platform === 'darwin' ||
process.platform === 'sunos') {
exports.usage = function* (drive) {
if (!fs.existsSync(drive)) {
return null
}
try {
var res = yield exec("df -k '" + drive.replace(/'/g, "'\\''") + "'")
var lines = res.trim().split('\n')
var strDiskInfo = lines[lines.length - 1].replace(/[\s\n\r]+/g, ' ')
var diskInfo = strDiskInfo.split(' ')
return {
available: diskInfo[3] * 1024,
total: diskInfo[1] * 1024,
free: diskInfo[3] * 1024
}
} catch (e) {
return null
}
}
} else {
exports.usage = function* () {
return null
}
}
/**
* 获取系统的memory swap buffer/cache 使用情况
* 单位为byte
* */
'use strict'
var exec = require('../util/shell').exec
var os = require('os')
const DEFAULT_RESULT = {
totalMem: 0,
freeMem: 0,
totalSwap: 0,
freeSwap: 0,
totalCache: 0,
freeCache: 0
}
/**
* return Object
* {
* totalMem: 1,
* freeMem:1,
* totalSwap:1,
* freeSwap:1,
* totalCache: 0,
* freeCache: 0
* }
* */
exports.usage = function* () {
function split(line) {
return line.split(/[\s\n\r]+/)
}
if (process.platform === 'linux' ||
process.platform === 'freebsd' ||
process.platform === 'sunos') {
try {
var res = yield exec('free -m')
var lines = res.trim().split('\n')
var usage = Object.assign({}, DEFAULT_RESULT)
var mem = split(lines[1])
usage.totalMem = mem[1] * 1024 * 1024
usage.freeMem = mem[3] * 1024 * 1024
var swap = split(lines[3])
usage.totalSwap = swap[1] * 1024 * 1024
usage.freeSwap = swap[3] * 1024 * 1024
var cache = split(lines[2])
usage.totalCache = (+cache[2] + cache[2]) * 1024 * 1024
usage.freeCache = cache[3] * 1024 * 1024
return usage
} catch (e) {
// 不支持free 命令
return {
totalMem: os.totalmem(),
freeMem: os.freemem(),
totalSwap: 0,
freeSwap: 0,
totalCache: 0,
freeCache: 0
}
}
} else if (process.platform === 'darwin') {
// mac
return {
totalMem: os.totalmem(),
freeMem: os.freemem(),
totalSwap: 0,
freeSwap: 0,
totalCache: 0,
freeCache: 0
}
} else {
return {}
}
}
'use strict'
/**
* Base Class for System info
* */
class SystemBaseInfo {
constructor(name, properties, content) {
this.name = name
this.children = []
this.attrs = properties || {}
this.content = content
}
toString() {
// to xml item
var tag = '<' + this.name
var attrKeys = Object.keys(this.attrs)
if (attrKeys.length) {
tag += (' ' + attrKeys.map(key => key + '=' + '"' + this.attrs[key] + '"').join(' '))
}
if (this.children.length) {
tag += '>\n'
this.children.forEach(child => {
tag += child.toString()
})
tag += ('</' + this.name + '>\n')
} else if (this.content) {
tag += ('>' + this.content + '</' + this.name + '>\n')
} else {
tag += '/>\n'
}
return tag
}
addChild(childInfo) {
if (childInfo && childInfo instanceof SystemBaseInfo) {
this.children.push(childInfo)
}
}
}
module.exports = SystemBaseInfo
'use strict'
const os = require('os')
const SystemInfo = require('./SystemBaseInfo')
const Time = require('../message/util/time')
const Disk = require('./Disk')
const Mem = require('./Memory')
var userName = ''
if (os.userInfo) {
userName = os.userInfo().username
} else {
userName = require('child_process').execSync('whoami', {encoding: 'utf8', timeout: 1000}).replace('\n', '')
}
/**
* @return {SystemInfo} base system info
*/
exports.SystemInfoCollector = function* () {
let mem = yield Mem.usage()
let rootDisk = yield Disk.usage('/')
let status = new SystemInfo('status', {
timestamp: Time.date2str(new Date())
})
// System Extension
let systemExtension = new SystemInfo('extension', {
id: 'System'
})
status.addChild(systemExtension)
systemExtension.addChild(new SystemInfo('extensionDetail', {
id: 'LoadAverage',
value: os.loadavg()[0],
}))
systemExtension.addChild(new SystemInfo('extensionDetail', {
id: 'FreePhysicalMemory',
value: mem.freeMem,
}))
systemExtension.addChild(new SystemInfo('extensionDetail', {
id: 'Cache/Buffer',
value: mem.freeCache / 1024 / 1024
}))
systemExtension.addChild(new SystemInfo('extensionDetail', {
id: 'FreeSwapSpaceSize',
value: mem.freeSwap
}))
systemExtension.addChild(new SystemInfo('extensionDetail', {
id: 'HeapUsage',
value: process.memoryUsage().heapUsed / 1024 / 1024
}))
// Disk Extension
let diskExtension = new SystemInfo('extension', {
id: 'Disk'
})
status.addChild(diskExtension)
if (rootDisk) {
diskExtension.addChild(new SystemInfo('extensionDetail', {
id: '/ Free',
value: rootDisk.free
}))
}
return status
}
'use strict'
const HeartBeat = require('./message/heartbeat')
const os = require('os')
const co = require('co')
const collector = require('./sys/collector').SystemInfoCollector
let config = require('./config')()
exports.collectStart = function (cat) {
if (process.env && process.env.pm_id) {
// in pm2 , only one worker does this collection
try {
if (process.env.pm_id % os.cpus().length !== 0) {
return
}
} catch (e) {
}
}
cat.logEvent('Reboot', '' + config.ip)
function sys() {
co(function* () {
let t = cat.newTransaction('System', 'Status')
let status = yield collector()
cat.treeManager.addMessage(new HeartBeat({
data: '<?xml version="1.0" encoding="utf-8"?>\n' + status.toString()
}))
t.setStatus(cat.STATUS.SUCCESS)
t.complete()
})
}
sys()
setInterval(sys, 60000)
}
'use strict'
const spawn = require('child_process').spawn
exports.exec = function (cmd) {
return new Promise((resolve, reject) => {
let shell = spawn('sh', ['-c', cmd])
let result = []
shell.stdout.on('data', (data) => {
result.push(data.toString())
})
shell.on('error', e => {
reject(e)
})
shell.on('close', code => {
if (code) {
reject(new Error('exec [' + cmd + '] fail'))
} else {
resolve(result.join())
}
})
})
}
因为 它太大了无法显示 source diff 。你可以改为 查看blob
{
"name": "nodecat",
"version": "1.0.0",
"description": "Cat client for Node.js.",
"main": "index.js",
"scripts": {
"test": "mocha",
"release": "standard-version",
"build": "node-gyp configure && node-gyp build"
},
"keywords": [
"cat",
"client",
"nodejs"
],
"author": "ywang1724",
"dependencies": {
"buffer-builder": "^0.2.0",
"debug": "^2.2.0",
"mkdirp": "^0.5.1",
"moment": "^2.10.6",
"request": "^2.67.0",
"xml2js": "^0.4.15"
},
"devDependencies": {
"cz-conventional-changelog": "^2.0.0",
"eslint": "^3.19.0",
"eslint-config-standard": "^10.2.1",
"eslint-config-yayajing": "^1.0.0",
"eslint-plugin-import": "^2.3.0",
"eslint-plugin-jest": "^20.0.3",
"eslint-plugin-node": "^4.2.2",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^3.0.1",
"jest": "^20.0.4",
"mocha": "^2.3.4",
"node-cmake": "^2.5.1",
"standard-version": "^4.0.0"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
}
//
// Created by Terence on 2018/9/3.
//
#ifndef NODECAT_DEBUG_H
#define NODECAT_DEBUG_H
#include <iostream>
#define DEBUG_MODE 1
#ifdef DEBUG_MODE
#define debuginfo(s) std::cout << s << std::endl;
#else
#define debuginfo(s)
#endif
#endif //NODECAT_DEBUG_H
#include <node.h>
#include "client.h"
#include "debug.h"
using namespace v8;
using namespace std;
namespace catapi {
/**
* Initialize cat
* @param args
*/
void Init(const FunctionCallbackInfo<Value> &args) {
Isolate *isolate = args.GetIsolate();
if (args.Length() < 1) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Appkey is required")));
return;
}
String::Utf8Value str(args[0]->ToString());
CatClientConfig config = DEFAULT_CCAT_CONFIG;
config.enableHeartbeat = 0;
config.enableMultiprocessing = 1;
catClientInitWithConfig((const char *) (*str), &config);
}
/**
* Destroy cat
* @param args
*/
void Destroy(const FunctionCallbackInfo<Value> &args) {
catClientDestroy();
}
void InnerSendNode(Isolate *isolate, Local<Object> node) {
if (node->IsNull() || node->IsUndefined()) {
return;
}
String::Utf8Value messageType((node->Get(String::NewFromUtf8(isolate, "messageType")))->ToString());
if (strcmp(*messageType, "transaction") == 0) {
String::Utf8Value type((node->Get(String::NewFromUtf8(isolate, "type")))->ToString());
String::Utf8Value name((node->Get(String::NewFromUtf8(isolate, "name")))->ToString());
String::Utf8Value status((node->Get(String::NewFromUtf8(isolate, "status")))->ToString());
String::Utf8Value data((node->Get(String::NewFromUtf8(isolate, "data")))->ToString());
double begin = node->Get(String::NewFromUtf8(isolate, "beginTimestamp"))->NumberValue();
double end = node->Get(String::NewFromUtf8(isolate, "endTimestamp"))->NumberValue();
CatTransaction *t = newTransaction((const char *) (*type), (const char *) (*name));
t->setDurationInMillis(t, static_cast<unsigned long long int>(end - begin));
t->setTimestamp(t, static_cast<unsigned long long int>(begin));
t->setStatus(t, (const char *) (*status));
t->addData(t, (const char *) (*data));
// Iterate children recursively
Handle<Array> children = Handle<Array>::Cast(node->Get(String::NewFromUtf8(isolate, "children")));
int count = children->Length();
for (u_int32_t i = 0; i < count; i++) {
InnerSendNode(isolate, Handle<Object>::Cast(children->Get(i)));
}
t->complete(t);
} else if (strcmp(*messageType, "event") == 0) {
String::Utf8Value type((node->Get(String::NewFromUtf8(isolate, "type")))->ToString());
String::Utf8Value name((node->Get(String::NewFromUtf8(isolate, "name")))->ToString());
String::Utf8Value status((node->Get(String::NewFromUtf8(isolate, "status")))->ToString());
String::Utf8Value data((node->Get(String::NewFromUtf8(isolate, "data")))->ToString());
double begin((node->Get(String::NewFromUtf8(isolate, "beginTimestamp")))->NumberValue());
CatEvent *e = newEvent((const char *) (*type), (const char *) (*name));
e->setTimestamp(e, static_cast<unsigned long long int>(begin));
e->addData(e, (const char *) (*data));
e->setStatus(e, (const char *) (*status));
e->complete(e);
} else if (strcmp(*messageType, "heartbeat") == 0) {
String::Utf8Value type((node->Get(String::NewFromUtf8(isolate, "type")))->ToString());
String::Utf8Value name((node->Get(String::NewFromUtf8(isolate, "name")))->ToString());
String::Utf8Value data((node->Get(String::NewFromUtf8(isolate, "data")))->ToString());
CatHeartBeat *h = newHeartBeat((const char *) (*type), (const char *) (*name));
h->addData(h, (const char *) (*data));
h->setStatus(h, CAT_SUCCESS);
h->complete(h);
}
}
void SendTree(const FunctionCallbackInfo<Value> &args) {
Isolate *isolate = args.GetIsolate();
if (args.Length() == 0) {
return;
}
Handle<Object> tree = Handle<Object>::Cast(args[0]);
Handle<Object> root = Handle<Object>::Cast(tree->Get(String::NewFromUtf8(isolate, "root")));
InnerSendNode(isolate, root);
}
void exports(Local<Object> exports) {
NODE_SET_METHOD(exports, "init", Init);
NODE_SET_METHOD(exports, "destroy", Destroy);
NODE_SET_METHOD(exports, "sendTree", SendTree);
}
NODE_MODULE(nodecat, exports)
} // namespace catapi
\ No newline at end of file
......@@ -110,7 +110,7 @@ with cat.Transaction("Transaction", "T1") as t:
### Transaction apis
we offered a list of APIs to modify the transaction.
We offered a list of APIs to modify the transaction.
* add\_data
* set\_status
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册