BitLab 0.1.0
BitLab: A Browser for the Bitcoin P2P Network and Blockchain
Loading...
Searching...
No Matches
log.c
Go to the documentation of this file.
1#include "log.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <stdarg.h>
6#include <string.h>
7#include <unistd.h>
8#include <errno.h>
9#include <pthread.h>
10#include <sys/file.h>
11#include <sys/stat.h>
12#include <sys/types.h>
13
14#include "utils.h"
15
16static struct loggers logs = { PTHREAD_MUTEX_INITIALIZER, {NULL}, 0 };
17
18static const char* logs_dir = NULL;
19
20const char* create_logs_dir()
21{
22 const char* home = getenv("HOME");
23 if (home == NULL)
24 return NULL;
25 const char* suffix = "/.bitlab/logs";
26 char* logs_dir = malloc(strlen(home) + strlen(suffix) + 1);
27 if (logs_dir == NULL)
28 return NULL;
29 strcpy(logs_dir, home);
30 strcat(logs_dir, suffix);
31 return logs_dir;
32}
33
34void init_logging(const char* filename)
35{
37 if (logs_dir != NULL)
38 {
39 free((void*)logs_dir);
40 }
42 struct stat st = { 0 };
43
44 if (stat(logs_dir, &st) == -1)
45 {
46 if (mkdir(logs_dir, 0700) != 0)
47 {
48 perror("Failed to create logs directory");
50 free((void*)logs_dir); // Free logs_dir on error
51 logs_dir = NULL;
52 return;
53 }
54 }
55
56 char full_path[BUFFER_SIZE];
57 snprintf(full_path, sizeof(full_path), "%s/%s", logs_dir, filename);
58
59 pthread_mutex_lock(&logs.log_mutex);
60 for (int i = 0; i < MAX_LOG_FILES; ++i)
61 {
62 if (logs.array[i] == NULL)
63 {
64 logs.array[i] = (struct logger*)malloc(sizeof(logger));
65 logs.array[i]->filename = (char*)malloc(strlen(full_path) + 1);
66 if (logs.array[i]->filename == NULL)
67 {
68 perror("Failed to allocate memory for filename");
69 free(logs.array[i]);
70 logs.array[i] = NULL;
71 break;
72 }
73 snprintf(logs.array[i]->filename, strlen(full_path) + 1, "%s", full_path);
74 logs.array[i]->file = fopen(full_path, "a");
75 if (logs.array[i]->file == NULL)
76 {
77 perror("Failed to open log file");
78 free(logs.array[i]->filename);
79 free(logs.array[i]);
80 logs.array[i] = NULL;
81 }
82 break;
83 }
84 }
86 pthread_mutex_unlock(&logs.log_mutex);
87}
88
89void log_message(log_level level, const char* filename, const char* source_file, const char* format, ...)
90{
91 if (logs_dir == NULL)
92 {
94 if (logs_dir == NULL)
95 {
96 fprintf(stderr, "Failed to create logs directory\n");
97 return;
98 }
99 }
100
101 char full_path[BUFFER_SIZE];
102 snprintf(full_path, sizeof(full_path), "%s/%s", logs_dir, filename);
103
104 pthread_mutex_lock(&logs.log_mutex);
105
106 FILE* log = NULL;
107 for (int i = 0; i < MAX_LOG_FILES; ++i)
108 {
109 if (logs.array[i] != NULL && !strcmp(logs.array[i]->filename, full_path))
110 {
111 log = logs.array[i]->file;
112 break;
113 }
114 }
115
116 if (log == NULL)
117 {
118 // Try to create and open the log file if it doesn't exist
119 log = fopen(full_path, "a");
120 if (log == NULL)
121 {
122 pthread_mutex_unlock(&logs.log_mutex);
123 fprintf(stderr, "Failed to create or open log file: %s\n", full_path);
124 return;
125 }
126
127 // Add the new log file to the logs array
128 for (int i = 0; i < MAX_LOG_FILES; ++i)
129 {
130 if (logs.array[i] == NULL)
131 {
132 logs.array[i] = (struct logger*)malloc(sizeof(logger));
133 logs.array[i]->filename = (char*)malloc(strlen(full_path) + 1);
134 if (logs.array[i]->filename == NULL)
135 {
136 perror("Failed to allocate memory for filename");
137 free(logs.array[i]);
138 logs.array[i] = NULL;
139 fclose(log);
140 pthread_mutex_unlock(&logs.log_mutex);
141 return;
142 }
143 snprintf(logs.array[i]->filename, strlen(full_path) + 1, "%s", full_path);
144 logs.array[i]->file = log;
145 break;
146 }
147 }
148 }
149
150 int timeout = 0;
151 int error = 0;
152
153 error = flock(fileno(log), LOCK_EX | LOCK_NB);
154 while (error == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
155 {
157 timeout += LOCKED_FILE_RETRY_TIME;
158
159 if (timeout > LOCKED_FILE_TIMEOUT)
160 {
161 fprintf(stderr, "Log file locking timed out: %s\n", full_path);
162 pthread_mutex_unlock(&logs.log_mutex);
163 return;
164 }
165 error = flock(fileno(log), LOCK_EX | LOCK_NB);
166 }
167
168 fseek(log, 0, SEEK_END);
169
170 const char* level_str;
171 switch (level)
172 {
173 case LOG_DEBUG: level_str = "DEBUG"; break;
174 case LOG_INFO: level_str = "INFO"; break;
175 case LOG_WARN: level_str = "WARN"; break;
176 case LOG_ERROR: level_str = "ERROR"; break;
177 case LOG_FATAL: level_str = "FATAL"; break;
178 default: level_str = "UNKNOWN"; break;
179 }
180
181 char timestamp[TIMESTAMP_LENGTH];
183
184 char message[1024];
185 va_list args;
186 va_start(args, format);
187 vsnprintf(message, sizeof(message), format, args);
188 va_end(args);
189
190 fprintf(log, "%s - %s - %s - %s\n", timestamp, level_str, source_file, message);
191
192 fflush(log);
193
194 flock(fileno(log), LOCK_UN);
195
196 pthread_mutex_unlock(&logs.log_mutex);
197}
198
200{
201 while (logs.is_initializing)
202 {
203 usleep(10000); // 10 ms
204 }
205 pthread_mutex_lock(&logs.log_mutex);
206 for (int i = 0; i < MAX_LOG_FILES; ++i)
207 {
208 if (logs.array[i] != NULL)
209 {
210 fclose(logs.array[i]->file);
211 if (logs.array[i]->filename != NULL)
212 free(logs.array[i]->filename);
213 free(logs.array[i]);
214 logs.array[i] = NULL;
215 }
216 }
217 pthread_mutex_unlock(&logs.log_mutex);
218 if (logs_dir != NULL)
219 {
220 free((void*)logs_dir);
221 logs_dir = NULL;
222 }
223}
void finish_logging()
Finish logging used to finish logging and close the log file.
Definition log.c:199
void log_message(log_level level, const char *filename, const char *source_file, const char *format,...)
Log a message used to log a message to the console or a file.
Definition log.c:89
static const char * logs_dir
Definition log.c:18
void init_logging(const char *filename)
Initialize logging used to initialize the logging system, open and preserve the log file.
Definition log.c:34
const char * create_logs_dir()
Create logs directory used to create the logs directory if it does not exist.
Definition log.c:20
static struct loggers logs
Definition log.c:16
#define MAX_LOG_FILES
Definition log.h:9
#define LOCKED_FILE_RETRY_TIME
Definition log.h:15
log_level
The log level enumeration used to define the level of logging that is being used.
Definition log.h:28
@ LOG_ERROR
Definition log.h:32
@ LOG_INFO
Definition log.h:30
@ LOG_DEBUG
Definition log.h:29
@ LOG_FATAL
Definition log.h:33
@ LOG_WARN
Definition log.h:31
#define LOCKED_FILE_TIMEOUT
Definition log.h:16
The logger structure used to store the logger for the logging system.
Definition log.h:43
char * filename
Definition log.h:44
FILE * file
Definition log.h:45
The loggers structure used to store the loggers for the logging system.
Definition log.h:56
logger * array[MAX_LOG_FILES]
Definition log.h:58
int is_initializing
Definition log.h:59
pthread_mutex_t log_mutex
Definition log.h:57
int fileno(FILE *__stream)
void get_formatted_timestamp(char *buffer, size_t buffer_size)
Get the formatted timestamp.
Definition utils.c:25
#define BUFFER_SIZE
Definition utils.h:12
void usleep(unsigned int usec)
#define TIMESTAMP_LENGTH
Definition utils.h:11