Mstdlib-1.24.0
mstdlib_log.h
1#ifndef MSTDLIB_LOG_H
2#define MSTDLIB_LOG_H
3
4#include <mstdlib/mstdlib.h>
5#include <mstdlib/mstdlib_thread.h>
6#include <mstdlib/mstdlib_io.h>
7
8/*! \defgroup m_log Logging Subsystem
9 *
10 * \code{.c}
11 * #include <mstdlib/mstdlib_log.h>
12 * \endcode
13 *
14 * Example:
15 *
16 * \code{.c}
17 * #include <mstdlib/mstdlib_log.h>
18 *
19 * #define STREAM_QUEUE_SIZE (1500*1000) // 1.5 MB - small enough to cause a few drops, since all threads combined will
20 * // output about 1.7 MB of data
21 * #define FILE_QUEUE_SIZE (1500*1000) // 1.5 MB
22 * #define SYSLOG_QUEUE_SIZE (10*1000) // 10 KB
23 * #define CRIT_FREQ (500) // critical messages are sent every time message count hits a multiple of this number
24 * #define NUM_MSGS (10*1000) // total number of messages to send
25 *
26 * //#define FILE_DO_ARCHIVE //Uncomment to test file archiving.
27 * //#define ADD_LINE_END //Uncomment to add embedded line endings to log messages (tests multiline functionality).
28 *
29 * //#define DO_MEMBUF //Uncomment to test membuf.
30 *
31 *
32 * #ifdef FILE_DO_ARCHIVE
33 * static const char *archive_cmd = "bzip2 -f";
34 * static const char *archive_ext = ".bz2";
35 * #else
36 * static const char *archive_cmd = NULL;
37 * static const char *archive_ext = NULL;
38 * #endif
39 *
40 * static const M_bool flush_on_destroy = M_TRUE;
41 *
42 * typedef enum {
43 * TAG_1 = 1 << 0,
44 * TAG_2 = 1 << 1,
45 * TAG_3 = 1 << 2,
46 * CRIT_1 = 1 << 3,
47 * CRIT_2 = 1 << 4,
48 * CRIT_3 = 1 << 5
49 * } tags_t;
50 *
51 * static const char *tag_to_str(tags_t tag)
52 * {
53 * switch (tag) {
54 * case TAG_1: return "tag 1";
55 * case TAG_2: return "tag 2";
56 * case TAG_3: return "tag 3";
57 * case CRIT_1: return "CRITICAL 1";
58 * case CRIT_2: return "CRITICAL 2";
59 * case CRIT_3: return "CRITICAL 3";
60 * }
61 * return "unknown tag";
62 * }
63 *
64 *
65 * typedef struct {
66 * M_log_t *log;
67 * const char *msg;
68 * tags_t tag;
69 * tags_t crit_tag;
70 * } tdata_t;
71 *
72 * static void set(tdata_t *td, M_log_t *log, const char *msg, tags_t tag, tags_t crit_tag)
73 * {
74 * td->log = log;
75 * td->msg = msg;
76 * td->tag = tag;
77 * td->crit_tag = crit_tag;
78 * }
79 *
80 *
81 * static void prefix_cb(M_buf_t *buf, M_uint64 tag, void *prefix_thunk, void *msg_thunk)
82 * {
83 * (void)prefix_thunk;
84 * (void)msg_thunk;
85 * M_bprintf(buf, ": [%s]\t", tag_to_str((tags_t)tag));
86 * }
87 *
88 *
89 * static void *test_thread(void *arg)
90 * {
91 * tdata_t *td = arg;
92 * M_uint64 i;
93 * M_uint64 crit;
94 *
95 * crit = CRIT_FREQ;
96 * for (i=0; i<NUM_MSGS; i++) {
97 * M_log_error_t err;
98 * tags_t tag;
99 *
100 * //M_thread_sleep(10*1000); //Add 10 ms delay
101 *
102 * if (crit >= CRIT_FREQ) {
103 * tag = td->crit_tag;
104 * crit = 1;
105 * } else {
106 * tag = td->tag;
107 * ++crit;
108 * }
109 *
110 * err = M_log_printf(td->log, tag, NULL,
111 * #ifdef ADD_LINE_END
112 * "%s --\n %llu",
113 * #else
114 * "%s -- %llu",
115 * #endif
116 * td->msg, i);
117 *
118 * if (err != M_LOG_SUCCESS) {
119 * M_fprintf(stderr, "Error writing log message: %s\n", M_log_err_to_str(err));
120 * }
121 * }
122 *
123 * return NULL;
124 * }
125 *
126 *
127 * int main(int argc, char *argv[])
128 * {
129 * M_log_t *log;
130 * M_log_error_t res;
131 * M_log_module_t *mod_stream;
132 * M_log_module_t *mod_syslog;
133 * M_log_module_t *mod_file;
134 * #ifdef DO_MEMBUF
135 * M_log_module_t *mod_membuf;
136 * M_fs_file_t *membuf_out;
137 * M_buf_t *membuf;
138 * #endif
139 * M_thread_attr_t *attr;
140 * M_threadid_t t1, t2, t3;
141 * tdata_t data1, data2, data3;
142 *
143 * (void)argc;
144 * (void)argv;
145 *
146 * // Set up the log.
147 * log = M_log_create(M_LOG_LINE_END_NATIVE, flush_on_destroy, NULL);
148 * M_log_set_time_format(log, "[%a %D %Y %H:%m:%s:%u %z]");
149 * M_log_set_tag_name(log, TAG_1, "tag_1_name");
150 * M_log_set_tag_name(log, TAG_2, "tag_2_name");
151 * M_log_set_tag_name(log, TAG_3, "tag_3_name");
152 * M_log_set_tag_name(log, CRIT_1, "crit_1_name");
153 * M_log_set_tag_name(log, CRIT_2, "crit_2_name");
154 * M_log_set_tag_name(log, CRIT_3, "crit_3_name");
155 *
156 * // Set up the stream module.
157 * res = M_log_module_add_stream(log, M_STREAM_STDOUT, STREAM_QUEUE_SIZE, &mod_stream);
158 * //res = M_log_module_add_nslog(log, STREAM_QUEUE_SIZE, &mod_stream);
159 * if (res != M_LOG_SUCCESS) {
160 * M_fprintf(stderr, "Could not add stream module: %s\n", M_log_err_to_str(res));
161 * } else {
162 * M_log_module_set_accepted_tags(log, mod_stream, TAG_1 | TAG_2 | TAG_3 | CRIT_1 | CRIT_2 | CRIT_3);
163 *
164 * M_log_module_set_prefix(log, mod_stream, prefix_cb, NULL, NULL);
165 * }
166 *
167 * // Set up the file module.
168 * res = M_log_module_add_file(log, "~/Tmp/logs/testing.log", 15, 150000, 0, FILE_QUEUE_SIZE, archive_cmd, archive_ext, &mod_file);
169 * if (res != M_LOG_SUCCESS) {
170 * M_fprintf(stderr, "Could not add file module: %s\n", M_log_err_to_str(res));
171 * } else {
172 * M_log_module_set_accepted_tags(log, mod_file, TAG_1 | TAG_2 | TAG_3 | CRIT_1 | CRIT_2 | CRIT_3);
173 *
174 * M_log_module_set_prefix(log, mod_file, prefix_cb, NULL, NULL);
175 * }
176 *
177 *
178 * // Set up the syslog module.
179 * res = M_log_module_add_syslog(log, "log_example", M_SYSLOG_FACILITY_LOCAL5, SYSLOG_QUEUE_SIZE, &mod_syslog);
180 *
181 * if (res != M_LOG_SUCCESS) {
182 * M_fprintf(stderr, "Could not add syslog module: %s\n", M_log_err_to_str(res));
183 * } else {
184 * M_log_module_set_accepted_tags(log, mod_syslog, CRIT_1 | CRIT_2 | CRIT_3);
185 *
186 * M_log_module_set_prefix(log, mod_syslog, prefix_cb, NULL, NULL);
187 *
188 * M_log_module_syslog_set_tag_priority(log, mod_syslog, CRIT_1 | CRIT_2, M_SYSLOG_WARNING);
189 *
190 * M_log_module_syslog_set_tag_priority(log, mod_syslog, CRIT_3, M_SYSLOG_CRIT);
191 * }
192 *
193 * // Do an emergency call (just to see if it really works).
194 * M_log_emergency(log, "RED ALERT! WOOT WOOT WOOT\r\n");
195 *
196 * // Launch three test threads that spam the logger with a bunch of messages.
197 * set(&data1, log, "data1", TAG_1, CRIT_1);
198 * set(&data2, log, "data2", TAG_2, CRIT_2);
199 * set(&data3, log, "data3", TAG_3, CRIT_3);
200 *
201 * attr = M_thread_attr_create();
202 * M_thread_attr_set_create_joinable(attr, M_TRUE);
203 *
204 * t1 = M_thread_create(attr, test_thread, &data1);
205 * t2 = M_thread_create(attr, test_thread, &data2);
206 * t3 = M_thread_create(attr, test_thread, &data3);
207 *
208 * M_thread_attr_destroy(attr);
209 *
210 * #ifdef DO_MEMBUF
211 * // Wait a little before we add the membuf module.
212 * M_thread_sleep(50*1000); // 50 ms
213 *
214 * res = M_log_module_add_membuf(log, 400*1000, 60*1000, NULL, NULL, &mod_membuf);
215 * if (res != M_LOG_SUCCESS) {
216 * M_fprintf(stderr, "Could not add membuf module: %s\n", M_log_err_to_str(res));
217 * } else {
218 * M_log_module_set_accepted_tags(log, mod_membuf, TAG_1 | CRIT_2);
219 *
220 * M_log_module_set_prefix(log, mod_membuf, prefix_cb, NULL, NULL);
221 * }
222 * #endif
223 *
224 * // Do a suspend/resume operation.
225 * M_log_suspend(log);
226 * M_thread_sleep(500); // sleep for 0.5 ms
227 * M_log_resume(log, NULL);
228 *
229 * // Wait until all three threads are done spamming.
230 * M_thread_join(t1, NULL);
231 * M_thread_join(t2, NULL);
232 * M_thread_join(t3, NULL);
233 *
234 * #ifdef DO_MEMBUF
235 * // Pull contents of membuf, dump to file.
236 * M_log_module_take_membuf(log, mod_membuf, &membuf);
237 *
238 * M_fs_file_open(&membuf_out, "~/Tmp/logs/log_membuf.txt", 0, M_FS_FILE_MODE_WRITE | M_FS_FILE_MODE_OVERWRITE, NULL);
239 * M_fs_file_write(membuf_out, (const unsigned char *)M_buf_peek(membuf), M_buf_len(membuf),
240 * NULL, M_FS_FILE_RW_NORMAL);
241 * M_fs_file_close(membuf_out);
242 *
243 * M_buf_cancel(membuf);
244 * #endif
245 *
246 * // Destroy the log. If internal workers are still processing messages, this will wait until they finish outputting
247 * // their internal message queues and exit. This ensures that we don't see any memory leaks at process exit.
248 * //
249 * // Wait up to five seconds for threads to finish writing.
250 * M_log_destroy_blocking(log, 5000);
251 *
252 * return EXIT_SUCCESS;
253 * }
254 * \endcode
255 *
256 * Example (simple logging to a file):
257 *
258 * \code{.c}
259 * #include <mstdlib/mstdlib.h>
260 * #include <mstdlib/mstdlib_log.h>
261 * #include <mstdlib/mstdlib_thread.h>
262 *
263 * typedef enum {
264 * TAG_1 = 1 << 0
265 * } tags_t;
266 *
267 * int main(int argc, char **argv)
268 * {
269 * M_log_t *log;
270 * M_log_module_t *mod_file;
271 * M_log_error_t res;
272 * size_t i;
273 *
274 * log = M_log_create(M_LOG_LINE_END_NATIVE, M_TRUE, NULL);
275 * M_log_set_tag_name(log, TAG_1, "tag_1_name");
276 * // Use /dev/stdout (*nix/macOS) as a pretend file. We could have specified an actual file that
277 * // exists or not but this is a good way to see what's happening.
278 * res = M_log_module_add_file(log, "/dev/stdout", 15, 150000, 0, 150000, NULL, NULL, &mod_file);
279 * if (res != M_LOG_SUCCESS) {
280 * M_fprintf(stderr, "Could not add file module: %s\n", M_log_err_to_str(res));
281 * M_log_destroy(log);
282 * return 1;
283 * } else {
284 * M_log_module_set_accepted_tags(log, mod_file, TAG_1);
285 * }
286 *
287 * for (i=0; i<12; i++) {
288 * M_log_printf(log, TAG_1, NULL, "Test.. %zu ...\n", i+1);
289 * M_thread_sleep(10*10000);
290 * }
291 *
292 * M_log_destroy_blocking(log, 5000);
293 * return 0;
294 * }
295 * \endcode
296 */
297
298#include <mstdlib/log/m_async_writer.h>
299#include <mstdlib/log/m_log.h>
300
301#endif /* MSTDLIB_LOG_H */
302