DungeonCrawl
Loading...
Searching...
No Matches
logger.c
Go to the documentation of this file.
1
5
6#include "logger.h"
7
9#include "logger_config.h"
10#include "ringbuffer.h"
11
12#include <stdarg.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <sys/stat.h>
16#include <time.h>
17
18#ifdef _WIN32
19 #include <direct.h>
20
21 #define STAT_STRUCT struct _stat
22 #define STAT_FUNC _stat
23 #define MKDIR(path) _mkdir(path)
24 #define PATH_SEP "\\"
25#else
26 #include <dirent.h>
27
28 #define STAT_STRUCT struct stat
29 #define STAT_FUNC stat
30 #define MKDIR(path) mkdir(path, 0755)
31 #define PATH_SEP "/"
32#endif
33
34#define MAX_PATH_SIZE 4096
35
36// relative directory from the project root
37#define LOG_DIRECTORY "log"
38#define LOG_FILE_FORMAT "log-%d.txt"
39
40#define TIMESTAMP_FORMAT "%Y-%m-%d %H:%M:%S"
41#define MSG_FORMAT "[%s] [%s] [%s] : %s\n"
42
43const char* log_level_str[] = {"DEBUG", "FINE", "INFO", "WARNING", "ERROR"};
44
45// === internal functions ===
52int ensure_log_dir(void);
53
62int open_log_file(int is_init);
63
74int check_log_file(void);
75
82int get_latest_file_id(void);
83
89
94void log_writer_thread(void);
95
96// === global variables ===
97FILE* log_file = NULL;
98ring_buffer_t log_buffer;
99
100//states if the file writing thread is still running, if set to false, the thread terminates or is terminated
101bool logger_is_running = false;
102//the id of the used file
103int file_id = 0;
104
105int ensure_log_dir(void) {
106 STAT_STRUCT st;
107
108 if (STAT_FUNC(LOG_DIRECTORY, &st) == -1) {
109 if (MKDIR(LOG_DIRECTORY) == -1) {
110 return 1;
111 }
112 }
113 return 0;
114}
115
116
117int open_log_file(const int is_init) {
118 char name[16];
119 snprintf(name, sizeof(name), LOG_FILE_FORMAT, file_id);
120
121 char filename[256];
122 snprintf(filename, sizeof(filename), "%s" PATH_SEP "%s", LOG_DIRECTORY, name);
123
124 if (is_init) {
125 // Check if the file already exists
126 FILE* existing_file = fopen(filename, "r");
127 if (existing_file) {
128 fclose(existing_file);
129
130 // If the file already exists, remove it
131 if (remove(filename) != 0) {
132 return 1;
133 }
134 }
135 }
136
137 log_file = fopen(filename, "a");
138 if (!log_file) {
139 return 1;
140 }
141 return 0;
142}
143
144int check_log_file(void) {
145 if (log_file) {
146 fseek(log_file, 0, SEEK_END);
147 const long file_size = ftell(log_file);
148 if (file_size >= MAX_FILE_SIZE) {
149 fclose(log_file);//close the current file
150 file_id = (file_id + 1) % MAX_N_FILES;
151
152 // open a new file
153 if (open_log_file(1) != 0) {
154 // failed to open a new file
155 return 1;
156 }
157 }
158 }
159 return 0;
160}
161
163 if (ensure_log_dir() != 0) {
164 // failed to ensure the log directory
165 return -1;
166 }
167
168 int latest_id = 0;
169 DIR* dir = opendir(LOG_DIRECTORY);
170 struct dirent* entry;
171 time_t latest_time = 0;
172
173 if (!dir) {
174 // failed to open the log directory
175 return -1;
176 }
177
178 // Iterate through the log directory and find the latest used file
179 while ((entry = readdir(dir)) != NULL) {
180 int id;
181 struct stat file_stat;
182
183 if (sscanf(entry->d_name, LOG_FILE_FORMAT, &id) == 1 && id >= 0 && id < MAX_N_FILES) {
184 char filepath[MAX_PATH_SIZE];
185 snprintf(filepath, sizeof(filepath), "%s/%s", LOG_DIRECTORY, entry->d_name);
186
187 if (stat(filepath, &file_stat) == 0) {
188 if (file_stat.st_mtime > latest_time) {
189 // found a newer file
190 latest_time = file_stat.st_mtime;
191 latest_id = id;
192 }
193 }
194 }
195 }
196
197 closedir(dir);
198 return latest_id;
199}
200
202 logger_is_running = true;
203
205}
206
208 bool running = true;
209 while (running) {
210 char log_msg[MAX_MSG_SIZE];
211 if (read_from_ringbuffer(&log_buffer, log_msg) == 0) {
212 // message successfully read from the ringbuffer
214 if (log_file) {
215 fprintf(log_file, "%s", log_msg);
216 fflush(log_file);
217 }
218 }
219 if (!logger_is_running) {
220 // thread must be terminated
221 running = false;
222 }
223 }
224 //closes all pressures
225 if (log_file) {
226 fclose(log_file);
227 log_file = NULL;
228 }
229 free_ringbuffer(&log_buffer);
230}
231
232void init_logger(void) {
233 // ensures the init_logger can only be called when the file is null
234 if (log_file == NULL) {
235 if (init_ringbuffer(&log_buffer) == 0) {
236 file_id = get_latest_file_id();
237 if (file_id == -1 || open_log_file(0) != 0) {
238 fclose(log_file);
239 } else {
241 }
242 }
243 }
244}
245
246void log_msg(const log_level_t level, const char* module, const char* format, ...) {
247 if (log_file == NULL || !logger_is_running) {
248 // logger is not initialized or not running
249 return;
250 }
251
252 //get timestamp
253 const time_t now = time(NULL);
254 const struct tm* tm = localtime(&now);
255
256 char timestamp[32];
257 strftime(timestamp, sizeof(timestamp), TIMESTAMP_FORMAT, tm);
258
259 const char* log_level;
260 if (level >= MAX_LOG_LEVEL) {
261 log_level = log_level_str[INFO];
262 } else {
263 log_level = log_level_str[level];
264 }
265
266 va_list args;
267 va_start(args, format);
268 char msg[MAX_MSG_SIZE - MAX_HEADER_SIZE];
269 vsnprintf(msg, sizeof(msg), format, args);
270
271 char log_msg[MAX_MSG_SIZE];
272 snprintf(log_msg, MAX_MSG_SIZE, MSG_FORMAT, timestamp, log_level, module, msg);
273 va_end(args);
274
275 write_to_ringbuffer(&log_buffer, log_msg);
276}
277
278void shutdown_logger(void) {
279 logger_is_running = false;
280}
int ensure_log_dir(void)
Ensures that the predefined log directory already exists, if not create a new one.
Definition logger.c:105
int open_log_file(int is_init)
Opens the log file with the current saved file id in appended modus.
Definition logger.c:117
void shutdown_logger(void)
Shuts down the logging system for the application.
Definition logger.c:278
int check_log_file(void)
This function should be called whenever a new log msg must be written.
Definition logger.c:144
int get_latest_file_id(void)
This function gets the latest file id from the log directory.
Definition logger.c:162
void init_logger(void)
Initializes the logging system for the application.
Definition logger.c:232
void start_log_writer_thread(void)
Starts the log writer thread.
Definition logger.c:201
void log_msg(const log_level_t level, const char *module, const char *format,...)
Logs a formatted message with a specified log level and module.
Definition logger.c:246
void log_writer_thread(void)
This function will be called from a different thread to read from the ringbuffer and then write in th...
Definition logger.c:207
Header file for logging functionality of the game.
Configuration file for the logger.
void start_simple_thread(void(*thread_func)(void))
Starts a new thread with the given function.
Exposes functions for the thread_handler.