c-template
logger.h
Go to the documentation of this file.
1 /*! @file logger.h
2  * @brief a thread safe logger with optional printf style logging
3  * @details allows writing color coded logs to stdout, with optional file output as well. timestamps all logs, and provides optional printf style logging
4  * @note logf_func has a bug where some format is respected and others are not, consider the following from a `%s%s` format:
5  * - [error - Jul 06 10:01:07 PM] one<insert-tab-here>two
6  * - [warn - Jul 06 10:01:07 PM] one two
7  * @note warn, and info appear to not respect format, while debug and error do
8  * @todo
9  * - buffer logs and use a dedicated thread for writing (avoid blocking locks)
10  * - handling system signals (exit, kill, etc...)
11 */
12 
13 #pragma once
14 
15 #include <pthread.h>
16 #include <string.h>
17 #include <stdbool.h>
18 #include "colors.h"
19 
20 /*! @struct base struct used by the thread_logger
21 */
22 struct thread_logger;
23 
24 /*! @typedef specifies log_levels, typically used when determining function invocation by log_fn
25 */
26 typedef enum {
27  /*! indicates the message we are logging is of type info (color green) */
29  /*! indicates the message we are logging is of type warn (color yellow) */
31  /*! indicates the message we are logging is of type error (color red) */
33  /*! indicates the message we are logging is of type debug (color soft red) */
35 } LOG_LEVELS;
36 
37 /*! @typedef signature of pthread_mutex_unlock and pthread_mutex_lock used by the thread_logger
38  * @param mx pointer to a pthread_mutex_t type
39 */
40 typedef int (*mutex_fn)(pthread_mutex_t *mx);
41 
42 /*! @typedef signature used by the thread_logger for log_fn calls
43  * @param thl pointer to an instance of thread_logger
44  * @param file_descriptor file descriptor to write log messages to, if 0 then only stdout is used
45  * @param message the actual message we want to log
46  * @param level the log level to use (effects color used)
47 */
48 typedef void (*log_fn)(struct thread_logger *thl, int file_descriptor, char *message, LOG_LEVELS level);
49 /*! @typedef signatured used by the thread_logger for printf style log_fn calls
50  * @param thl pointer to an instance of thread_logger
51  * @param file_descriptor file descriptor to write log messages to, if 0 then only stdout is used
52  * @param level the log level to use (effects color used)
53  * @param message format string like `%sFOO%sBAR`
54  * @param ... values to supply to message
55 */
56 typedef void (*log_fnf)(struct thread_logger *thl, int file_descriptor, LOG_LEVELS level, char *message, ...);
57 
58 /*! @typedef a thread safe logger
59  * @brief guards all log calls with a mutex lock/unlock
60  * recommended usage is to call thread_logger:log(instance_of_thread_logger, char*_of_your_log_message, log_level)
61  * alternatively you can call the `*_log` functions directly
62 */
63 typedef struct thread_logger {
64  // todo(bonedaddy): make atomic
65  bool debug;
66  pthread_mutex_t mutex;
72 
73 /*! @typedef a wrapper around thread_logger that enables file logging
74  * @brief like thread_logger but also writes to a file
75  * @todo
76  * - enable log rotation
77 */
78 typedef struct file_logger {
81 } file_logger;
82 
83 /*! @brief returns a new thread safe logger
84  * if with_debug is false, then all debug_log calls will be ignored
85  * @param with_debug whether to enable debug logging, if false debug log calls will be ignored
86 */
87 thread_logger *new_thread_logger(bool with_debug);
88 
89 /*! @brief returns a new file_logger
90  * Calls new_thread_logger internally
91  * @param output_file the file we will dump logs to. created if not exists and is appended to
92 */
93 file_logger *new_file_logger(char *output_file, bool with_debug);
94 
95 /*! @brief free resources for the threaded logger
96  * @param thl the thread_logger instance to free memory for
97 */
99 
100 /*! @brief free resources for the file ogger
101  * @param fhl the file_logger instance to free memory for. also frees memory for the embedded thread_logger and closes the open file
102 */
103 void clear_file_logger(file_logger *fhl);
104 
105 /*! @brief main function you should call, which will delegate to the appopriate *_log function
106  * @param thl pointer to an instance of thread_logger
107  * @param file_descriptor file descriptor to write log messages to, if 0 then only stdout is used
108  * @param message the actual message we want to log
109  * @param level the log level to use (effects color used)
110 */
111 void log_func(thread_logger *thl, int file_descriptor, char *message, LOG_LEVELS level);
112 
113 /*! @brief like log_func but for formatted logs
114  * @param thl pointer to an instance of thread_logger
115  * @param file_descriptor file descriptor to write log messages to, if 0 then only stdout is used
116  * @param level the log level to use (effects color used)
117  * @param message format string like `<percent-sign>sFOO<percent-sign>sBAR`
118  * @param ... values to supply to message
119 */
120 void logf_func(thread_logger *thl, int file_descriptor, LOG_LEVELS level, char *message, ...);
121 
122 
123 /*! @brief logs a debug styled message - called by log_fn
124  * @param thl pointer to an instance of thread_logger
125  * @param file_descriptor file descriptor to write log messages to in addition to stdout logging. if 0 only stdout is used
126  * @param message the actuall message to log
127 */
128 void debug_log(thread_logger *thl, int file_descriptor, char *message);
129 
130 /*! @brief logs a warned styled message - called by log_fn
131  * @param thl pointer to an instance of thread_logger
132  * @param file_descriptor file descriptor to write log messages to in addition to stdout logging. if 0 only stdout is used
133  * @param message the actuall message to log
134 */
135 void warn_log(thread_logger *thl, int file_descriptor, char *message);
136 
137 /*! @brief logs an error styled message - called by log_fn
138  * @param thl pointer to an instance of thread_logger
139  * @param file_descriptor file descriptor to write log messages to in addition to stdout logging. if 0 only stdout is used
140  * @param message the actuall message to log
141 */
142 void error_log(thread_logger *thl, int file_descriptor, char *message);
143 
144 /*! @brief logs an info styled message - called by log_fn
145  * @param thl pointer to an instance of thread_logger
146  * @param file_descriptor file descriptor to write log messages to in addition to stdout logging. if 0 only stdout is used
147  * @param message the actuall message to log
148 */
149 void info_log(thread_logger *thl, int file_descriptor, char *message);
150 
151 /*! @brief used to write a log message to file
152  * @param thl pointer to an instance of thread_logger
153  * @param file_descriptor file descriptor to write log messages to in addition to stdout logging. if 0 only stdout is used
154  * @param message the actuall message to log
155 */
156 int write_file_log(int file_descriptor, char *message);
clear_file_logger
void clear_file_logger(file_logger *fhl)
free resources for the file ogger
Definition: logger.c:228
clear_thread_logger
void clear_thread_logger(thread_logger *thl)
free resources for the threaded logger
Definition: logger.c:224
log_fnf
void(* log_fnf)(struct thread_logger *thl, int file_descriptor, LOG_LEVELS level, char *message,...)
Definition: logger.h:56
error_log
void error_log(thread_logger *thl, int file_descriptor, char *message)
logs an error styled message - called by log_fn
Definition: logger.c:183
new_file_logger
file_logger * new_file_logger(char *output_file, bool with_debug)
returns a new file_logger Calls new_thread_logger internally
Definition: logger.c:43
LOG_LEVELS_DEBUG
@ LOG_LEVELS_DEBUG
Definition: logger.h:34
thread_logger::lock
mutex_fn lock
Definition: logger.h:67
LOG_LEVELS_ERROR
@ LOG_LEVELS_ERROR
Definition: logger.h:32
file_logger::thl
thread_logger * thl
Definition: logger.h:79
log_fn
void(* log_fn)(struct thread_logger *thl, int file_descriptor, char *message, LOG_LEVELS level)
Definition: logger.h:48
thread_logger::unlock
mutex_fn unlock
Definition: logger.h:68
thread_logger::log
log_fn log
Definition: logger.h:69
thread_logger::logf
log_fnf logf
Definition: logger.h:70
logf_func
void logf_func(thread_logger *thl, int file_descriptor, LOG_LEVELS level, char *message,...)
like log_func but for formatted logs
Definition: logger.c:95
info_log
void info_log(thread_logger *thl, int file_descriptor, char *message)
logs an info styled message - called by log_fn
Definition: logger.c:143
thread_logger::mutex
pthread_mutex_t mutex
Definition: logger.h:66
thread_logger::debug
bool debug
Definition: logger.h:65
write_file_log
int write_file_log(int file_descriptor, char *message)
used to write a log message to file
Definition: logger.c:73
LOG_LEVELS
LOG_LEVELS
Definition: logger.h:26
thread_logger
struct thread_logger thread_logger
thread_logger
Definition: logger.h:63
colors.h
macros and utilities for printing color to stdout from https://www.quora.com/How-do-I-print-a-colored...
log_func
void log_func(thread_logger *thl, int file_descriptor, char *message, LOG_LEVELS level)
main function you should call, which will delegate to the appopriate *_log function
Definition: logger.c:112
file_logger::file_descriptor
int file_descriptor
Definition: logger.h:80
LOG_LEVELS_INFO
@ LOG_LEVELS_INFO
Definition: logger.h:28
new_thread_logger
thread_logger * new_thread_logger(bool with_debug)
returns a new thread safe logger if with_debug is false, then all debug_log calls will be ignored
Definition: logger.c:28
file_logger
struct file_logger file_logger
file_logger
Definition: logger.h:78
LOG_LEVELS_WARN
@ LOG_LEVELS_WARN
Definition: logger.h:30
warn_log
void warn_log(thread_logger *thl, int file_descriptor, char *message)
logs a warned styled message - called by log_fn
Definition: logger.c:161
debug_log
void debug_log(thread_logger *thl, int file_descriptor, char *message)
logs a debug styled message - called by log_fn
Definition: logger.c:201
mutex_fn
int(* mutex_fn)(pthread_mutex_t *mx)
Definition: logger.h:40