add new qloud-c-sdk component

This commit is contained in:
mculover666
2022-03-25 10:06:56 +08:00
parent 565cd29e94
commit a3ac2e56d8
166 changed files with 35027 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
file(GLOB src_log_upload ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc_log_upload ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_services ${src_services} ${src_log_upload} PARENT_SCOPE)
set(inc_services ${inc_services} ${inc_log_upload} PARENT_SCOPE)
file(GLOB src_log_upload_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/log_upload_sample.c)
add_executable(log_upload_sample ${src_log_upload_sample})
target_link_libraries(log_upload_sample ${libsdk})
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
endif()

View File

@@ -0,0 +1,215 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client.h
* @brief mqtt client internel api
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-31
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_log_upload.h"
#include "utils_list.h"
#include "utils_base64.h"
#include "utils_hmac.h"
/**
* @brief To check log http server return msg or not
*
*/
#define LOG_CHECK_HTTP_RET_CODE
/**
* @brief enable log upload debug or not
*
*/
// #define LOG_UPLOAD_DEBUG
/**
* @brief http recv length
*
*/
#define HTTP_RET_JSON_LENGTH 256
/**
* @brief wait http recv time out
*
*/
#define HTTP_WAIT_RET_TIMEOUT_MS 1000
/**
* @brief do immediate log update if buffer is lower than this threshold (about two max log item)
*
*/
#define LOG_LOW_BUFFER_THRESHOLD (LOG_UPLOAD_BUFFER_SIZE / 4)
/**
* @brief sing key length
*
*/
#define SIGN_KEY_SIZE (24)
#ifdef LOG_UPLOAD_DEBUG
#define UPLOAD_DBG(fmt, ...) HAL_Printf(">>LOG-DBG>>%s(%d): " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define UPLOAD_DBG(...)
#endif
#define UPLOAD_ERR(fmt, ...) HAL_Printf(">>LOG-ERR>>%s(%d): " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
/**
* @brief clear log upload buffer
*
*/
void log_upload_clear_buffer(void);
/**
* @brief set log upload in comm err state
*
* @param state true or false
*/
void log_upload_set_upload_log_in_comm_err(bool state);
/**
* @brief change log upload level
*
* @param level
*/
void log_upload_set_log_upload_level(LogLevel level);
/**
* @brief get log upload level
*
* @return LogLevel
*/
LogLevel log_upload_get_log_upload_level(void);
/**
* @brief log upload buffer init
*
* @param init_params @see LogUploadInitParams
* @return void* if success return @see LogUploadBuffer
*/
void *log_upload_buffer_init(LogUploadInitParams *init_params);
/**
* @brief deinit log buffer handle
*
* @param upload_buffer_handle
*/
void log_upload_buffer_deinit(void *upload_buffer_handle);
/**
* @brief Get the log buffer write index object
*
* @param log_upload_buffer_handle
* @return uint32_t
*/
uint32_t get_log_buffer_write_index(void *log_upload_buffer_handle);
/**
* @brief Get the log buffer head len object
*
* @param log_upload_buffer_handle
* @return uint32_t
*/
uint32_t get_log_buffer_head_len(void *log_upload_buffer_handle);
/**
* @brief check log buffer is empty or not
*
* @param log_upload_buffer_handle
* @return true empty
* @return false not empty
*/
bool have_data_need_update(void *log_upload_buffer_handle);
/**
* @brief When a new log appears during the reporting period, move the log to the specified position in the buffer
*
*/
void remove_log_from_buffer(void *log_upload_buffer_handle, uint32_t need_remove_size);
/**
* @brief copy log buffer head
*
* @param[out] dst Destination address
* @param log_upload_buffer_handle
*/
void copy_log_buffer_header(char *dst, void *log_upload_buffer_handle);
/**
* @brief lock log buffer
*
*/
void log_buffer_lock(void);
/**
* @brief unlock log buffer
*
*/
void log_buffer_unlock(void);
/**
* @brief post data to tencent cloud over http
*
* @param[in] post_buf need post data buffer
* @param[in] post_size need post data buffer length
* @return @see IotReturnCode
*/
int post_one_http_to_server(void *handle, char *post_buf, size_t post_size);
/**
* @brief Get the log buffer object
*
* @param log_upload_buffer_handle
* @return char*
*/
char *get_log_buffer(void *log_upload_buffer_handle);
/**
* @brief push data to log buffer
*
* @param[in] log_content need push data
* @return int if 0 success else QCLOUD_ERR_FAILURE
*/
int append_data_to_log_buffer(const char *log_content);
/**
* @brief init log upload module
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int log_upload_init(void *client);
/**
* @brief Get the log buffer size object
*
* @return uint32_t log buffer size
*/
uint32_t get_log_buffer_size(void);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_

View File

