diff --git a/include/neuron/adapter.h b/include/neuron/adapter.h index 1b88121b..2a695364 100644 --- a/include/neuron/adapter.h +++ b/include/neuron/adapter.h @@ -37,6 +37,7 @@ extern "C" { typedef struct { neu_node_running_state_e running; neu_node_link_state_e link; + char log_level[NEU_LOG_LEVEL_LEN]; } neu_node_state_t; typedef enum neu_reqresp_type { @@ -1050,6 +1051,7 @@ typedef struct { typedef struct neu_req_update_log_level { char node[NEU_NODE_NAME_LEN]; + char log_level[NEU_LOG_LEVEL_LEN]; } neu_req_update_log_level_t; void neu_msg_gen(neu_reqresp_head_t *header, void *data); diff --git a/include/neuron/define.h b/include/neuron/define.h index 605c9678..5bdc1bb4 100644 --- a/include/neuron/define.h +++ b/include/neuron/define.h @@ -44,6 +44,13 @@ #define NEU_TAG_META_LENGTH 20 #define NEU_TAG_META_SIZE 3 +#define NEU_LOG_LEVEL_DEBUG "debug" +#define NEU_LOG_LEVEL_INFO "info" +#define NEU_LOG_LEVEL_NOTICE "notice" +#define NEU_LOG_LEVEL_WARN "warn" +#define NEU_LOG_LEVEL_ERROR "error" +#define NEU_LOG_LEVEL_FATAL "fatal" + extern int default_log_level; extern bool disable_jwt; extern char host_port[32]; diff --git a/include/neuron/plugin.h b/include/neuron/plugin.h index f7875221..be784bcf 100644 --- a/include/neuron/plugin.h +++ b/include/neuron/plugin.h @@ -46,6 +46,7 @@ typedef struct neu_plugin_common { char name[NEU_NODE_NAME_LEN]; neu_node_link_state_e link_state; + char log_level[NEU_LOG_LEVEL_LEN]; zlog_category_t *log; } neu_plugin_common_t; diff --git a/plugins/restful/adapter_handle.c b/plugins/restful/adapter_handle.c index be5cf5d3..8ce716d1 100644 --- a/plugins/restful/adapter_handle.c +++ b/plugins/restful/adapter_handle.c @@ -330,9 +330,10 @@ void handle_get_node_state_resp(nng_aio *aio, neu_resp_get_node_state_t *state) neu_json_get_node_state_resp_t res = { 0 }; char * result = NULL; - res.link = state->state.link; - res.running = state->state.running; - res.rtt = state->rtt; + res.link = state->state.link; + res.running = state->state.running; + res.rtt = state->rtt; + res.log_level = state->state.log_level; neu_json_encode_by_fn(&res, neu_json_encode_get_node_state_resp, &result); @@ -357,6 +358,7 @@ void handle_get_nodes_state_resp(nng_aio * aio, states_res.nodes[index].running = state->state.running; states_res.nodes[index].link = state->state.link; states_res.nodes[index].rtt = state->rtt; + states_res.nodes[index].log_level = state->state.log_level; } neu_json_encode_by_fn(&states_res, neu_json_encode_get_nodes_state_resp, diff --git a/plugins/restful/log_handle.c b/plugins/restful/log_handle.c index 2b30231b..99fc1179 100644 --- a/plugins/restful/log_handle.c +++ b/plugins/restful/log_handle.c @@ -117,43 +117,72 @@ void handle_log_level(nng_aio *aio) neu_reqresp_head_t header = { 0 }; neu_req_update_log_level_t cmd = { 0 }; zlog_category_t * ct = zlog_get_category(req->node_name); - zlog_category_t * neuron = zlog_get_category("neuron"); - int log_level = ZLOG_LEVEL_NOTICE; + zlog_category_t * neuron = zlog_get_category("neuron"); + int log_level; + static int log_level_neuron = 60; - if (0 == strcmp(req->log_level, "debug")) { + if (0 == strcmp(req->log_level, NEU_LOG_LEVEL_DEBUG)) { log_level = ZLOG_LEVEL_DEBUG; - } else if (0 == strcmp(req->log_level, "info")) { + strcpy(cmd.log_level, NEU_LOG_LEVEL_DEBUG); + } else if (0 == strcmp(req->log_level, NEU_LOG_LEVEL_INFO)) { log_level = ZLOG_LEVEL_INFO; + strcpy(cmd.log_level, NEU_LOG_LEVEL_INFO); + } else if (0 == strcmp(req->log_level, NEU_LOG_LEVEL_NOTICE)) { + log_level = ZLOG_LEVEL_NOTICE; + strcpy(cmd.log_level, NEU_LOG_LEVEL_NOTICE); + } else if (0 == strcmp(req->log_level, NEU_LOG_LEVEL_WARN)) { + log_level = ZLOG_LEVEL_WARN; + strcpy(cmd.log_level, NEU_LOG_LEVEL_WARN); + } else if (0 == strcmp(req->log_level, NEU_LOG_LEVEL_ERROR)) { + log_level = ZLOG_LEVEL_ERROR; + strcpy(cmd.log_level, NEU_LOG_LEVEL_ERROR); + } else if (0 == strcmp(req->log_level, NEU_LOG_LEVEL_FATAL)) { + log_level = ZLOG_LEVEL_FATAL; + strcpy(cmd.log_level, NEU_LOG_LEVEL_FATAL); + } else { + strcpy(cmd.log_level, "unknow"); + nlog_error( + "Failed to modify log_level of the node, node_name:%s, ", + req->node_name); + NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, { + neu_http_response(aio, error_code.error, result_error); + }); + goto end; } ret = zlog_level_switch(ct, log_level); if (ret != 0) { - nlog_error("Modify node log level fail, node_name:%s, " - "ret: %d,", - req->node_name, ret); + nlog_error( + "Failed to modify log_level of the node, node_name:%s, " + "ret: %d,", + req->node_name, ret); NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, { neu_http_response(aio, error_code.error, result_error); }); } - ret = zlog_level_switch(neuron, log_level); + if (log_level > log_level_neuron) { + log_level_neuron = log_level; + } + + ret = zlog_level_switch(neuron, log_level_neuron); if (ret != 0) { - nlog_error("Modify neuron log level fail, ret: %d", ret); + nlog_error("Failed to modify log_level of the node, ret: %d", + ret); NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, { neu_http_response(aio, error_code.error, result_error); }); } - header.ctx = aio; header.type = NEU_REQ_UPDATE_LOG_LEVEL; strcpy(cmd.node, req->node_name); - ret = neu_plugin_op(plugin, header, &cmd); if (ret != 0) { NEU_JSON_RESPONSE_ERROR(NEU_ERR_IS_BUSY, { neu_http_response(aio, NEU_ERR_IS_BUSY, result_error); }); } + end:; }) } diff --git a/src/adapter/adapter.c b/src/adapter/adapter.c index f43cf5c1..af3c4688 100644 --- a/src/adapter/adapter.c +++ b/src/adapter/adapter.c @@ -1328,6 +1328,7 @@ neu_node_state_t neu_adapter_get_state(neu_adapter_t *adapter) state.link = common->link_state; state.running = adapter->state; + strcpy(state.log_level, common->log_level); return state; } diff --git a/src/adapter/adapter_internal.h b/src/adapter/adapter_internal.h index 2ff2d75d..d9616b0e 100644 --- a/src/adapter/adapter_internal.h +++ b/src/adapter/adapter_internal.h @@ -33,6 +33,8 @@ struct neu_adapter { neu_node_running_state_e state; + char log_level[NEU_LOG_LEVEL_LEN]; + adapter_callbacks_t cb_funs; void * handle; diff --git a/src/base/neu_plugin_common.c b/src/base/neu_plugin_common.c index 5616d05e..781b61a7 100644 --- a/src/base/neu_plugin_common.c +++ b/src/base/neu_plugin_common.c @@ -31,6 +31,7 @@ void neu_plugin_common_init(neu_plugin_common_t *common) { common->magic = NEU_PLUGIN_MAGIC_NUMBER; common->link_state = NEU_NODE_LINK_STATE_DISCONNECTED; + strcpy(common->log_level, NEU_LOG_LEVEL_NOTICE); } bool neu_plugin_common_check(neu_plugin_t *plugin) diff --git a/src/core/manager.c b/src/core/manager.c index 7eb08ff2..ba805734 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1175,12 +1175,13 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) break; } + case NEU_REQ_GET_GROUP: case NEU_REQ_GET_NODE_SETTING: + case NEU_REQ_GET_NODE_STATE: case NEU_REQ_READ_GROUP: case NEU_REQ_WRITE_TAG: case NEU_REQ_WRITE_TAGS: - case NEU_REQ_GET_NODE_STATE: case NEU_REQ_GET_TAG: case NEU_REQ_GET_NDRIVER_TAGS: case NEU_REQ_NODE_CTL: @@ -1460,9 +1461,12 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) neu_msg_exchange(header); reply(manager, header, &e); } else { - struct timeval tv = { 0 }; - gettimeofday(&tv, NULL); - manager->timestamp_lev_manager = tv.tv_sec; + neu_req_update_log_level_t *cmd = + (neu_req_update_log_level_t *) &header[1]; + neu_adapter_t *adapter_log = + neu_node_manager_find(manager->node_manager, header->receiver); + strcpy(neu_plugin_to_plugin_common(adapter_log->plugin)->log_level, + cmd->log_level); forward_msg(manager, header, header->receiver); } diff --git a/src/core/node_manager.c b/src/core/node_manager.c index 889f6525..78ecca9f 100644 --- a/src/core/node_manager.c +++ b/src/core/node_manager.c @@ -414,6 +414,8 @@ UT_array *neu_node_manager_get_state(neu_node_manager_t *mgr) state.state.running = el->adapter->state; state.state.link = neu_plugin_to_plugin_common(el->adapter->plugin)->link_state; + strcpy(state.state.log_level, + neu_plugin_to_plugin_common(el->adapter->plugin)->log_level); neu_metric_entry_t *e = NULL; if (NULL != el->adapter->metrics) { HASH_FIND_STR(el->adapter->metrics->entries, diff --git a/src/parser/neu_json_log.c b/src/parser/neu_json_log.c index d1114d57..d354e742 100644 --- a/src/parser/neu_json_log.c +++ b/src/parser/neu_json_log.c @@ -76,10 +76,15 @@ decode_exit: void neu_json_decode_update_log_level_req_free( neu_json_update_log_level_req_t *req) { - free(req->node_name); - free(req->log_level); - - free(req); + if (req->node_name != NULL) { + free(req->node_name); + } + if (req->log_level != NULL) { + free(req->log_level); + } + if (req != NULL) { + free(req); + } } int neu_json_encode_get_log_resp(void *json_object, void *param) diff --git a/src/parser/neu_json_node.c b/src/parser/neu_json_node.c index 6c9c295d..ca137ccb 100644 --- a/src/parser/neu_json_node.c +++ b/src/parser/neu_json_node.c @@ -184,6 +184,11 @@ int neu_json_encode_get_node_state_resp(void *json_object, void *param) .name = "rtt", .t = NEU_JSON_INT, .v.val_int = resp->rtt, + }, + { + .name = "log_level", + .t = NEU_JSON_STR, + .v.val_str = resp->log_level, } }; ret = neu_json_encode_field(json_object, resp_elems, NEU_JSON_ELEM_SIZE(resp_elems)); @@ -200,28 +205,31 @@ int neu_json_encode_get_nodes_state_resp(void *json_object, void *param) void * node_array = neu_json_array(); neu_json_get_nodes_state_t *p_node = resp->nodes; for (int i = 0; i < resp->n_node; i++) { - neu_json_elem_t node_elems[] = { - { - .name = "node", - .t = NEU_JSON_STR, - .v.val_str = p_node->name, - }, - { - .name = "running", - .t = NEU_JSON_INT, - .v.val_int = p_node->running, - }, - { - .name = "link", - .t = NEU_JSON_INT, - .v.val_int = p_node->link, - }, - { - .name = "rtt", - .t = NEU_JSON_INT, - .v.val_int = p_node->rtt, - }, - }; + neu_json_elem_t node_elems[] = { { + .name = "node", + .t = NEU_JSON_STR, + .v.val_str = p_node->name, + }, + { + .name = "running", + .t = NEU_JSON_INT, + .v.val_int = p_node->running, + }, + { + .name = "link", + .t = NEU_JSON_INT, + .v.val_int = p_node->link, + }, + { + .name = "rtt", + .t = NEU_JSON_INT, + .v.val_int = p_node->rtt, + }, + { + .name = "log_level", + .t = NEU_JSON_STR, + .v.val_str = p_node->log_level, + } }; node_array = neu_json_encode_array(node_array, node_elems, NEU_JSON_ELEM_SIZE(node_elems)); p_node++; diff --git a/src/parser/neu_json_node.h b/src/parser/neu_json_node.h index 12273040..1476315f 100644 --- a/src/parser/neu_json_node.h +++ b/src/parser/neu_json_node.h @@ -25,6 +25,7 @@ #ifndef _NEU_JSON_API_NEU_JSON_NODE_H_ #define _NEU_JSON_API_NEU_JSON_NODE_H_ +#include "define.h" #include "json/json.h" #ifdef __cplusplus @@ -52,6 +53,7 @@ typedef struct { int64_t running; int64_t link; int64_t rtt; + char * log_level; } neu_json_get_node_state_resp_t; int neu_json_encode_get_node_state_resp(void *json_object, void *param); @@ -61,6 +63,7 @@ typedef struct { int64_t running; int64_t link; int64_t rtt; + char * log_level; } neu_json_get_nodes_state_t; typedef struct { diff --git a/tests/ft/log/__init__.py b/tests/ft/log/__init__.py new file mode 100644 index 00000000..6da12771 --- /dev/null +++ b/tests/ft/log/__init__.py @@ -0,0 +1,3 @@ +import sys + +sys.path.append("tests/ft") diff --git a/tests/ft/log/test_log.py b/tests/ft/log/test_log.py new file mode 100644 index 00000000..185f2c99 --- /dev/null +++ b/tests/ft/log/test_log.py @@ -0,0 +1,78 @@ +import os +import sys + +import neuron.api as api +from neuron.config import * +from neuron.common import * + + +class TestLog: + + @description(given="node", when="change log_level to debug", then="change success") + def test_change_log_level_debug(self): + response = api.add_node(node='modbus-tcp-1', plugin=modbus_tcp) + assert 200 == response.status_code + assert 0 == response.json()['error'] + + response = api.change_log_level(node='modbus-tcp-1', level='debug') + assert 200 == response.status_code + assert 0 == response.json()['error'] + + response = api.get_nodes_state() + assert 200 == response.status_code + assert "debug" == response.json()['states'][0]['log_level'] + + @description(given="node", when="change log_level to info", then="change success") + def test_change_log_level_info(self): + response = api.change_log_level(node='modbus-tcp-1', level='info') + assert 0 == response.json()['error'] + + response = api.get_nodes_state() + assert 200 == response.status_code + assert "info" == response.json()['states'][0]['log_level'] + + @description(given="node", when="change log_level to warn", then="change success") + def test_change_log_level_warn(self): + response = api.change_log_level(node='modbus-tcp-1', level='warn') + assert 0 == response.json()['error'] + + response = api.get_nodes_state() + assert 200 == response.status_code + assert "warn" == response.json()['states'][0]['log_level'] + + @description(given="node", when="change log_level to error", then="change success") + def test_change_log_level_error(self): + response = api.change_log_level(node='modbus-tcp-1', level='error') + assert 0 == response.json()['error'] + + response = api.get_nodes_state() + assert 200 == response.status_code + assert "error" == response.json()['states'][0]['log_level'] + + @description(given="node", when="change log_level to fatal", then="change success") + def test_change_log_level_fatal(self): + response = api.change_log_level(node='modbus-tcp-1', level='fatal') + assert 0 == response.json()['error'] + + response = api.get_nodes_state() + assert 200 == response.status_code + assert "fatal" == response.json()['states'][0]['log_level'] + + @description(given="node", when="change log_level to notice", then="change success") + def test_change_log_level_notice(self): + response = api.change_log_level(node='modbus-tcp-1', level='notice') + assert 0 == response.json()['error'] + + response = api.get_nodes_state() + assert 200 == response.status_code + assert "notice" == response.json()['states'][0]['log_level'] + + @description(given="node", when="change log_level to invalid level", then="change failed") + def test_change_log_level_invalid(self): + response = api.change_log_level(node='modbus-tcp-1', level='invalid') + assert 400 == response.status_code + assert 1003 == response.json()['error'] + + response = api.get_nodes_state() + assert 200 == response.status_code + assert "notice" == response.json()['states'][0]['log_level'] \ No newline at end of file diff --git a/tests/ft/neuron/api.py b/tests/ft/neuron/api.py index 25b27359..fd293524 100644 --- a/tests/ft/neuron/api.py +++ b/tests/ft/neuron/api.py @@ -16,3 +16,12 @@ def get_license(jwt=config.default_jwt): def upload_license(license, jwt=config.default_jwt): return requests.post(url=config.BASE_URL + '/api/v2/license', headers={"Authorization": jwt}, json={"license": license}) + +def change_log_level(node, level, jwt=config.default_jwt): + return requests.put(url=config.BASE_URL + '/api/v2/log/level', headers={"Authorization": jwt}, json={"node": node, "level": level}) + +def add_node(node, plugin, jwt=config.default_jwt): + return requests.post(url=config.BASE_URL + '/api/v2/node', headers={"Authorization": jwt}, json={"name": node, "plugin": plugin}) + +def get_nodes_state(jwt=config.default_jwt): + return requests.get(url=config.BASE_URL + '/api/v2/node/state', headers={"Authorization": jwt}) diff --git a/tests/ft/neuron/config.py b/tests/ft/neuron/config.py index 569fe6d1..78e172ca 100644 --- a/tests/ft/neuron/config.py +++ b/tests/ft/neuron/config.py @@ -1,2 +1,3 @@ BASE_URL = "http://127.0.0.1:7000" default_jwt = "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJuZXVyb24iLCJib2R5RW5jb2RlIjowLCJleHAiOjE3MTc4MzUyNjIsImlhdCI6MTY4NjI5OTI2MiwiaXNzIjoibmV1cm9uIn0.tJ8Z12S5lzzyqvJedFr11w0FmQ8l9inFHuzcuA_3uKoNBJSZlQAkcWyDAO2XELyuZFtHPrk8pJFgEqTzwphtBAybVXbeHBRWED5XuHvYqznzwk1J2UCOuw_ehmHnLszZHRAcMoeLWIRIx0KM7BXvZAyFA2J1axJntxfRrurNMn6MbfnGGtza6hJfuQ6pG-h4cHhrBOogf8nw3f8RLfd16vbWobhEp8KBUrwsfn2Iv4hMXvZMekv_PXboLO74f7oMkEL6pdM9g6G6rDBTjTU8FssfeyJcAP7Xb9mS2G7GNW5B8twtPVLtpOQH9VNbLa4OQxdMb0WN86i9vSDiCOoN6A" +modbus_tcp = "Modbus TCP" \ No newline at end of file