CAEN Utility  2.0.2
Utilities for CAEN projects
CAENLogger.c
Go to the documentation of this file.
1 /******************************************************************************
2 *
3 * CAEN SpA - Software Division
4 * Via Vetraia, 11 - 55049 - Viareggio ITALY
5 * +39 0594 388 398 - www.caen.it
6 *
7 *******************************************************************************
8 *
9 * Copyright (C) 2019-2022 CAEN SpA
10 *
11 * This file is part of the CAEN Utility.
12 *
13 * The CAEN Utility is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 3 of the License, or (at your option) any later version.
17 *
18 * The CAEN Utility is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with the CAEN Utility; if not, see
25 * https://www.gnu.org/licenses/.
26 *
27 * SPDX-License-Identifier: LGPL-3.0-or-later
28 *
29 ***************************************************************************/
37 #include <CAENLogger.h>
38 
39 #include <stdlib.h>
40 
41 #include <CAENMultiplatform.h>
42 #include <CAENThread.h>
43 
44 #ifdef LOGROTATION
45 #include <fcntl.h>
46 // TODO change the default fifo name to something unambiguous between different processes using c_logger.
47 #define LOGGER_ROTATE_FIFO_NAME "/tmp/rotatefifo"
48 static int32_t rotatepipe = -1;
49 #endif
50 
51 struct global_logger {
52  FILE *file;
53  FILE *file_default;
54  char *filename;
55  char *lastHeader;
58  uint32_t configuredMask; // set to lConfiguredMask when configured
59 };
60 
61 static const uint32_t lConfiguredMask = 0xcafecae0;
62 
63 // default log level is all (for retrocomp)
65 static uint64_t lStartTimeMs = 0;
66 static struct global_logger *lGlobal = NULL;
67 static const char lSeverityMap[][3] = {
68  "UU", // UNKNOWN
69  "EE", // ERROR
70  "WW", // WARNING
71  "UU", // UNKNOWN
72  "II", // INFO
73  "UU", // UNKNOWN
74  "UU", // UNKNOWN
75  "UU", // UNKNOWN
76  "DD", // DEBUG
77 };
78 
79 static int32_t _lopenfile(void) {
80  if (lGlobal->file != NULL)
81  return c_logger_Success; // assume already opened
82 
83  const char *openFlags;
84 
85  switch (lGlobal->openMode) {
86  case c_logger_OpenMode_W: openFlags = "w"; break;
87  case c_logger_OpenMode_A: openFlags = "a"; break;
88  case c_logger_OpenMode_RW: openFlags = "r+"; break;
89  case c_logger_OpenMode_RA: openFlags = "a+"; break;
90  default: return c_logger_WrongOpenMode;
91  }
92 
93  lGlobal->file = fopen(lGlobal->filename, openFlags);
94  if (lGlobal->file == NULL)
96  return c_logger_Success;
97 }
98 
99 #ifdef LOGROTATION
100 static void _lcheckrotation(void) {
101  if (rotatepipe == -1)
102  rotatepipe = open(LOGGER_ROTATE_FIFO_NAME, O_RDONLY | O_NDELAY);
103  if (rotatepipe != -1) {
104  // Read from rotatepipe and check if a rotation has been requested
105  char rotbuff[11];
106  ssize_t numb = read(rotatepipe, rotbuff, 10);
107  rotbuff[numb] = '\0';
108  if (strcmp(rotbuff, "ROTATE\n") == 0) {
109  // logrotate changed the filename. Close and reopen it.
110  if (lGlobal != NULL) {
111  // IMPORTANT NOTE: no need to lock here since logger is already locked.
112  if (lGlobal->file != NULL) {
113  FILE *oldFileBkp = lGlobal->file;
114  lGlobal->file = NULL;
115  if (_lopenfile() != c_logger_Success) {
116  // ERROR opening new file. keep old one..
117  // Check if we can log an error even inside the logger itself!
118  lGlobal->file = oldFileBkp;
119  }
120  else {
121  fclose(oldFileBkp);
122  }
123  }
124  }
125  }
126  }
127 }
128 #endif
129 
130 static bool _lcheckmask(uint32_t mask) {
131  return (mask == lConfiguredMask);
132 }
133 
134 static bool _lisconfigured(void) {
135  bool configured = FALSE;
136 
137  if (lGlobal != NULL)
138  configured = _lcheckmask(lGlobal->configuredMask);
139 
140  return configured;
141 }
142 
143 // secure version withmutex lock
144 static bool _lisconfigured_locked(void) {
145  bool configured = FALSE;
146 
147  if (lGlobal != NULL) {
148  // _lconfigure lock the mutex while configuring
149  c_mutex_lock(lGlobal->mutex);
150  configured = _lcheckmask(lGlobal->configuredMask);
151  c_mutex_unlock(lGlobal->mutex);
152  }
153 
154  return configured;
155 }
156 
157 static void _lfreeglobal(void) {
158 #ifdef LOGROTATION
159  if (rotatepipe != -1) {
160  close(rotatepipe);
161  rotatepipe = -1;
162  }
163 #endif
164 
165  if (lGlobal == NULL)
166  return;
167 
168  c_mutex_lock(lGlobal->mutex);
169 
170  // close the file if opened
171  if (lGlobal->file != NULL)
172  fclose(lGlobal->file);
173 
174  c_mutex_unlock(lGlobal->mutex);
175 
176  c_mutex_destroy(lGlobal->mutex);
177  c_free(lGlobal->mutex);
178  c_free(lGlobal->lastHeader);
179  c_free(lGlobal->filename);
180  c_free(lGlobal);
181 
182  lGlobal = NULL;
183 }
184 
185 static int32_t c_attribute_format(4, 0) _lvprintf(const char *module_tree, c_logger_Severity s, int32_t line, const char *__restrict fmt, va_list args) {
186 #ifdef LOGROTATION
187  _lcheckrotation();
188 #endif
189 
190  const double time = (double)(c_get_time() - lStartTimeMs) * 0.001;
191 
192  // [time in seconds, 3 decimals and 12 chars in total][severity][source_file]
193  if (fprintf(lGlobal->file, "[%12.3f][%s][%s::%"PRIi32"]: ", time, lSeverityMap[s], module_tree, line) < 0)
194  return c_logger_PrintError;
195 
196  if (vfprintf(lGlobal->file, fmt, args) < 0)
197  return c_logger_PrintError;
198 
199  if (fputc('\n', lGlobal->file) == EOF)
200  return c_logger_PrintError;
201 
202  if (fflush(lGlobal->file) == EOF)
203  return c_logger_PrintError;
204 
205  return c_logger_Success;
206 }
207 
208 // secure version with null check and mutex lock
209 static int32_t _lvprintf_s(const char *module_tree, c_logger_Severity s, int32_t line, const char *__restrict fmt, va_list args) {
210  if (module_tree == NULL || fmt == NULL)
212 
213  c_mutex_lock(lGlobal->mutex);
214  int32_t res = _lvprintf(module_tree, s, line, fmt, args);
215  c_mutex_unlock(lGlobal->mutex);
216 
217  return res;
218 }
219 
220 static int32_t _lconfigure(const char *filename, c_logger_OpenMode_t om) {
221  int32_t res = c_logger_Success;
222  bool opening = FALSE;
223  bool mutex_locked = FALSE;
224  char *expandedFileName = NULL;
225 
226  // use locked version, as another thread may be in this same function
227  // and lGlobal could be != NULL while still not configured.
228  if (_lisconfigured_locked()) {
229  // logger already configured
231  goto QuitFunction;
232  }
233 
234  lGlobal = c_malloc(sizeof(*lGlobal));
235  if (lGlobal == NULL) {
236  res = c_logger_OutOfMemory;
237  goto QuitFunction;
238  }
239 
240  opening = TRUE;
241 
242  // init the mutex
243  lGlobal->mutex = c_malloc(sizeof(*lGlobal->mutex));
244  if (lGlobal->mutex == NULL) {
245  res = c_logger_OutOfMemory;
246  goto QuitFunction;
247  }
248 
249  c_mutex_init(lGlobal->mutex);
250  c_mutex_lock(lGlobal->mutex);
251  mutex_locked = TRUE;
252 
253  // Get optional verbosity level from from env. variable
254  // Otherwise, keep default value
255  char *verbosity_env = getenv(LOG_ENV_LEVEL);
256  if (verbosity_env != NULL) {
257  uint32_t level = (uint32_t)strtoul(verbosity_env, NULL, 0);
258  lSeverityMask = (UINT32_C(1) << level) - 1;
259  }
260 
261  // Get optional filename from env. variable
262  char *filename_env = getenv(LOG_ENV_FILENAME);
263 
264  const char *real_filename = (filename_env != NULL) ? filename_env : filename;
265 
266  // expand env variables in the string filename
267  // allocate expandedFileName inside!
268  expandedFileName = c_getExpandedString(real_filename);
269  if (expandedFileName == NULL) {
271  goto QuitFunction;
272  }
273 
274  // set the filename
275  lGlobal->filename = expandedFileName;
276 
277  // initialize the last header string
278  lGlobal->lastHeader = c_calloc(2, sizeof(*lGlobal->lastHeader));
279  if (lGlobal->lastHeader == NULL) {
280  res = c_logger_OutOfMemory;
281  goto QuitFunction;
282  }
283 
284  // open the file
285  lGlobal->openMode = om;
286  lGlobal->file = NULL;
287  res = _lopenfile();
288  if (res != c_logger_Success)
289  goto QuitFunction;
290 
291  // Store f pointer here, to be used by c_lresumefile() after a call to c_lchangefile()
292  lGlobal->file_default = lGlobal->file;
293 
294  // set configured mask
295  lGlobal->configuredMask = lConfiguredMask;
296 
297 QuitFunction:
298  if (mutex_locked)
299  c_mutex_unlock(lGlobal->mutex);
300 
301  if (res != c_logger_Success && opening)
302  _lfreeglobal();
303 
304  return res;
305 }
306 
307 // PUBLIC
308 
309 void c_lsetst(uint64_t time) {
310  lStartTimeMs = time;
311 }
312 
313 int32_t c_lchangefile(FILE *file) {
314  int32_t res = c_logger_Success;
315 
316  // First of all check if the global internal logger is configured
317  if (!_lisconfigured()) {
319  goto QuitFunction;
320  }
321 
322  // Set lGlobal->file to new file
323  lGlobal->file = file;
324 
325 QuitFunction:
326  return res;
327 }
328 
329 int32_t c_lresumefile(void) {
330  int32_t res = c_logger_Success;
331 
332  // First of all check if the global internal logger is configured
333  if (!_lisconfigured()) {
335  goto QuitFunction;
336  }
337 
338  // Set lGlobal->file to new file
339  lGlobal->file = lGlobal->file_default;
340 
341 QuitFunction:
342  return res;
343 }
344 
345 int32_t c_lsetsm(uint32_t sevMask) {
346  lSeverityMask = sevMask;
347  return c_logger_Success;
348 }
349 
350 int32_t c_lgetsm(uint32_t *sevMask) {
351  if (sevMask != NULL) {
352  *sevMask = lSeverityMask;
353  return c_logger_Success;
354  }
356 }
357 
358 int32_t c_lprintf(const c_locallogger_t *locallogger, c_logger_Severity s, int32_t line, const char *__restrict fmt, ...) {
359  va_list args;
360  va_start(args, fmt);
361  int32_t ret = c_lvprintf(locallogger, s, line, fmt, args);
362  va_end(args);
363  return ret;
364 }
365 
366 int32_t c_lvprintf(const c_locallogger_t *locallogger, c_logger_Severity s, int32_t line, const char *__restrict fmt, va_list args) {
367  int32_t res = c_logger_Success;
368 
369  if (locallogger == NULL || fmt == NULL) {
371  goto QuitFunction;
372  }
373 
374  if (!_lisconfigured()) {
375  res = _lconfigure(locallogger->filename, c_logger_OpenMode_W);
376  if (res != c_logger_Success)
377  goto QuitFunction;
378  }
379 
380  if (lSeverityMask & s) {
381  res = _lvprintf_s(locallogger->moduletree, s, line, fmt, args);
382  if (res != c_logger_Success)
383  goto QuitFunction;
384  }
385 
386 QuitFunction:
387  return res;
388 }
389 
390 void c_ldeinit(void) {
391  // deinit all allocated c_logger_t*
392  if (!_lisconfigured_locked())
393  return;
394 
395  // deinit lGlobal
396  _lfreeglobal();
397 }
int32_t c_mutex_lock(c_mutex_t *m)
Definition: CAENThread.c:316
void c_lsetst(uint64_t time)
Definition: CAENLogger.c:309
int32_t c_lprintf(const c_locallogger_t *locallogger, c_logger_Severity s, int32_t line, const char *__restrict fmt,...)
Definition: CAENLogger.c:358
int32_t c_lsetsm(uint32_t sevMask)
Definition: CAENLogger.c:345
char * filename
Definition: CAENLogger.c:54
void c_free(void *ptr)
void * c_calloc(size_t nmemb, size_t size)
static bool _lisconfigured_locked(void)
Definition: CAENLogger.c:144
int32_t c_mutex_init(c_mutex_t *m)
Definition: CAENThread.c:288
int32_t c_lchangefile(FILE *file)
Definition: CAENLogger.c:313
FILE * file
Definition: CAENLogger.c:52
int32_t c_mutex_destroy(c_mutex_t *m)
Definition: CAENThread.c:390
static struct global_logger * lGlobal
Definition: CAENLogger.c:66
static int32_t _lvprintf_s(const char *module_tree, c_logger_Severity s, int32_t line, const char *__restrict fmt, va_list args)
Definition: CAENLogger.c:209
static const char lSeverityMap[][3]
Definition: CAENLogger.c:67
FILE * file_default
Definition: CAENLogger.c:53
int32_t c_lgetsm(uint32_t *sevMask)
Definition: CAENLogger.c:350
c_mutex_t * mutex
Definition: CAENLogger.c:56
static bool _lisconfigured(void)
Definition: CAENLogger.c:134
const char *const moduletree
uint64_t c_get_time(void)
Get time in milliseconds since 00:00:00 UTC, January 1, 1970.
c_logger_OpenMode_t
#define c_attribute_format(_FMT_, _ARGS_)
Definition: CAENUtility.h:173
#define LOG_ENV_FILENAME
Definition: CAENLogger.h:60
void c_ldeinit(void)
Definition: CAENLogger.c:390
#define TRUE
#define FALSE
Functions to handle threads, inspired to C11 threads.h.
c_logger_OpenMode_t openMode
Definition: CAENLogger.c:57
char * c_getExpandedString(const char *filename)
void * c_malloc(size_t size)
Generic wrappers to platform-dependent functions.
static uint32_t lSeverityMask
Definition: CAENLogger.c:64
static int32_t _lconfigure(const char *filename, c_logger_OpenMode_t om)
Definition: CAENLogger.c:220
const char *const filename
uint32_t configuredMask
Definition: CAENLogger.c:58
Logger implemented in C.
pthread_mutex_t c_mutex_t
Mutex type.
int32_t c_lvprintf(const c_locallogger_t *locallogger, c_logger_Severity s, int32_t line, const char *__restrict fmt, va_list args)
Definition: CAENLogger.c:366
int32_t c_lresumefile(void)
Definition: CAENLogger.c:329
static bool _lcheckmask(uint32_t mask)
Definition: CAENLogger.c:130
int32_t c_mutex_unlock(c_mutex_t *m)
Definition: CAENThread.c:367
char * lastHeader
Definition: CAENLogger.c:55
static void _lfreeglobal(void)
Definition: CAENLogger.c:157
static int32_t _lvprintf(const char *module_tree, c_logger_Severity s, int32_t line, const char *__restrict fmt, va_list args)
Definition: CAENLogger.c:185
static uint64_t lStartTimeMs
Definition: CAENLogger.c:65
#define LOG_ENV_LEVEL
Definition: CAENLogger.h:61
c_logger_Severity
static int32_t _lopenfile(void)
Definition: CAENLogger.c:79
static const uint32_t lConfiguredMask
Definition: CAENLogger.c:61