@@ -0,0 +1,220 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file broadcast_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "utils_log.h"
/**
* @brief MQTT event callback, @see MQTTEventHandleFun
*
* @param[in] client pointer to mqtt client
* @param[in] handle_context context
* @param[in] msg msg
*/
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
{
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
uintptr_t packet_id = (uintptr_t)msg->msg;
switch (msg->event_type) {
case MQTT_EVENT_UNDEF:
Log_i("undefined event occur.");
break;
case MQTT_EVENT_DISCONNECT:
Log_i("MQTT disconnect.");
break;
case MQTT_EVENT_RECONNECT:
Log_i("MQTT reconnect.");
break;
case MQTT_EVENT_PUBLISH_RECEIVED:
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
break;
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_NACK:
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_NACK:
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_SUCCESS:
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_TIMEOUT:
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_NACK:
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
break;
default:
Log_i("Should NOT arrive here.");
break;
}
}
/**
* @brief Setup MQTT construct parameters.
*
* @param[in,out] initParams @see MQTTInitParams
* @param[in] device_info @see DeviceInfo
*/
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_info = device_info;
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
init_params->auto_connect_enable = 1;
init_params->event_handle.h_fp = _mqtt_event_handler;
init_params->event_handle.context = NULL;
}
static void _setup_log_upload_init_params(LogUploadInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_name = device_info->device_name;
init_params->product_id = device_info->product_id;
init_params->sign_key = device_info->device_secret;
init_params->log_buffer_size = LOG_UPLOAD_BUFFER_SIZE;
init_params->save_log_filename = "./tmp/upload-fail-save.log";
init_params->read_func = HAL_File_Read;
init_params->save_func = HAL_File_Save;
init_params->del_func = HAL_File_Del;
init_params->get_size_func = HAL_File_Get_Size;
}
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc;
int loop_cnt = 5;
// init log level
LogHandleFunc func = {0};
func.log_malloc = HAL_Malloc;
func.log_free = HAL_Free;
func.log_get_current_time_str = HAL_Timer_Current;
func.log_printf = HAL_Printf;
func.log_upload = IOT_Log_Upload_AppendToUploadBuffer;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
DeviceInfo device_info;
rc = HAL_GetDevInfo((void *)&device_info);
if (rc) {
Log_e("get device info failed: %d", rc);
return rc;
}
// init connection
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
_setup_connect_init_params(&init_params, &device_info);
// init log upload
LogUploadInitParams log_upload_init_params = DEFAULT_LOG_UPLOAD_INIT_PARAMS;
_setup_log_upload_init_params(&log_upload_init_params, &device_info);
IOT_Log_Upload_InitPre(&log_upload_init_params);
// create MQTT client and connect with server
void *client = IOT_MQTT_Construct(&init_params);
if (client) {
Log_i("Cloud Device Construct Success");
rc = IOT_Log_Upload_Init(client);
if (rc) {
Log_e("Log upload init failed: %d", rc);
}
IOT_Log_Upload(true);
} else {
Log_e("MQTT Construct failed!");
return QCLOUD_ERR_FAILURE;
}
do {
Log_d("log upload test debug %d...", loop_cnt);
Log_i("log upload test info %d...", loop_cnt);
Log_w("log upload test waring %d...", loop_cnt);
Log_e("log upload test error %d...", loop_cnt);
rc = IOT_MQTT_Yield(client, 200);
IOT_Log_Upload(rc ? true : false);
} while (loop_cnt--);
// If you have to report some urgent messages, you can call this function
IOT_Log_Upload(true);
rc = IOT_MQTT_Destroy(&client);
IOT_Log_Upload_Deinit();
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,741 @@
/**
* @file log_buffer.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-01-23
*
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @par Change Log:
* <table>
* Date Version Author Description
* 2022-01-23 1.0 hubertxxu first commit
* </table>
*/
#include "log_upload.h"
typedef struct {
void *lock_buf;
char *log_buffer;
const char *host;
uint32_t log_buffer_size;
uint32_t log_buffer_head_len;
uint32_t write_index;
char sign_key[SIGN_KEY_SIZE + 1];
} LogUploadBuffer;
static LogUploadBuffer *sg_log_upload_buffer_handle = NULL;
// ----------------------------------------------------------------------------
// Common
// ----------------------------------------------------------------------------
/**
* @brief reset log upload buffer
*
* @param[in,out] log_upload_buffer_handle handle of log upload buffer
*/
static void _reset_log_buffer(LogUploadBuffer *log_upload_buffer_handle)
{
log_upload_buffer_handle->write_index = log_upload_buffer_handle->log_buffer_head_len;
memset(log_upload_buffer_handle->log_buffer + log_upload_buffer_handle->log_buffer_head_len, 0,
log_upload_buffer_handle->log_buffer_size - log_upload_buffer_handle->log_buffer_head_len);
}
static LogUploadBuffer *_log_upload_buffer_init_pre(LogUploadInitParams *init_params)
{
LogUploadBuffer *handle = (LogUploadBuffer *)HAL_Malloc(sizeof(LogUploadBuffer));
if (!handle) {
UPLOAD_ERR("log upload buffer handle malloc error.");
goto err_exit;
}
memset(handle, 0, sizeof(LogUploadBuffer));
handle->lock_buf = HAL_MutexCreate();
if (!handle->lock_buf) {
UPLOAD_ERR("mutex create failed");
goto err_exit;
}
handle->log_buffer_size = init_params->log_buffer_size;
handle->log_buffer = HAL_Malloc(handle->log_buffer_size);
if (!handle->log_buffer) {
UPLOAD_ERR("malloc log buffer failed");
goto err_exit;
}
memset(handle->log_buffer, 0, handle->log_buffer_size);
/*init sign key*/
int key_len = strlen(init_params->sign_key);
if (!key_len) {
UPLOAD_ERR("invalid sign key length");
goto err_exit;
}
memcpy(handle->sign_key, init_params->sign_key, key_len > SIGN_KEY_SIZE ? SIGN_KEY_SIZE : key_len);
sg_log_upload_buffer_handle = handle;
return handle;
err_exit:
if (handle) {
HAL_MutexDestroy(handle->lock_buf);
HAL_Free(handle);
}
return NULL;
}
/**
* @brief deinit log buffer handle
*
* @param upload_buffer_handle
*/
void log_upload_buffer_deinit(void *upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)upload_buffer_handle;
if (!handle) {
return;
}
HAL_MutexLock(handle->lock_buf);
_reset_log_buffer(handle);
HAL_Free(handle->log_buffer);
handle->log_buffer = NULL;
HAL_MutexUnlock(handle->lock_buf);
HAL_MutexDestroy(handle->lock_buf);
handle->lock_buf = NULL;
HAL_Free(handle);
sg_log_upload_buffer_handle = NULL;
}
/**
* @brief Get the log buffer write index object
*
* @param log_upload_buffer_handle
* @return uint32_t
*/
uint32_t get_log_buffer_write_index(void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
HAL_MutexLock(handle->lock_buf);
uint32_t write_index = handle->write_index;
HAL_MutexUnlock(handle->lock_buf);
return write_index;
}
/**
* @brief Get the log buffer object
*
* @param log_upload_buffer_handle
* @return char*
*/
char *get_log_buffer(void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
return handle->log_buffer;
}
/**
* @brief Get the log buffer head len object
*
* @param log_upload_buffer_handle
* @return uint32_t
*/
uint32_t get_log_buffer_head_len(void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
return handle->log_buffer_head_len;
}
/**
* @brief check log buffer is empty or not
*
* @param log_upload_buffer_handle
* @return true empty
* @return false not empty
*/
bool have_data_need_update(void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
return handle->write_index == handle->log_buffer_head_len;
}
/**
* @brief When a new log appears during the reporting period, move the log to the specified position in the buffer
*
*/
void remove_log_from_buffer(void *log_upload_buffer_handle, uint32_t need_remove_size)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
if (need_remove_size > handle->write_index || need_remove_size > handle->log_buffer_size) {
return;
}
HAL_MutexLock(handle->lock_buf);
memmove(handle->log_buffer + handle->log_buffer_head_len, handle->log_buffer + need_remove_size,
handle->write_index - need_remove_size);
handle->write_index = handle->write_index - need_remove_size + handle->log_buffer_head_len;
memset(handle->log_buffer + handle->write_index, 0, handle->log_buffer_size - handle->write_index);
HAL_MutexUnlock(handle->lock_buf);
}
/**
* @brief copy log buffer head
*
* @param[out] dst Destination address
* @param log_upload_buffer_handle
*/
void copy_log_buffer_header(char *dst, void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
memcpy(dst, handle->log_buffer, handle->log_buffer_head_len);
}
/**
* @brief clear log upload buffer
*
*/
void log_upload_clear_buffer(void)
{
if (!sg_log_upload_buffer_handle) {
UPLOAD_ERR("log upload buffer not init.");
return;
}
HAL_MutexLock(sg_log_upload_buffer_handle->lock_buf);
_reset_log_buffer(sg_log_upload_buffer_handle);
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
}
/**
* @brief Get the log buffer size object
*
* @return uint32_t log buffer size
*/
uint32_t get_log_buffer_size(void)
{
if (!sg_log_upload_buffer_handle) {
UPLOAD_ERR("log upload buffer not init.");
return 0;
}
return sg_log_upload_buffer_handle->log_buffer_size;
}
/**
* @brief
*
*/
void log_buffer_lock(void)
{
HAL_MutexLock(sg_log_upload_buffer_handle->lock_buf);
}
/**
* @brief
*
*/
void log_buffer_unlock(void)
{
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
}
// ----------------------------------------------------------------------------
// New log fromat by json
// ----------------------------------------------------------------------------
#ifdef LOG_UPLOAD_TYPE_JSON
/**
* @brief log upload buffer init
*
* @param init_params @see LogUploadInitParams
* @return void* if success return @see LogUploadBuffer
*/
void *log_upload_buffer_init(LogUploadInitParams *init_params)
{
LogUploadBuffer *handle = _log_upload_buffer_init_pre(init_params);
if (!handle) {
return NULL;
}
handle->log_buffer_head_len = HAL_Snprintf(handle->log_buffer, handle->log_buffer_size,
"{\"DeviceName\":\"%s\",\"ProductId\":\"%s\",\"Message\":[",
init_params->device_name, init_params->product_id);
handle->write_index = handle->log_buffer_head_len;
handle->host = init_params->host;
_reset_log_buffer(handle);
return handle;
}
#ifdef LOG_CHECK_HTTP_RET_CODE
/**
* @brief get http respose result
*
* @param[in] json http response buffer
* @param[in] json_len http response buffer length
* @return true log report success
* @return false log report error
*/
static bool _get_json_ret_code(char *json, size_t json_len)
{
int rc = 0;
UtilsJsonValue resposse;
// get response
rc = utils_json_value_get("Response.Error", strlen("Response.Error"), json, json_len, &resposse);
if (rc) {
return true;
}
return false;
}
#endif
/**
* @brief when upload. append endchar in the upload buffer ending
*
* @param[in,out] log_buf upload buffer
* @param[in] log_size upload buffer index
*/
static void _append_endchar_to_upload_buffer(char *log_buf, size_t log_size)
{
log_buf[log_size - 2] = ']';
log_buf[log_size - 1] = '}';
}
/**
* @brief when upload. remove endchar in the upload buffer ending
*
* @param[in,out] log_buf upload buffer
* @param[in] log_size upload buffer index
*/
static void _remove_endchar_from_upload_buffer(char *log_buf, size_t log_size)
{
log_buf[log_size - 2] = ',';
log_buf[log_size - 1] = ' ';
}
/**
* @brief post data to tencent cloud over http
*
* @param[in] post_buf need post data buffer
* @param[in] post_size need post data buffer length
* @return @see IotReturnCode
*/
int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size)
{
int rc = 0;
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
if (!post_size) {
return 0;
}
_append_endchar_to_upload_buffer(post_buf, post_size);
HttpSignedParams params = {
.host = handle->host ? handle->host : LOG_UPLOAD_SERVER_URL,
.uri = LOG_UPLOAD_URI_PATH,
.secretkey = handle->sign_key,
#ifdef LOG_CHECK_HTTP_RET_CODE
.need_recv = true,
.recv_timeout_ms = HTTP_WAIT_RET_TIMEOUT_MS,
#else
.need_recv = false,
.recv_timeout_ms = 0,
#endif
};
#ifdef LOG_CHECK_HTTP_RET_CODE
char buf[HTTP_RET_JSON_LENGTH] = {0};
rc = IOT_HTTP_Signed_Request(&params, post_buf, post_size, (uint8_t *)buf, HTTP_RET_JSON_LENGTH);
if (rc < 0) {
UPLOAD_ERR("IOT_HTTP_Signed_Request failed, rc = %d", rc);
goto exit;
}
if (rc == 0) {
UPLOAD_ERR("HTTP Recv length = %d", rc);
rc = QCLOUD_ERR_HTTP;
goto exit;
}
buf[rc - 1] = '\0'; // json_parse relies on a string
if (strlen(buf) > 0 && _get_json_ret_code(buf, rc)) {
UPLOAD_DBG("Log server return SUCCESS: %s", buf);
rc = QCLOUD_RET_SUCCESS;
} else {
UPLOAD_ERR("Log server return FAIL: %s", buf);
rc = QCLOUD_ERR_HTTP;
}
#else
rc = IOT_HTTP_Signed_Request(&params, post_buf, post_size, NULL, 0);
#endif
exit:
_remove_endchar_from_upload_buffer(post_buf, post_size);
UPLOAD_DBG("%ld data have be post to server. rc : %d", post_size, rc);
return rc;
}
/**
* @brief push data to log buffer
*
* @param[in] log_content need push data
* @return int if 0 success else QCLOUD_ERR_FAILURE
*/
int append_data_to_log_buffer(const char *log_content)
{
uint16_t log_size = strlen(log_content);
if (HAL_MutexTryLock(sg_log_upload_buffer_handle->lock_buf) != 0) {
UPLOAD_ERR("trylock buffer failed!");
return QCLOUD_ERR_FAILURE;
}
if ((sg_log_upload_buffer_handle->write_index + log_size + 5) > sg_log_upload_buffer_handle->log_buffer_size) {
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
UPLOAD_ERR("log upload buffer is not enough!");
return QCLOUD_ERR_FAILURE;
}
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
int index = 0;
while (index < (log_size - 2)) {
// replace may overflow buffer size
if ((sg_log_upload_buffer_handle->write_index + 5) > sg_log_upload_buffer_handle->log_buffer_size) {
break;
}
switch (log_content[index]) {
case '\"':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
break;
case '\\':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
break;
case '/':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '/';
break;
case '\b':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'b';
break;
case '\f':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'f';
break;
case '\n':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'n';
break;
case '\r':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'r';
break;
case '\t':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 't';
break;
default:
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] =
log_content[index];
}
index++;
}
/* replace \r\n to \", add as delimiter */
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = ',';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = ' ';
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
return QCLOUD_RET_SUCCESS;
}
#else
// ----------------------------------------------------------------------------
// Old log format by text
// ----------------------------------------------------------------------------
/* log post header format */
#define TIMESTAMP_SIZE (10)
#define SIGNATURE_SIZE (40)
#define CTRL_BYTES_SIZE (4)
// LOG_BUF_FIXED_HEADER_SIZE = 112
#define LOG_BUF_FIXED_HEADER_SIZE \
(SIGNATURE_SIZE + CTRL_BYTES_SIZE + MAX_SIZE_OF_PRODUCT_ID + MAX_SIZE_OF_DEVICE_NAME + TIMESTAMP_SIZE)
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
#include "mbedtls/aes.h"
/**
* @brief header of encrypt
*
*/
static char sg_head_content[128] = {0};
/**
* @brief log data encrypt by aes cbc
*
* @param handle
* @param buf need encrypt data
* @param datalen need encrypt data length
* @return int
*/
static int _log_aes_cbc_encrypt(LogUploadBuffer *handle, uint8_t *buf, uint32_t datalen)
{
int ret = QCLOUD_RET_SUCCESS;
mbedtls_aes_context ctx;
unsigned char iv[16];
memset(iv, '0', 16);
mbedtls_aes_init(&ctx);
ret = mbedtls_aes_setkey_enc(&ctx, (uint8_t *)handle->sign_key, 128);
if (ret) {
UPLOAD_ERR("Set encry key err,ret:%d", ret);
ret = QCLOUD_ERR_FAILURE;
goto exit;
}
ret = mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, datalen, iv, buf, buf);
if (ret) {
Log_e("encrypt err,ret:%d", ret);
ret = QCLOUD_ERR_FAILURE;
goto exit;
}
mbedtls_aes_free(&ctx);
return QCLOUD_RET_SUCCESS;
exit:
mbedtls_aes_free(&ctx);
return ret;
}
#endif
/**
* @brief log upload buffer init
*
* @param init_params @see LogUploadInitParams
* @return void* if success return @see LogUploadBuffer
*/
void *log_upload_buffer_init(LogUploadInitParams *init_params)
{
LogUploadBuffer *handle = _log_upload_buffer_init_pre(init_params);
if (!handle) {
return NULL;
}
handle->log_buffer_head_len = LOG_BUF_FIXED_HEADER_SIZE;
memset(handle->log_buffer, '#', handle->log_buffer_head_len);
handle->log_buffer[SIGNATURE_SIZE] = 'P';
memcpy(handle->log_buffer + SIGNATURE_SIZE + CTRL_BYTES_SIZE, init_params->product_id, MAX_SIZE_OF_PRODUCT_ID);
memcpy(handle->log_buffer + SIGNATURE_SIZE + CTRL_BYTES_SIZE + MAX_SIZE_OF_PRODUCT_ID, init_params->device_name,
strlen(init_params->device_name));
handle->write_index = handle->log_buffer_head_len;
handle->host = init_params->host;
_reset_log_buffer(handle);
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
HAL_Snprintf(sg_head_content, 128,
"Accept:application/json;*/*\r\n"
"X-TC-IotEncryType:%c\r\n"
"X-TC-IotCid:%s%s\r\n",
handle->log_buffer[SIGNATURE_SIZE], init_params->product_id, init_params->device_name);
#endif
return handle;
}
#ifdef LOG_CHECK_HTTP_RET_CODE
/**
* @brief get http respose result
*
* @param[in] json http response buffer
* @param[in] json_len http response buffer length
* @return true log report success
* @return false log report error
*/
static bool _get_json_ret_code(char *json, size_t json_len)
{
int rc = 0;
uint32_t ret_code;
rc = utils_json_get_uint32("Retcode", strlen("Retcode"), json, json_len, &ret_code);
if (!rc) {
if (!ret_code) {
return true;
}
}
return false;
}
#endif
/**
* @brief update timestamp add signature post buffer
*
*/
static void _update_time_and_signature(LogUploadBuffer *handle, char *post_buf, size_t post_size)
{
char timestamp[TIMESTAMP_SIZE + 1] = {0};
char signature[SIGNATURE_SIZE + 1] = {0};
uint32_t now_time = HAL_Timer_CurrentSec();
/* record the timestamp for this log uploading */
HAL_Snprintf(timestamp, TIMESTAMP_SIZE + 1, "%010d", now_time);
memcpy(post_buf + sg_log_upload_buffer_handle->log_buffer_head_len - TIMESTAMP_SIZE, timestamp, strlen(timestamp));
/* signature of this log uploading */
utils_hmac_sha1(post_buf + SIGNATURE_SIZE, post_size - SIGNATURE_SIZE, (uint8_t *)handle->sign_key,
strlen(handle->sign_key), signature);
memcpy(post_buf, signature, SIGNATURE_SIZE);
}
/**
* @brief post data to tencent cloud over http
*
* @param[in] post_buf need post data buffer
* @param[in] post_size need post data buffer length
* @return @see IotReturnCode
*/
int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size)
{
int rc = 0;
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
char *log_encrypt_buffer = NULL;
#endif
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
_update_time_and_signature(handle, post_buf, post_size);
void *http_client = IOT_HTTP_Init();
if (!http_client) {
UPLOAD_ERR("http client init error");
goto exit;
}
IotHTTPConnectParams connect_params = {
.url = handle->host ? handle->host : LOG_UPLOAD_OLD_SERVER_URL,
.port = LOG_UPLOAD_OLD_SERVER_PORT,
.ca_crt = NULL, // TODO: support cert
};
rc = IOT_HTTP_Connect(http_client, &connect_params);
if (rc) {
UPLOAD_ERR("http connect error.");
goto exit;
}
IotHTTPRequestParams request = {
.url = handle->host ? handle->host : LOG_UPLOAD_OLD_SERVER_URL,
.method = IOT_HTTP_METHOD_POST,
.content_length = post_size,
.content_type = "text/plain;charset=utf-8",
.content = post_buf,
.header = "Accept:application/json;*/*\r\n",
};
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
/*ANSI X.923: zero-padding with last byte as padlen */
int padlen = 16 - post_size % 16;
int datalen = post_size + padlen;
log_encrypt_buffer = HAL_Malloc(datalen + 1);
if (!log_encrypt_buffer) {
UPLOAD_ERR("malloc log encrypt buffer error. malloc length : %d", datalen);
rc = QCLOUD_ERR_MALLOC;
goto exit;
}
memset(log_encrypt_buffer, 0, datalen + 1);
memcpy(log_encrypt_buffer, post_buf, post_size);
log_encrypt_buffer[datalen - 1] = padlen;
// aes cbc
rc = _log_aes_cbc_encrypt(handle, (uint8_t *)log_encrypt_buffer, datalen);
if (QCLOUD_RET_SUCCESS != rc) {
UPLOAD_ERR("log encrypt failed, ret = %d", rc);
goto exit;
}
request.header = sg_head_content;
request.content_type = "application/octet-stream";
request.content_length = datalen;
request.content = log_encrypt_buffer;
#endif
rc = IOT_HTTP_Request(http_client, &request);
// 2. check rc
if (rc) {
UPLOAD_ERR("http post error.");
goto exit;
}
#ifdef LOG_CHECK_HTTP_RET_CODE
char buf[HTTP_RET_JSON_LENGTH] = {0};
rc = IOT_HTTP_Recv(http_client, (uint8_t *)buf, HTTP_RET_JSON_LENGTH, HTTP_WAIT_RET_TIMEOUT_MS);
if (rc < 0) {
UPLOAD_ERR("qcloud_http_recv_data failed, rc = %d", rc);
} else if (rc == 0) {
UPLOAD_ERR("HTTP Recv length == %d", rc);
rc = QCLOUD_ERR_HTTP_TIMEOUT;
} else {
buf[rc - 1] = '\0'; // json_parse relies on a string
if (strlen(buf) > 0 && _get_json_ret_code(buf, rc)) {
UPLOAD_DBG("Log server return SUCCESS: %s", buf);
rc = QCLOUD_RET_SUCCESS;
} else {
UPLOAD_ERR("Log server return FAIL: %s", buf);
rc = QCLOUD_ERR_HTTP;
}
}
#endif
exit:
if (http_client) {
IOT_HTTP_Disconnect(http_client);
IOT_HTTP_Deinit(http_client);
}
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
if (log_encrypt_buffer) {
HAL_Free(log_encrypt_buffer);
log_encrypt_buffer = NULL;
}
#endif
UPLOAD_DBG("%ld data have be post to server. rc : %d", post_size, rc);
return rc;
}
/**
* @brief push data to log buffer
*
* @param[in] log_content need push data
* @return int if 0 success else QCLOUD_ERR_FAILURE
*/
int append_data_to_log_buffer(const char *log_content)
{
uint16_t log_size = strlen(log_content);
if (HAL_MutexTryLock(sg_log_upload_buffer_handle->lock_buf) != 0) {
UPLOAD_ERR("trylock buffer failed!");
return QCLOUD_ERR_FAILURE;
}
if ((sg_log_upload_buffer_handle->write_index + log_size + 1) > sg_log_upload_buffer_handle->log_buffer_size) {
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
UPLOAD_ERR("log upload buffer is not enough! remain : %d, need upload length: %d",
sg_log_upload_buffer_handle->log_buffer_size - sg_log_upload_buffer_handle->write_index, log_size);
return -1;
}
memcpy(sg_log_upload_buffer_handle->log_buffer + sg_log_upload_buffer_handle->write_index, log_content, log_size);
sg_log_upload_buffer_handle->write_index += log_size;
/* replace \r\n to \n\f as delimiter */
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index - 1] = '\f';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index - 2] = '\n';
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
return QCLOUD_RET_SUCCESS;
}
#endif

