diff --git a/README-CN.md b/README-CN.md index d1eb96b4..9840008c 100644 --- a/README-CN.md +++ b/README-CN.md @@ -70,7 +70,7 @@ Neuron 文档地址:[https://neugates.io/docs/zh/latest/](https://neugates.io/ **安装依赖** -请参考 [github.com/neugates/neuron/blob/main/Install-dependencies.md](https://github.com/neugates/neuron/blob/main/Install-dependencies.md) +请参考 [Install-dependencies](https://github.com/emqx/neuron/blob/main/Install-dependencies.md) **编译** @@ -84,7 +84,7 @@ $ cmake .. && make **安装 Dashboard** -在 [github.com/emqx/neuron-dashboard/releases](https://github.com/emqx/neuron-dashboard/releases) 页面下载最新的 `neuron-dashboard.zip`,解压后放到 Neuron 可执行目录下的 dist 目录中。 +在 [neuron-dashboard](https://github.com/emqx/neuron-dashboard/releases) 页面下载最新的 `neuron-dashboard.zip`,解压后放到 Neuron 可执行目录下的 dist 目录中。 **运行** diff --git a/README.md b/README.md index 29598d67..823c30c8 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ The Neuron documentation is available at [neugates.io/docs/en/latest/](https://n **Install required dependencies** -[github.com/neugates/neuron/blob/main/Install-dependencies.md](https://github.com/neugates/neuron/blob/main/Install-dependencies.md) +[Install-dependencies](https://github.com/emqx/neuron/blob/main/Install-dependencies.md) **Build** @@ -81,7 +81,7 @@ $ cmake .. && make **Install Dashboard** -Download the latest `neuron-dashboard.zip` from the [github.com/emqx/neuron-dashboard/releases](https://github.com/emqx/neuron-dashboard/releases) page, unzip it and put it to the `dist` directory under the Neuron executable directory. +Download the latest `neuron-dashboard.zip` from the [neuron-dashboard](https://github.com/emqx/neuron-dashboard/releases) page, unzip it and put it to the `dist` directory under the Neuron executable directory. **Run** diff --git a/include/neuron/define.h b/include/neuron/define.h index 60a01a1d..977d9b50 100644 --- a/include/neuron/define.h +++ b/include/neuron/define.h @@ -34,6 +34,7 @@ #define NEU_TAG_FLOAG_PRECISION_MAX 17 #define NEU_USER_PASSWORD_MIN_LEN 4 #define NEU_USER_PASSWORD_MAX_LEN 16 +#define NEU_LOG_LEVEL_LEN 9 typedef enum neu_plugin_kind { NEU_PLUGIN_KIND_STATIC = 0, diff --git a/include/neuron/errcodes.h b/include/neuron/errcodes.h index 35e21c14..c9e8f173 100644 --- a/include/neuron/errcodes.h +++ b/include/neuron/errcodes.h @@ -44,6 +44,7 @@ typedef enum { NEU_ERR_FILE_NOT_EXIST = 1011, NEU_ERR_INVALID_PASSWORD_LEN = 1012, NEU_ERR_DUPLICATE_PASSWORD = 1013, + NEU_ERR_LOG_LEVEL_NOT_EXIST = 1014, NEU_ERR_NODE_EXIST = 2002, NEU_ERR_NODE_NOT_EXIST = 2003, diff --git a/plugins/restful/handle.c b/plugins/restful/handle.c index b0bd92f3..bb90f303 100644 --- a/plugins/restful/handle.c +++ b/plugins/restful/handle.c @@ -291,7 +291,13 @@ struct neu_rest_handler api_handlers[] = { .method = NEU_REST_METHOD_GET, .type = NEU_REST_HANDLER_FUNCTION, .url = "/api/v2/log", - .value.handler = handle_get_log, + .value.handler = handle_download_log, + }, + { + .method = NEU_REST_METHOD_PUT, + .type = NEU_REST_HANDLER_FUNCTION, + .url = "/api/v2/level", + .value.handler = handle_log_level, }, { .method = NEU_REST_METHOD_POST, diff --git a/plugins/restful/http.c b/plugins/restful/http.c index ec913b5e..f0ac7d63 100644 --- a/plugins/restful/http.c +++ b/plugins/restful/http.c @@ -307,6 +307,7 @@ int http_response(nng_aio *aio, neu_err_code_e code, char *content) break; case NEU_ERR_BODY_IS_WRONG: case NEU_ERR_PARAM_IS_WRONG: + case NEU_ERR_LOG_LEVEL_NOT_EXIST: case NEU_ERR_NODE_SETTING_INVALID: case NEU_ERR_LIBRARY_INFO_INVALID: case NEU_ERR_LICENSE_INVALID: diff --git a/plugins/restful/log_handle.c b/plugins/restful/log_handle.c index 29d5b0be..a150414d 100644 --- a/plugins/restful/log_handle.c +++ b/plugins/restful/log_handle.c @@ -34,6 +34,7 @@ #include "errcodes.h" #include "handle.h" #include "http.h" +#include "parser/neu_json_log.h" #include "utils/log.h" #include "utils/neu_jwt.h" #include "json/neu_json_fn.h" @@ -42,50 +43,53 @@ #include "utils/utarray.h" UT_array *collect_log_files(); -void handle_get_log_files(nng_aio *aio); -int read_log_file(const char *log_file, void **datap, size_t *lenp); +// void handle_get_log_files(nng_aio *aio); +// int read_log_file(const char *log_file, void **datap, size_t *lenp); +int read_file(const char *file_name, void **datap, size_t *lenp); +// int update_log_level(const char *file_name, const char *zlog_level); -void handle_get_log(nng_aio *aio) +void handle_download_log(nng_aio *aio) { - int rv = 0; - char log_file[NEU_NODE_NAME_LEN + 32] = { 0 }; + int rv = 0; + char log_file[NEU_NODE_NAME_LEN] = { 0 }; VALIDATE_JWT(aio); - // optional params - rv = http_get_param_str(aio, "log_file", log_file, sizeof(log_file)); - if (-1 == rv) { - nlog_error("parse query param `log_file` fail"); + rv = http_get_param_str(aio, "node_name", log_file, sizeof(log_file)); + if (-1 == rv || -2 == rv) { + nlog_error("parse query param `node_name` fail"); NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, { http_response(aio, error_code.error, result_error); - }); - return; - } else if (-2 == rv) { - // handle no param - handle_get_log_files(aio); + }) return; } else if ((size_t) rv >= sizeof(log_file)) { nlog_error("query param overflow, `log_file`:%s", log_file); NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, { http_response(aio, error_code.error, result_error); - }); + }) return; } - void * data = NULL; - size_t len = 0; - nng_http_res *res = NULL; - rv = read_log_file(log_file, &data, &len); + void * data = NULL; + size_t len = 0; + nng_http_res *res = NULL; + char path[128] = { 0 }; + + snprintf(path, sizeof(path), "./logs/%s.log", log_file); + + rv = read_file(path, &data, &len); if (0 != rv) { NEU_JSON_RESPONSE_ERROR( rv, { http_response(aio, error_code.error, result_error); }); return; } - if (((rv = nng_http_res_alloc(&res)) != 0) || ((rv = nng_http_res_set_status(res, NNG_HTTP_STATUS_OK)) != 0) || ((rv = nng_http_res_set_header(res, "Content-Type", "application/octet-stream")) != 0) || + ((rv = nng_http_res_set_header(res, "Content-Disposition", + "attachment; filename=log_file.log")) != + 0) || ((rv = nng_http_res_set_header(res, "Access-Control-Allow-Origin", "*")) != 0) || ((rv = nng_http_res_set_header(res, "Access-Control-Allow-Methods", @@ -104,46 +108,186 @@ void handle_get_log(nng_aio *aio) nng_aio_finish(aio, 0); } -void handle_get_log_files(nng_aio *aio) +void handle_log_level(nng_aio *aio) { - UT_array *log_files = collect_log_files(); - if (NULL == log_files) { - NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, { - http_response(aio, error_code.error, result_error); - }); - return; - } + REST_PROCESS_HTTP_REQUEST_VALIDATE_JWT( + aio, neu_json_update_log_level_req_t, + neu_json_decode_update_log_level_req, { + int rv = 0; + int lev = 0; - size_t len = 1 + strlen("{log_files:[ ]}"); - utarray_foreach(log_files, char **, log_file) - { - // one byte for the comma, - // the last one will be for the terminating NULL byte - len += strlen(*log_file) + 1; - } + char level[NEU_LOG_LEVEL_LEN] = { 0 }; + char node_name[NEU_NODE_NAME_LEN] = { 0 }; - char *res = malloc(len); - if (NULL == res) { - NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, { - http_response(aio, error_code.error, result_error); - }); - utarray_free(log_files); - return; - } + strcpy(level, req->level); + if (strcmp(level, "DEBUG") == 0) { + lev = ZLOG_LEVEL_DEBUG; + } else if (strcmp(level, "INFO") == 0) { + lev = ZLOG_LEVEL_INFO; + } else if (strcmp(level, "WARN") == 0) { + lev = ZLOG_LEVEL_WARN; + } else if (strcmp(level, "ERROR") == 0) { + lev = ZLOG_LEVEL_ERROR; + } else if (strcmp(level, "NOTICE") == 0) { + lev = ZLOG_LEVEL_NOTICE; + } else if (strcmp(level, "FATAL") == 0) { + lev = ZLOG_LEVEL_FATAL; + } else { + free(req); - // with the space char to simplify code - int i = snprintf(res, len, "{log_files:[ "); - utarray_foreach(log_files, char **, log_file) - { - i += snprintf(res + i, len - i, "%s,", *log_file); - } - // will write over the last comma/space - snprintf(res + i - 1, len, "]}"); - http_ok(aio, res); - free(res); - utarray_free(log_files); + NEU_JSON_RESPONSE_ERROR(NEU_ERR_LOG_LEVEL_NOT_EXIST, { + http_response(aio, error_code.error, result_error); + }); + + return; + } + + UT_array *log_files = collect_log_files(); + utarray_foreach(log_files, char **, log_file) + { + if (strncmp(*log_file, req->node_name, strlen(*log_file) - 4) == + 0) { + strcpy(node_name, req->node_name); + } + } + + if (strlen(node_name) == 0) { + utarray_free(log_files); + free(req); + + NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_NOT_EXIST, { + http_response(aio, error_code.error, result_error); + }); + + return; + } + + zlog_category_t *ct = zlog_get_category(node_name); + + rv = zlog_level_switch(ct, lev); + if (rv != 0) { + utarray_free(log_files); + free(req); + + nlog_error("Modify log level fail"); + NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, { + http_response(aio, error_code.error, result_error); + }); + + return; + } + + utarray_free(log_files); + free(req); + + NEU_JSON_RESPONSE_ERROR(NEU_ERR_SUCCESS, { + http_response(aio, NEU_ERR_SUCCESS, result_error); + }); + + return; + }) } +// void handle_get_log(nng_aio *aio) +// { +// int rv = 0; +// char log_file[NEU_NODE_NAME_LEN + 32] = { 0 }; + +// VALIDATE_JWT(aio); + +// // optional params +// rv = http_get_param_str(aio, "log_file", log_file, sizeof(log_file)); +// if (-1 == rv) { +// nlog_error("parse query param `log_file` fail"); +// NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, { +// http_response(aio, error_code.error, result_error); +// }); +// return; +// } else if (-2 == rv) { +// // handle no param +// handle_get_log_files(aio); +// return; +// } else if ((size_t) rv >= sizeof(log_file)) { +// nlog_error("query param overflow, `log_file`:%s", log_file); +// NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, { +// http_response(aio, error_code.error, result_error); +// }); +// return; +// } + +// void * data = NULL; +// size_t len = 0; +// nng_http_res *res = NULL; +// rv = read_log_file(log_file, &data, &len); +// if (0 != rv) { +// NEU_JSON_RESPONSE_ERROR( +// rv, { http_response(aio, error_code.error, result_error); }); +// return; +// } + +// if (((rv = nng_http_res_alloc(&res)) != 0) || +// ((rv = nng_http_res_set_status(res, NNG_HTTP_STATUS_OK)) != 0) || +// ((rv = nng_http_res_set_header(res, "Content-Type", +// "application/octet-stream")) != 0) || +// ((rv = nng_http_res_set_header(res, "Access-Control-Allow-Origin", +// "*")) != 0) || +// ((rv = nng_http_res_set_header(res, "Access-Control-Allow-Methods", +// "POST,GET,PUT,DELETE,OPTIONS")) != 0) +// || +// ((rv = nng_http_res_set_header(res, "Access-Control-Allow-Headers", +// "*")) != 0) || +// ((rv = nng_http_res_copy_data(res, data, len)) != 0)) { +// nng_http_res_free(res); +// free(data); +// nng_aio_finish(aio, rv); +// return; +// } + +// free(data); +// nng_aio_set_output(aio, 0, res); +// nng_aio_finish(aio, 0); +// } + +// void handle_get_log_files(nng_aio *aio) +// { +// UT_array *log_files = collect_log_files(); +// if (NULL == log_files) { +// NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, { +// http_response(aio, error_code.error, result_error); +// }); +// return; +// } + +// size_t len = 1 + strlen("{log_files:[ ]}"); +// utarray_foreach(log_files, char **, log_file) +// { +// // one byte for the comma, +// // the last one will be for the terminating NULL byte +// len += strlen(*log_file) + 1; +// } + +// char *res = malloc(len); +// if (NULL == res) { +// NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, { +// http_response(aio, error_code.error, result_error); +// }); +// utarray_free(log_files); +// return; +// } + +// // with the space char to simplify code +// int i = snprintf(res, len, "{log_files:[ "); +// utarray_foreach(log_files, char **, log_file) +// { +// i += snprintf(res + i, len - i, "%s,", *log_file); +// } +// // will write over the last comma/space +// snprintf(res + i - 1, len, "]}"); +// http_ok(aio, res); +// free(res); +// utarray_free(log_files); +// } + static inline bool ends_with(const char *str, const char *suffix) { size_t m = strlen(str); @@ -175,29 +319,20 @@ UT_array *collect_log_files() return log_files; } -// reads the entire named file, allocating storage to receive the data and -// returning the data and the size in the reference arguments. -int read_log_file(const char *log_file, void **datap, size_t *lenp) +int read_file(const char *file_name, void **datap, size_t *lenp) { int rv = 0; FILE * f; struct stat st; size_t len; void * data; - char name[128] = { 0 }; - if ((size_t) snprintf(name, sizeof(name), "./logs/%s", log_file) >= - sizeof(name)) { - nlog_error("log_file path overflow:%s", name); - return NEU_ERR_EINTERNAL; - } - - if (stat(name, &st) != 0) { + if (stat(file_name, &st) != 0) { return NEU_ERR_FILE_NOT_EXIST; } - if ((f = fopen(name, "rb")) == NULL) { - nlog_error("open fail: %s", name); + if ((f = fopen(file_name, "rb")) == NULL) { + nlog_error("open fail: %s", file_name); return NEU_ERR_EINTERNAL; } @@ -209,6 +344,7 @@ int read_log_file(const char *log_file, void **datap, size_t *lenp) } if (fread(data, 1, len, f) != len) { rv = errno; + nlog_info("rv = %d", rv); free(data); goto done; } diff --git a/plugins/restful/log_handle.h b/plugins/restful/log_handle.h index ca4192ce..f81706ff 100644 --- a/plugins/restful/log_handle.h +++ b/plugins/restful/log_handle.h @@ -23,5 +23,7 @@ #include void handle_get_log(nng_aio *aio); +void handle_download_log(nng_aio *aio); +void handle_log_level(nng_aio *aio); #endif diff --git a/src/parser/neu_json_log.c b/src/parser/neu_json_log.c index 70d3a268..d35897f2 100644 --- a/src/parser/neu_json_log.c +++ b/src/parser/neu_json_log.c @@ -28,6 +28,59 @@ #include "json/json.h" #include "neu_json_log.h" +#include "utils/log.h" + +int neu_json_decode_update_log_level_req( + char *buf, neu_json_update_log_level_req_t **result) +{ + int ret = 0; + void *json_obj = neu_json_decode_new(buf); + + neu_json_update_log_level_req_t *req = + calloc(1, sizeof(neu_json_update_log_level_req_t)); + if (req == NULL) { + return -1; + } + + neu_json_elem_t req_elems[] = { { + .name = "level", + .t = NEU_JSON_STR, + }, + { + .name = "node_name", + .t = NEU_JSON_STR, + } }; + + ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems), + req_elems); + if (ret != 0) { + goto decode_fail; + } + + req->level = req_elems[0].v.val_str; + req->node_name = req_elems[1].v.val_str; + + *result = req; + goto decode_exit; + +decode_fail: + free(req); + ret = -1; +decode_exit: + if (json_obj != NULL) { + neu_json_decode_free(json_obj); + } + return ret; +} + +void neu_json_decode_update_log_level_req_free( + neu_json_update_log_level_req_t *req) +{ + free(req->level); + free(req->node_name); + + free(req); +} int neu_json_encode_get_log_resp(void *json_object, void *param) { diff --git a/src/parser/neu_json_log.h b/src/parser/neu_json_log.h index 85a4e192..3ba6caf5 100644 --- a/src/parser/neu_json_log.h +++ b/src/parser/neu_json_log.h @@ -33,6 +33,11 @@ extern "C" { typedef char *neu_json_get_log_resp_row_t; +typedef struct { + char *level; + char *node_name; +} neu_json_update_log_level_req_t; + typedef struct { int64_t page_count; int n_row; @@ -40,6 +45,11 @@ typedef struct { int64_t error; } neu_json_get_log_resp_t; +void neu_json_decode_update_log_level_req_free( + neu_json_update_log_level_req_t *req); +int neu_json_decode_update_log_level_req( + char *buf, neu_json_update_log_level_req_t **result); + int neu_json_encode_get_log_resp(void *json_object, void *param); #ifdef __cplusplus diff --git a/zlog.conf b/zlog.conf index 8af6bab4..66e7dd48 100644 --- a/zlog.conf +++ b/zlog.conf @@ -1,6 +1,5 @@ [global] -reload conf period = 60 file perms = 666 [formats]