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