View File

@@ -0,0 +1,183 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file log_mqtt.c
* @brief
* @author hubertxxu (hubertxxu@tencent.com)
* @version 1.0
* @date 2022-01-05
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-01-05 <td>1.0 <td>hubertxxu <td>first commit
* </table>
*/
#include "log_upload.h"
/**
* @brief LogLevelResultInfo handle
*
*/
typedef struct {
bool result_recv_ok;
uint32_t log_level;
} LogLevelResultInfo;
/**
* @brief MQTTLogUploadHandle handle
*
*/
typedef struct {
void *mqtt_client;
Timer time_update_timer;
} MQTTLogUploadHandle;
static MQTTLogUploadHandle sg_mqtt_log_upload_handle;
/**
* @brief
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see LogLevelResultInfo
*/
static void _log_mqtt_message_callback(void *client, const MQTTMessage *message, void *usr_data)
{
int rc;
LogLevelResultInfo *result = (LogLevelResultInfo *)usr_data;
Log_d("Receive log result message:%.*s", message->payload_len, message->payload_str);
// get log level
rc = utils_json_get_uint32("log_level", strlen("log_level"), message->payload_str, message->payload_len,
&result->log_level);
if (rc) {
return;
}
switch (result->log_level) {
case LOG_LEVEL_DISABLE:
log_upload_clear_buffer();
log_upload_set_upload_log_in_comm_err(true);
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
// when use aes encrypt. no need report log
log_upload_set_log_upload_level(LOG_LEVEL_DISABLE);
UPLOAD_DBG("disable log upload.");
#else
log_upload_set_log_upload_level(LOG_LEVEL_ERROR);
UPLOAD_DBG("Upload log level change to: %d", LOG_LEVEL_ERROR);
#endif
break;
case LOG_LEVEL_ERROR:
case LOG_LEVEL_WARN:
case LOG_LEVEL_INFO:
case LOG_LEVEL_DEBUG:
if (result->log_level < log_upload_get_log_upload_level()) {
log_upload_clear_buffer();
}
log_upload_set_upload_log_in_comm_err(false);
log_upload_set_log_upload_level((LogLevel)result->log_level);
UPLOAD_DBG("Upload log level change to: %d", result->log_level);
break;
default:
UPLOAD_ERR("Invalid log level: %d", result->log_level);
break;
}
result->result_recv_ok = true;
}
/**
* @brief Check and subscribe log result topic.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
static int _log_upload_topic_check_and_sub(void *client, const char *topic)
{
int rc = 0;
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
LogLevelResultInfo *log_level_info = (LogLevelResultInfo *)HAL_Malloc(sizeof(LogLevelResultInfo));
if (!log_level_info) {
return QCLOUD_ERR_MALLOC;
}
log_level_info->result_recv_ok = false;
sub_params.on_message_handler = _log_mqtt_message_callback;
sub_params.qos = QOS1;
sub_params.user_data = log_level_info;
sub_params.user_data_free = HAL_Free;
rc = IOT_MQTT_SubscribeSync(client, topic, &sub_params);
if (rc) {
HAL_Free(log_level_info);
}
return rc;
}
/**
* @brief Publish get log level message to log topic.
*
* @param[in,out] client pointer to mqtt client
* @return >= 0 for success
*/
static int _mqtt_get_log_level_publish(void *client)
{
static int sg_get_log_level_token_count = 0;
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC];
char payload[128] = {0};
HAL_Snprintf(topic_name, sizeof(topic_name), "$log/operation/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
HAL_Snprintf(payload, sizeof(payload), "{\"type\": \"get_log_level\",\"clientToken\": \"get_log_level-%u\"}",
sg_get_log_level_token_count++);
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.qos = QOS0;
pub_params.payload = payload;
pub_params.payload_len = strlen(payload);
return IOT_MQTT_Publish(client, topic_name, &pub_params);
}
/**
* @brief init log upload module
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int log_upload_init(void *client)
{
int rc = 0;
char log_upload_topic[MAX_SIZE_OF_CLOUD_TOPIC];
/* subscribe the log topic: "$log/operation/result/${productId}/${deviceName}" */
HAL_Snprintf(log_upload_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$log/operation/result/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
rc = _log_upload_topic_check_and_sub(client, log_upload_topic);
if (rc) {
return rc;
}
rc = _mqtt_get_log_level_publish(client);
if (rc < 0) {
return rc;
}
sg_mqtt_log_upload_handle.mqtt_client = client;
return rc;
}

