file_be.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*
  2. * Copyright (c) 2006-2021, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2021-01-07 ChenYong first version
  9. * 2021-12-20 armink add multi-instance version
  10. */
  11. #include <rtthread.h>
  12. #include <dfs_file.h>
  13. #include <unistd.h>
  14. #include <ulog.h>
  15. #include <ulog_be.h>
  16. #ifdef ULOG_BACKEND_USING_FILE
  17. #if defined(ULOG_ASYNC_OUTPUT_THREAD_STACK) && (ULOG_ASYNC_OUTPUT_THREAD_STACK < 2048)
  18. #error "The value of ULOG_ASYNC_OUTPUT_THREAD_STACK must be greater than 2048."
  19. #endif
  20. /* rotate the log file xxx_n-1.log => xxx_n.log, and xxx.log => xxx_0.log */
  21. static rt_bool_t ulog_file_rotate(struct ulog_file_be *be)
  22. {
  23. #define SUFFIX_LEN 10
  24. /* mv xxx_n-1.log => xxx_n.log, and xxx.log => xxx_0.log */
  25. static char old_path[ULOG_FILE_PATH_LEN], new_path[ULOG_FILE_PATH_LEN];
  26. int index = 0, err = 0, file_fd = 0;
  27. rt_bool_t result = RT_FALSE;
  28. size_t base_len = 0;
  29. rt_snprintf(old_path, ULOG_FILE_PATH_LEN, "%s/%s", be->cur_log_dir_path, be->parent.name);
  30. rt_snprintf(new_path, ULOG_FILE_PATH_LEN, "%s/%s", be->cur_log_dir_path, be->parent.name);
  31. base_len = rt_strlen(be->cur_log_dir_path) + rt_strlen(be->parent.name) + 1;
  32. if (be->cur_log_file_fd >= 0)
  33. {
  34. close(be->cur_log_file_fd);
  35. }
  36. for (index = be->file_max_num - 2; index >= 0; --index)
  37. {
  38. rt_snprintf(old_path + base_len, SUFFIX_LEN, index ? "_%d.log" : ".log", index - 1);
  39. rt_snprintf(new_path + base_len, SUFFIX_LEN, "_%d.log", index);
  40. /* remove the old file */
  41. if ((file_fd = open(new_path, O_RDONLY)) >= 0)
  42. {
  43. close(file_fd);
  44. unlink(new_path);
  45. }
  46. /* change the new log file to old file name */
  47. if ((file_fd = open(old_path , O_RDONLY)) >= 0)
  48. {
  49. close(file_fd);
  50. err = dfs_file_rename(old_path, new_path);
  51. }
  52. if (err < 0)
  53. {
  54. result = RT_FALSE;
  55. goto __exit;
  56. }
  57. result = RT_TRUE;
  58. }
  59. __exit:
  60. /* reopen the file */
  61. be->cur_log_file_fd = open(be->cur_log_file_path, O_CREAT | O_RDWR | O_APPEND);
  62. return result;
  63. }
  64. static void ulog_file_backend_flush_with_buf(struct ulog_backend *backend)
  65. {
  66. struct ulog_file_be *be = (struct ulog_file_be *) backend;
  67. rt_size_t file_size = 0, write_size = 0;
  68. if (be->enable == RT_FALSE || be->buf_ptr_now == be->file_buf)
  69. {
  70. return;
  71. }
  72. if (be->cur_log_file_fd < 0)
  73. {
  74. /* check log file directory */
  75. if (access(be->cur_log_dir_path, F_OK) < 0)
  76. {
  77. mkdir(be->cur_log_dir_path, 0);
  78. }
  79. /* open file */
  80. rt_snprintf(be->cur_log_file_path, ULOG_FILE_PATH_LEN, "%s/%s.log", be->cur_log_dir_path, be->parent.name);
  81. be->cur_log_file_fd = open(be->cur_log_file_path, O_CREAT | O_RDWR | O_APPEND);
  82. if (be->cur_log_file_fd < 0)
  83. {
  84. rt_kprintf("ulog file(%s) open failed.", be->cur_log_file_path);
  85. return;
  86. }
  87. }
  88. file_size = lseek(be->cur_log_file_fd, 0, SEEK_END);
  89. if (file_size >= (be->file_max_size - be->buf_size * 2))
  90. {
  91. if (!ulog_file_rotate(be))
  92. {
  93. return;
  94. }
  95. }
  96. write_size = (rt_size_t)(be->buf_ptr_now - be->file_buf);
  97. /* write to the file */
  98. if (write(be->cur_log_file_fd, be->file_buf, write_size) != write_size)
  99. {
  100. return;
  101. }
  102. /* flush file cache */
  103. fsync(be->cur_log_file_fd);
  104. /* point be->buf_ptr_now at the head of be->file_buf[be->buf_size] */
  105. be->buf_ptr_now = be->file_buf;
  106. }
  107. static void ulog_file_backend_output_with_buf(struct ulog_backend *backend, rt_uint32_t level,
  108. const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len)
  109. {
  110. struct ulog_file_be *be = (struct ulog_file_be *)backend;
  111. rt_size_t copy_len = 0, free_len = 0;
  112. const unsigned char *buf_ptr_end = be->file_buf + be->buf_size;
  113. while (len)
  114. {
  115. /* free space length */
  116. free_len = buf_ptr_end - be->buf_ptr_now;
  117. /* copy the log to the mem buffer */
  118. if (len > free_len)
  119. {
  120. copy_len = free_len;
  121. }
  122. else
  123. {
  124. copy_len = len;
  125. }
  126. rt_memcpy(be->buf_ptr_now, log, copy_len);
  127. /* update data pos */
  128. be->buf_ptr_now += copy_len;
  129. len -= copy_len;
  130. log += copy_len;
  131. RT_ASSERT(be->buf_ptr_now <= buf_ptr_end);
  132. /* check the log buffer remain size */
  133. if (buf_ptr_end == be->buf_ptr_now)
  134. {
  135. ulog_file_backend_flush_with_buf(backend);
  136. if (buf_ptr_end == be->buf_ptr_now)
  137. {
  138. /* There is no space, indicating that the data cannot be refreshed
  139. to the back end of the file Discard data and exit directly */
  140. break;
  141. }
  142. }
  143. }
  144. }
  145. /* initialize the ulog file backend */
  146. int ulog_file_backend_init(struct ulog_file_be *be, const char *name, const char *dir_path, rt_size_t max_num,
  147. rt_size_t max_size, rt_size_t buf_size)
  148. {
  149. be->file_buf = rt_calloc(1, buf_size);
  150. if (!be->file_buf)
  151. {
  152. rt_kprintf("Warning: NO MEMORY for %s file backend\n", name);
  153. return -RT_ENOMEM;
  154. }
  155. /* temporarily store the start address of the ulog file buffer */
  156. be->buf_ptr_now = be->file_buf;
  157. be->cur_log_file_fd = -1;
  158. be->file_max_num = max_num;
  159. be->file_max_size = max_size;
  160. be->buf_size = buf_size;
  161. be->enable = RT_FALSE;
  162. rt_strncpy(be->cur_log_dir_path, dir_path, ULOG_FILE_PATH_LEN);
  163. /* the buffer length MUST less than file size */
  164. RT_ASSERT(be->buf_size < be->file_max_size);
  165. be->parent.output = ulog_file_backend_output_with_buf;
  166. be->parent.flush = ulog_file_backend_flush_with_buf;
  167. ulog_backend_register((ulog_backend_t) be, name, RT_FALSE);
  168. return 0;
  169. }
  170. /* uninitialize the ulog file backend */
  171. int ulog_file_backend_deinit(struct ulog_file_be *be)
  172. {
  173. if (be->cur_log_file_fd >= 0)
  174. {
  175. /* flush log to file */
  176. ulog_file_backend_flush_with_buf((ulog_backend_t)be);
  177. /* close */
  178. close(be->cur_log_file_fd);
  179. be->cur_log_file_fd = -1;
  180. }
  181. if (!be->file_buf)
  182. {
  183. rt_free(be->file_buf);
  184. be->file_buf = RT_NULL;
  185. }
  186. ulog_backend_unregister((ulog_backend_t)be);
  187. return 0;
  188. }
  189. void ulog_file_backend_enable(struct ulog_file_be *be)
  190. {
  191. be->enable = RT_TRUE;
  192. }
  193. void ulog_file_backend_disable(struct ulog_file_be *be)
  194. {
  195. be->enable = RT_FALSE;
  196. }
  197. #endif /* ULOG_BACKEND_USING_FILE */