View File

@@ -0,0 +1,539 @@
/**
* @file log_upload.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-01-24
*
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @par Change Log:
* <table>
* Date Version Author Description
* 2022-01-24 1.0 hubertxxu first commit
* </table>
*/
#include "log_upload.h"
#ifdef LOG_UPLOAD_TYPE_JSON
#define LOG_DELIMITER "\", "
#else
#define LOG_DELIMITER "\n\f"
#endif
/**
* @brief data structure for log upload
*
*/
typedef struct {
bool upload_only_in_comm_err;
LogLevel log_level;
Timer upload_timer;
void *upload_buffer_handle;
const char *save_log_filename;
LogSaveFunc save_func;
LogReadFunc read_func;
LogDelFunc del_func;
LogGetSizeFunc get_size_func;
bool log_save_enabled;
bool log_client_init_done;
} QcloudIoTLogUpload;
static QcloudIoTLogUpload *sg_log_uploader_client;
// ----------------------------------------------------------------------------
// Internal API
// ----------------------------------------------------------------------------
/**
* @brief
*
* @return QcloudIoTLogUpload* @see QcloudIoTLogUpload
*/
static QcloudIoTLogUpload *_get_log_uploader_client(void)
{
if (!sg_log_uploader_client) {
return NULL;
}
if (!sg_log_uploader_client->log_client_init_done) {
return NULL;
}
return sg_log_uploader_client;
}
/**
* @brief
*
* @param client @see QcloudIoTLogUpload
*/
static void _set_log_uploader_client(void *client)
{
sg_log_uploader_client = client;
}
/**
* @brief set log upload in comm err state
*
* @param state true or false
*/
void log_upload_set_upload_log_in_comm_err(bool state)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK_RTN(log_uploader_handle);
log_uploader_handle->upload_only_in_comm_err = state;
}
/**
* @brief change log upload level
*
* @param level
*/
void log_upload_set_log_upload_level(LogLevel level)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK_RTN(log_uploader_handle);
log_uploader_handle->log_level = level;
}
/**
* @brief get log upload level
*
* @return LogLevel
*/
LogLevel log_upload_get_log_upload_level(void)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK(log_uploader_handle, LOG_LEVEL_DISABLE);
return log_uploader_handle->log_level;
}
/**
* @brief check if need force upload
*
* @param[in] force_upload
* @return true need force upload
* @return false not needed force upload
*/
static bool _check_force_upload(bool force_upload)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
if (!log_uploader_handle) {
return false;
}
/* 1: check if have data to upload */
if (have_data_need_update(log_uploader_handle->upload_buffer_handle)) {
return false;
}
uint32_t write_index = get_log_buffer_write_index(log_uploader_handle->upload_buffer_handle);
bool is_low_buffer = (get_log_buffer_size() - write_index) < LOG_LOW_BUFFER_THRESHOLD ? true : false;
/* 2. check if upload only in comm err */
if (!force_upload && log_uploader_handle->upload_only_in_comm_err) {
/* buffer is low but we couldn't upload now, reset buffer */
if (is_low_buffer) {
log_upload_clear_buffer();
}
HAL_Timer_CountdownMs(&log_uploader_handle->upload_timer, LOG_UPLOAD_INTERVAL_MS);
return false;
}
/* 3: force upload is true, handle it right now */
if (force_upload) {
return true;
}
/* 4 : check if buffer is low */
if (is_low_buffer) {
/* buffer is low, handle it right now */
return true;
}
/*5: check if timeout, handle it right now*/
return HAL_Timer_Expired(&log_uploader_handle->upload_timer);
}
/**
* @brief save log to file or flash
*
* @param[in] log_buf need saved buffer
* @param[in] log_size need saved buffer length
* @return @see IotReturnCode
*/
static int _save_log(char *log_buf, size_t log_size)
{
int rc = 0;
size_t write_size, current_size;
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
if (!log_size) {
UPLOAD_ERR("nothing need to save. log_size : %ld", log_size);
return -1;
}
current_size = log_uploader_handle->get_size_func(log_uploader_handle->save_log_filename);
/* overwrite the previous saved log to avoid too many saved logs */
if ((current_size + log_size) > MAX_LOG_SAVE_SIZE) {
UPLOAD_ERR("overwrite the previous saved log. %ld", current_size);
rc = log_uploader_handle->del_func(log_uploader_handle->save_log_filename);
if (rc) {
Log_e("fail to delete previous log");
}
}
UPLOAD_DBG("saved data [%ld]", log_size);
write_size = log_uploader_handle->save_func(log_uploader_handle->save_log_filename, log_buf, log_size);
if (write_size != log_size) {
Log_e("fail to save log. RC %ld - log size %ld", write_size, log_size);
}
return write_size != log_size ? -1 : 0;
}
static uint32_t _buffer_get_at_most_message(char *post_buf)
{
uint32_t possible_size = 0;
char *next_log_buf = NULL;
size_t delimiter_len = strlen(LOG_DELIMITER);
while (possible_size < MAX_HTTP_LOG_POST_SIZE) {
/* locate the delimiter */
next_log_buf = strstr(post_buf + possible_size, LOG_DELIMITER);
if (!next_log_buf) {
return 0;
}
possible_size = (size_t)(next_log_buf - post_buf + delimiter_len);
/* end of log */
if (next_log_buf[delimiter_len] == 0 && possible_size < MAX_HTTP_LOG_POST_SIZE) {
break;
}
}
return possible_size;
}
/**
* @brief report these to the server
*
* @param post_buf
* @param post_size
* @param is_log_buffer
* @return int
*/
static int _post_log_buffer_to_cloud(QcloudIoTLogUpload *log_uploader_handle, char *post_buf, size_t post_size,
bool is_log_buffer)
{
int rc = 0;
uint32_t log_buffer_head_len = get_log_buffer_head_len(log_uploader_handle->upload_buffer_handle);
size_t orig_post_size = post_size;
size_t post_payload, possible_size, actual_post_payload = 0;
do {
// 1. get message in buffer
possible_size = _buffer_get_at_most_message(post_buf);
if (!possible_size) {
UPLOAD_ERR("Upload size should not be 0! Total sent: %ld. Left: %ld",
actual_post_payload + log_buffer_head_len, post_size);
if (is_log_buffer) {
log_upload_clear_buffer();
}
return QCLOUD_RET_SUCCESS;
}
if (is_log_buffer && (possible_size > get_log_buffer_size() || possible_size > orig_post_size ||
possible_size <= log_buffer_head_len)) {
log_upload_clear_buffer();
UPLOAD_ERR("possible size (%ld) is too large.", possible_size);
return QCLOUD_RET_SUCCESS;
}
// 2. post to server
rc = post_one_http_to_server(log_uploader_handle->upload_buffer_handle, post_buf, possible_size);
if (rc) {
UPLOAD_ERR("Send log failed. Total sent: %ld. Left: %ld", actual_post_payload + log_buffer_head_len,
post_size);
// when send failed save log in file.
if (log_uploader_handle->log_save_enabled && is_log_buffer) {
_save_log(post_buf + log_buffer_head_len, possible_size - log_buffer_head_len);
}
}
// 3. remove have be post message
memmove(post_buf + log_buffer_head_len, post_buf + possible_size, post_size - possible_size);
post_payload = possible_size - log_buffer_head_len;
post_size -= post_payload;
actual_post_payload += post_payload;
memset(post_buf + post_size, 0, orig_post_size - post_size);
UPLOAD_DBG("post log %d OK. Total sent: %d. Left: %d ", possible_size,
actual_post_payload + log_buffer_head_len, post_size);
} while (post_size > log_buffer_head_len);
if (is_log_buffer) {
remove_log_from_buffer(log_uploader_handle->upload_buffer_handle, orig_post_size);
}
return rc;
}
/**
* @brief handle log to save flash or file
*
* @return @see IotReturnCode
*/
static int _handle_saved_log(void)
{
int rc = QCLOUD_RET_SUCCESS;
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
if (!log_uploader_handle) {
return QCLOUD_ERR_FAILURE;
}
size_t whole_log_size = log_uploader_handle->get_size_func(log_uploader_handle->save_log_filename);
if (!whole_log_size) {
return rc;
}
uint32_t log_buffer_head_len = get_log_buffer_head_len(log_uploader_handle->upload_buffer_handle);
size_t buf_size = whole_log_size + log_buffer_head_len + 1;
char *log_buf = HAL_Malloc(buf_size);
if (!log_buf) {
UPLOAD_ERR("Malloc failed, size: %ld", buf_size);
return QCLOUD_ERR_FAILURE;
}
/* read the whole log to buffer */
size_t read_len = log_uploader_handle->read_func(log_uploader_handle->save_log_filename,
log_buf + log_buffer_head_len, whole_log_size);
if (read_len != whole_log_size) {
Log_e("fail to read whole saved log. Size: %ld - read: %ld", whole_log_size, read_len);
rc = QCLOUD_ERR_FAILURE;
goto exit;
}
/* copy header from global log buffer */
size_t upload_size = whole_log_size + log_buffer_head_len;
copy_log_buffer_header(log_buf, log_uploader_handle->upload_buffer_handle);
log_buf[buf_size - 1] = 0;
/* post server */
rc = _post_log_buffer_to_cloud(log_uploader_handle, log_buf, upload_size, false);
if (rc == QCLOUD_RET_SUCCESS || rc == QCLOUD_ERR_INVAL || rc == QCLOUD_ERR_HTTP) {
UPLOAD_DBG("handle saved log done! Size: %ld. ", whole_log_size);
log_uploader_handle->del_func(log_uploader_handle->save_log_filename);
}
exit:
HAL_Free(log_buf);
return rc;
}
/**
* @brief append need report log to log upload buffer
*
* @param[in] log_content data of need to report
*/
static void _log_upload_append_to_log_buffer(int log_level, const char *log_content)
{
int rc = 0;
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
// POINTER_SANITY_CHECK_RTN(log_uploader_handle);
if (!log_uploader_handle) {
return;
}
// if not necessary. just give up upload.
if (log_level > log_uploader_handle->log_level) {
return;
}
uint16_t log_size = strlen(log_content);
if (!log_size) {
UPLOAD_ERR("log_size is not 0!");
return;
}
rc = append_data_to_log_buffer(log_content);
if (rc) {
/* log buffer is full. upload data right now */
HAL_Timer_CountdownMs(&log_uploader_handle->upload_timer, 0);
}
}
/**
* @brief upload data to tencent cloud
*
* @param force_upload if necessary
* @return @see IotReturnCode
*/
static int _do_log_upload(bool force_upload)
{
int rc;
static bool sg_need_handle_saved_log = true;
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
/* step1: handle previously saved log */
if (log_uploader_handle->log_save_enabled && sg_need_handle_saved_log) {
rc = _handle_saved_log();
if (!rc) {
sg_need_handle_saved_log = false;
}
}
/* step2: check if have data to upload */
if (!_check_force_upload(force_upload)) {
return QCLOUD_RET_SUCCESS;
}
// log_buffer_lock();
/* step3: post log buffer data to cloud */
uint32_t upload_log_size = get_log_buffer_write_index(log_uploader_handle->upload_buffer_handle);
rc = _post_log_buffer_to_cloud(log_uploader_handle, get_log_buffer(log_uploader_handle->upload_buffer_handle),
upload_log_size, true);
if (rc) {
sg_need_handle_saved_log = true;
}
HAL_Timer_CountdownMs(&log_uploader_handle->upload_timer, LOG_UPLOAD_INTERVAL_MS);
// log_buffer_unlock();
return QCLOUD_RET_SUCCESS;
}
/**
* @brief init log upload previously
*
* @param[in] init_params @see LogUploadInitParams
* @return @see IotReturnCode
*/
static int log_upload_init_pre(LogUploadInitParams *init_params)
{
if (_get_log_uploader_client()) {
UPLOAD_DBG("log upload initialized.");
return QCLOUD_RET_SUCCESS;
}
QcloudIoTLogUpload *log_uploader_handle = HAL_Malloc(sizeof(QcloudIoTLogUpload));
if (!log_uploader_handle) {
UPLOAD_ERR("allocate for log client failed");
goto err_exit;
}
log_uploader_handle->upload_only_in_comm_err = false;
/* all the call back functions are necessary to handle log save and re-upload*/
if (init_params->save_func && init_params->read_func && init_params->del_func && init_params->get_size_func &&
init_params->save_log_filename) {
log_uploader_handle->save_log_filename = init_params->save_log_filename;
log_uploader_handle->save_func = init_params->save_func;
log_uploader_handle->read_func = init_params->read_func;
log_uploader_handle->del_func = init_params->del_func;
log_uploader_handle->get_size_func = init_params->get_size_func;
log_uploader_handle->log_save_enabled = true;
} else {
log_uploader_handle->log_save_enabled = false;
}
log_uploader_handle->upload_buffer_handle = log_upload_buffer_init(init_params);
if (!log_uploader_handle->upload_buffer_handle) {
goto err_exit;
}
_set_log_uploader_client(log_uploader_handle);
log_uploader_handle->log_level = LOG_LEVEL_DEBUG;
log_uploader_handle->log_client_init_done = true;
return QCLOUD_RET_SUCCESS;
err_exit:
if (log_uploader_handle) {
log_upload_buffer_deinit(log_uploader_handle->upload_buffer_handle);
HAL_Free(log_uploader_handle);
log_uploader_handle = NULL;
}
return QCLOUD_ERR_FAILURE;
}
/**
* @brief stop log upload add release resources
*
* @return @see IotReturnCode
*/
static int log_upload_deinit(void)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
log_upload_buffer_deinit(log_uploader_handle->upload_buffer_handle);
HAL_Free(log_uploader_handle);
log_uploader_handle = NULL;
return QCLOUD_RET_SUCCESS;
}
// ----------------------------------------------------------------------------
// External API
// ----------------------------------------------------------------------------
/**
* @brief init log upload previously
*
* @param[in] init_params @see LogUploadInitParams
* @return @see IotReturnCode
*/
int IOT_Log_Upload_InitPre(LogUploadInitParams *init_params)
{
POINTER_SANITY_CHECK(init_params, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(init_params->product_id, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(init_params->device_name, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(init_params->sign_key, QCLOUD_ERR_INVAL);
return log_upload_init_pre(init_params);
}
/**
* @brief init log upload module
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int IOT_Log_Upload_Init(void *client)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
return log_upload_init(client);
}
/**
* @brief stop log upload add release resources
*
* @return @see IotReturnCode
*/
int IOT_Log_Upload_Deinit(void)
{
return log_upload_deinit();
}
/**
* @brief do log upload
*
* @param force_upload if necessary
* @return @see IotReturnCode
*/
int IOT_Log_Upload(bool force_upload)
{
return _do_log_upload(force_upload);
}
/**
* @brief append need report log to log upload buffer
*
* @param[in] log_content data of need to report
*/
void IOT_Log_Upload_AppendToUploadBuffer(int log_level, const char *log_content)
{
POINTER_SANITY_CHECK_RTN(log_content);
_log_upload_append_to_log_buffer(log_level, log_content);
}

View File

@@ -0,0 +1,8 @@
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 120
DerivePointerAlignment: true
PointerAlignment: Left
SortIncludes: true
IncludeBlocks: Preserve
IndentPPDirectives: AfterHash

View File

@@ -0,0 +1,82 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_broadcast.cc
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_common.h"
namespace mqtt_client_unittest {
/**
* @brief Test log upload
*
*/
TEST_F(MqttClientTest, log_upload_test) {
LogHandleFunc func = {0};
func.log_malloc = HAL_Malloc;
func.log_free = HAL_Free;
func.log_get_current_time_str = HAL_Timer_Current;
func.log_printf = HAL_Printf;
func.log_upload = IOT_Log_Upload_AppendToUploadBuffer;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
LogUploadInitParams log_upload_init_params = DEFAULT_LOG_UPLOAD_INIT_PARAMS;
log_upload_init_params.device_name = device_info.device_name;
log_upload_init_params.product_id = device_info.product_id;
log_upload_init_params.sign_key = device_info.device_secret;
log_upload_init_params.log_buffer_size = LOG_UPLOAD_BUFFER_SIZE;
log_upload_init_params.save_log_filename = "./tmp/upload-fail-save.log";
log_upload_init_params.read_func = HAL_File_Read;
log_upload_init_params.save_func = HAL_File_Save;
log_upload_init_params.del_func = HAL_File_Del;
log_upload_init_params.get_size_func = HAL_File_Get_Size;
ASSERT_EQ(IOT_Log_Upload_InitPre(&log_upload_init_params), 0);
uint32_t loop_cnt = 10;
int rc = 0;
do {
Log_d("log upload test debug %d...", loop_cnt);
Log_i("log upload test info %d...", loop_cnt);
Log_w("log upload test waring %d...", loop_cnt);
Log_e("log upload test error %d...", loop_cnt);
rc = IOT_MQTT_Yield(client, 200);
ASSERT_EQ(IOT_Log_Upload(rc ? true : false), 0);
} while (loop_cnt--);
ASSERT_EQ(IOT_Log_Upload_Deinit(), 0);
func.log_upload = NULL;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
}
} // namespace mqtt_client_unittest