Log.c
Go to the documentation of this file.
1 /*******************************************************************************
2  * Copyright (c) 2009, 2018 IBM Corp.
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v2.0
6  * and Eclipse Distribution License v1.0 which accompany this distribution.
7  *
8  * The Eclipse Public License is available at
9  * https://www.eclipse.org/legal/epl-2.0/
10  * and the Eclipse Distribution License is available at
11  * http://www.eclipse.org/org/documents/edl-v10.php.
12  *
13  * Contributors:
14  * Ian Craggs - initial API and implementation and/or initial documentation
15  * Ian Craggs - updates for the async client
16  * Ian Craggs - fix for bug #427028
17  *******************************************************************************/
18 
26 #include "Log.h"
27 #include "MQTTPacket.h"
28 #include "MQTTProtocol.h"
29 #include "MQTTProtocolClient.h"
30 #include "Messages.h"
31 #include "LinkedList.h"
32 #include "StackTrace.h"
33 #include "Thread.h"
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <time.h>
39 #include <string.h>
40 
41 #if !defined(_WIN32) && !defined(_WIN64)
42 #include <syslog.h>
43 #include <sys/stat.h>
44 #define GETTIMEOFDAY 1
45 #else
46 #define snprintf _snprintf
47 #endif
48 
49 #if defined(GETTIMEOFDAY)
50  #include <sys/time.h>
51 #else
52  #include <sys/timeb.h>
53 #endif
54 
55 #if !defined(_WIN32) && !defined(_WIN64)
56 
59 #define _unlink unlink
60 #endif
61 
62 
63 #if !defined(min)
64 #define min(A,B) ( (A) < (B) ? (A):(B))
65 #endif
66 
68 {
70  400,
72 };
73 
74 #define MAX_FUNCTION_NAME_LENGTH 256
75 
76 typedef struct
77 {
78 #if defined(GETTIMEOFDAY)
79  struct timeval ts;
80 #else
81  struct timeb ts;
82 #endif
84  int number;
85  int thread_id;
86  int depth;
88  int line;
89  int has_rc;
90  int rc;
91  enum LOG_LEVELS level;
92 } traceEntry;
93 
94 static int start_index = -1,
96 static traceEntry* trace_queue = NULL;
97 static int trace_queue_size = 0;
98 
99 static FILE* trace_destination = NULL;
100 static char* trace_destination_name = NULL;
101 static char* trace_destination_backup_name = NULL;
102 static int lines_written = 0;
103 static int max_lines_per_file = 1000;
106 static traceEntry* Log_pretrace(void);
107 static char* Log_formatTraceEntry(traceEntry* cur_entry);
108 static void Log_output(enum LOG_LEVELS log_level, const char *msg);
109 static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry);
110 static void Log_trace(enum LOG_LEVELS log_level, const char *buf);
111 #if 0
112 static FILE* Log_destToFile(const char *dest);
113 static int Log_compareEntries(const char *entry1, const char *entry2);
114 #endif
115 
116 static int sametime_count = 0;
117 #if defined(GETTIMEOFDAY)
118 struct timeval now_ts, last_ts;
119 #else
120 struct timeb now_ts, last_ts;
121 #endif
122 static char msg_buf[512];
123 
124 #if defined(_WIN32) || defined(_WIN64)
126 #else
127 static pthread_mutex_t log_mutex_store = PTHREAD_MUTEX_INITIALIZER;
129 #endif
130 
131 
133 {
134  int rc = SOCKET_ERROR;
135  char* envval = NULL;
136 #if !defined(_WIN32) && !defined(_WIN64)
137  struct stat buf;
138 #endif
139 
140  if ((trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries)) == NULL)
141  goto exit;
142  trace_queue_size = trace_settings.max_trace_entries;
143 
144  if ((envval = getenv("MQTT_C_CLIENT_TRACE")) != NULL && strlen(envval) > 0)
145  {
146  if (strcmp(envval, "ON") == 0 || (trace_destination = fopen(envval, "w")) == NULL)
147  trace_destination = stdout;
148  else
149  {
150  if ((trace_destination_name = malloc(strlen(envval) + 1)) == NULL)
151  {
152  free(trace_queue);
153  goto exit;
154  }
155  strcpy(trace_destination_name, envval);
156  if ((trace_destination_backup_name = malloc(strlen(envval) + 3)) == NULL)
157  {
158  free(trace_queue);
160  goto exit;
161  }
163  }
164  }
165  if ((envval = getenv("MQTT_C_CLIENT_TRACE_MAX_LINES")) != NULL && strlen(envval) > 0)
166  {
167  max_lines_per_file = atoi(envval);
168  if (max_lines_per_file <= 0)
169  max_lines_per_file = 1000;
170  }
171  if ((envval = getenv("MQTT_C_CLIENT_TRACE_LEVEL")) != NULL && strlen(envval) > 0)
172  {
173  if (strcmp(envval, "MAXIMUM") == 0 || strcmp(envval, "TRACE_MAXIMUM") == 0)
174  trace_settings.trace_level = TRACE_MAXIMUM;
175  else if (strcmp(envval, "MEDIUM") == 0 || strcmp(envval, "TRACE_MEDIUM") == 0)
176  trace_settings.trace_level = TRACE_MEDIUM;
177  else if (strcmp(envval, "MINIMUM") == 0 || strcmp(envval, "TRACE_MINIMUM") == 0)
178  trace_settings.trace_level = TRACE_MINIMUM;
179  else if (strcmp(envval, "PROTOCOL") == 0 || strcmp(envval, "TRACE_PROTOCOL") == 0)
181  else if (strcmp(envval, "ERROR") == 0 || strcmp(envval, "TRACE_ERROR") == 0)
183  }
184  Log_output(TRACE_MINIMUM, "=========================================================");
185  Log_output(TRACE_MINIMUM, " Trace Output");
186  if (info)
187  {
188  while (info->name)
189  {
190  snprintf(msg_buf, sizeof(msg_buf), "%s: %s", info->name, info->value);
192  info++;
193  }
194  }
195 #if !defined(_WIN32) && !defined(_WIN64)
196  if (stat("/proc/version", &buf) != -1)
197  {
198  FILE* vfile;
199 
200  if ((vfile = fopen("/proc/version", "r")) != NULL)
201  {
202  int len;
203 
204  strcpy(msg_buf, "/proc/version: ");
205  len = strlen(msg_buf);
206  if (fgets(&msg_buf[len], sizeof(msg_buf) - len, vfile))
208  fclose(vfile);
209  }
210  }
211 #endif
212  Log_output(TRACE_MINIMUM, "=========================================================");
213 exit:
214  return rc;
215 }
216 
217 
219 {
220  trace_callback = callback;
221 }
222 
223 
225 {
226  if (level < TRACE_MINIMUM) /* the lowest we can go is TRACE_MINIMUM*/
227  trace_settings.trace_level = level;
228  trace_output_level = level;
229 }
230 
231 
232 void Log_terminate(void)
233 {
234  free(trace_queue);
235  trace_queue = NULL;
236  trace_queue_size = 0;
237  if (trace_destination)
238  {
239  if (trace_destination != stdout)
240  fclose(trace_destination);
241  trace_destination = NULL;
242  }
245  trace_destination_name = NULL;
246  }
250  }
251  start_index = -1;
252  next_index = 0;
254  sametime_count = 0;
255 }
256 
257 
259 {
260  traceEntry *cur_entry = NULL;
261 
262  /* calling ftime/gettimeofday seems to be comparatively expensive, so we need to limit its use */
263  if (++sametime_count % 20 == 0)
264  {
265 #if defined(GETTIMEOFDAY)
266  gettimeofday(&now_ts, NULL);
267  if (now_ts.tv_sec != last_ts.tv_sec || now_ts.tv_usec != last_ts.tv_usec)
268 #else
269  ftime(&now_ts);
270  if (now_ts.time != last_ts.time || now_ts.millitm != last_ts.millitm)
271 #endif
272  {
273  sametime_count = 0;
274  last_ts = now_ts;
275  }
276  }
277 
278  if (trace_queue_size != trace_settings.max_trace_entries)
279  {
280  traceEntry* new_trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries);
281 
282  if (new_trace_queue == NULL)
283  goto exit;
284  memcpy(new_trace_queue, trace_queue, min(trace_queue_size, trace_settings.max_trace_entries) * sizeof(traceEntry));
285  free(trace_queue);
286  trace_queue = new_trace_queue;
287  trace_queue_size = trace_settings.max_trace_entries;
288 
289  if (start_index > trace_settings.max_trace_entries + 1 ||
290  next_index > trace_settings.max_trace_entries + 1)
291  {
292  start_index = -1;
293  next_index = 0;
294  }
295  }
296 
297  /* add to trace buffer */
298  cur_entry = &trace_queue[next_index];
299  if (next_index == start_index) /* means the buffer is full */
300  {
301  if (++start_index == trace_settings.max_trace_entries)
302  start_index = 0;
303  } else if (start_index == -1)
304  start_index = 0;
305  if (++next_index == trace_settings.max_trace_entries)
306  next_index = 0;
307 exit:
308  return cur_entry;
309 }
310 
311 static char* Log_formatTraceEntry(traceEntry* cur_entry)
312 {
313  struct tm *timeinfo;
314  int buf_pos = 31;
315 
316 #if defined(GETTIMEOFDAY)
317  timeinfo = localtime((time_t *)&cur_entry->ts.tv_sec);
318 #else
319  timeinfo = localtime(&cur_entry->ts.time);
320 #endif
321  strftime(&msg_buf[7], 80, "%Y%m%d %H%M%S ", timeinfo);
322 #if defined(GETTIMEOFDAY)
323  sprintf(&msg_buf[22], ".%.3lu ", cur_entry->ts.tv_usec / 1000L);
324 #else
325  sprintf(&msg_buf[22], ".%.3hu ", cur_entry->ts.millitm);
326 #endif
327  buf_pos = 27;
328 
329  sprintf(msg_buf, "(%.4d)", cur_entry->sametime_count);
330  msg_buf[6] = ' ';
331 
332  if (cur_entry->has_rc == 2)
333  strncpy(&msg_buf[buf_pos], cur_entry->name, sizeof(msg_buf)-buf_pos);
334  else
335  {
336  const char *format = Messages_get(cur_entry->number, cur_entry->level);
337  if (cur_entry->has_rc == 1)
338  snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
339  cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line, cur_entry->rc);
340  else
341  snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
342  cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line);
343  }
344  return msg_buf;
345 }
346 
347 
348 static void Log_output(enum LOG_LEVELS log_level, const char *msg)
349 {
350  if (trace_destination)
351  {
352  fprintf(trace_destination, "%s\n", msg);
353 
354  if (trace_destination != stdout && ++lines_written >= max_lines_per_file)
355  {
356 
357  fclose(trace_destination);
358  _unlink(trace_destination_backup_name); /* remove any old backup trace file */
359  rename(trace_destination_name, trace_destination_backup_name); /* rename recently closed to backup */
360  trace_destination = fopen(trace_destination_name, "w"); /* open new trace file */
361  if (trace_destination == NULL)
362  trace_destination = stdout;
363  lines_written = 0;
364  }
365  else
366  fflush(trace_destination);
367  }
368 
369  if (trace_callback)
370  (*trace_callback)(log_level, msg);
371 }
372 
373 
374 static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry)
375 {
376  if (((trace_output_level == -1) ? log_level >= trace_settings.trace_level : log_level >= trace_output_level))
377  {
378  char* msg = NULL;
379 
381  msg = &Log_formatTraceEntry(cur_entry)[7];
382 
383  Log_output(log_level, msg);
384  }
385 }
386 
387 
388 static void Log_trace(enum LOG_LEVELS log_level, const char *buf)
389 {
390  traceEntry *cur_entry = NULL;
391 
392  if (trace_queue == NULL)
393  return;
394 
395  cur_entry = Log_pretrace();
396 
397  memcpy(&(cur_entry->ts), &now_ts, sizeof(now_ts));
398  cur_entry->sametime_count = sametime_count;
399 
400  cur_entry->has_rc = 2;
401  strncpy(cur_entry->name, buf, sizeof(cur_entry->name));
402  cur_entry->name[MAX_FUNCTION_NAME_LENGTH] = '\0';
403 
404  Log_posttrace(log_level, cur_entry);
405 }
406 
407 
417 void Log(enum LOG_LEVELS log_level, int msgno, const char *format, ...)
418 {
419  if (log_level >= trace_settings.trace_level)
420  {
421  const char *temp = NULL;
422  va_list args;
423 
424  /* we're using a static character buffer, so we need to make sure only one thread uses it at a time */
426  if (format == NULL && (temp = Messages_get(msgno, log_level)) != NULL)
427  format = temp;
428 
429  va_start(args, format);
430  vsnprintf(msg_buf, sizeof(msg_buf), format, args);
431 
432  Log_trace(log_level, msg_buf);
433  va_end(args);
435  }
436 }
437 
438 
448 void Log_stackTrace(enum LOG_LEVELS log_level, int msgno, int thread_id, int current_depth, const char* name, int line, int* rc)
449 {
450  traceEntry *cur_entry = NULL;
451 
452  if (trace_queue == NULL)
453  return;
454 
455  if (log_level < trace_settings.trace_level)
456  return;
457 
459  cur_entry = Log_pretrace();
460 
461  memcpy(&(cur_entry->ts), &now_ts, sizeof(now_ts));
462  cur_entry->sametime_count = sametime_count;
463  cur_entry->number = msgno;
464  cur_entry->thread_id = thread_id;
465  cur_entry->depth = current_depth;
466  strcpy(cur_entry->name, name);
467  cur_entry->level = log_level;
468  cur_entry->line = line;
469  if (rc == NULL)
470  cur_entry->has_rc = 0;
471  else
472  {
473  cur_entry->has_rc = 1;
474  cur_entry->rc = *rc;
475  }
476 
477  Log_posttrace(log_level, cur_entry);
479 }
480 
481 
482 #if 0
483 static FILE* Log_destToFile(const char *dest)
484 {
485  FILE* file = NULL;
486 
487  if (strcmp(dest, "stdout") == 0)
488  file = stdout;
489  else if (strcmp(dest, "stderr") == 0)
490  file = stderr;
491  else
492  {
493  if (strstr(dest, "FFDC"))
494  file = fopen(dest, "ab");
495  else
496  file = fopen(dest, "wb");
497  }
498  return file;
499 }
500 
501 
502 static int Log_compareEntries(const char *entry1, const char *entry2)
503 {
504  int comp = strncmp(&entry1[7], &entry2[7], 19);
505 
506  /* if timestamps are equal, use the sequence numbers */
507  if (comp == 0)
508  comp = strncmp(&entry1[1], &entry2[1], 4);
509 
510  return comp;
511 }
512 
513 
518 int Log_dumpTrace(char* dest)
519 {
520  FILE* file = NULL;
521  ListElement* cur_trace_entry = NULL;
522  const int msgstart = 7;
523  int rc = -1;
524  int trace_queue_index = 0;
525 
526  if ((file = Log_destToFile(dest)) == NULL)
527  {
528  Log(LOG_ERROR, 9, NULL, "trace", dest, "trace entries");
529  goto exit;
530  }
531 
532  fprintf(file, "=========== Start of trace dump ==========\n");
533  /* Interleave the log and trace entries together appropriately */
534  ListNextElement(trace_buffer, &cur_trace_entry);
535  trace_queue_index = start_index;
536  if (trace_queue_index == -1)
537  trace_queue_index = next_index;
538  else
539  {
540  Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
541  if (trace_queue_index == trace_settings.max_trace_entries)
542  trace_queue_index = 0;
543  }
544  while (cur_trace_entry || trace_queue_index != next_index)
545  {
546  if (cur_trace_entry && trace_queue_index != -1)
547  { /* compare these timestamps */
548  if (Log_compareEntries((char*)cur_trace_entry->content, msg_buf) > 0)
549  cur_trace_entry = NULL;
550  }
551 
552  if (cur_trace_entry)
553  {
554  fprintf(file, "%s\n", &((char*)(cur_trace_entry->content))[msgstart]);
555  ListNextElement(trace_buffer, &cur_trace_entry);
556  }
557  else
558  {
559  fprintf(file, "%s\n", &msg_buf[7]);
560  if (trace_queue_index != next_index)
561  {
562  Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
563  if (trace_queue_index == trace_settings.max_trace_entries)
564  trace_queue_index = 0;
565  }
566  }
567  }
568  fprintf(file, "========== End of trace dump ==========\n\n");
569  if (file != stdout && file != stderr && file != NULL)
570  fclose(file);
571  rc = 0;
572 exit:
573  return rc;
574 }
575 #endif
576 
577 
static Log_traceCallback * trace_callback
Definition: Log.c:105
FMT_INLINE std::basic_string< Char > format(const S &format_str, Args &&...args)
Definition: core.h:2081
static int sametime_count
Definition: Log.c:116
static void Log_trace(enum LOG_LEVELS log_level, const char *buf)
Definition: Log.c:388
void Log_setTraceLevel(enum LOG_LEVELS level)
Definition: Log.c:224
LOG_LEVELS
Definition: Log.h:35
int max_trace_entries
Definition: Log.h:58
static char * trace_destination_name
Definition: Log.c:100
static int trace_queue_size
Definition: Log.c:97
int Thread_lock_mutex(mutex_type mutex)
Definition: Thread.c:112
size_t strftime(char *str, size_t count, const char *format, const std::tm *time)
Definition: chrono.h:375
#define malloc(x)
Definition: Heap.h:41
static void Log_output(enum LOG_LEVELS log_level, const char *msg)
Definition: Log.c:348
static traceEntry * trace_queue
Definition: Log.c:96
std::tm localtime(std::time_t time)
Definition: chrono.h:292
char name[MAX_FUNCTION_NAME_LENGTH+1]
Definition: Log.c:87
void Log_setTraceCallback(Log_traceCallback *callback)
Definition: Log.c:218
static char msg_buf[512]
Definition: Log.c:122
#define free(x)
Definition: Heap.h:55
enum LOG_LEVELS level
Definition: Log.c:91
int thread_id
Definition: Log.c:85
int depth
Definition: Log.c:86
#define _unlink
Definition: Log.c:59
static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry *cur_entry)
Definition: Log.c:374
#define SOCKET_ERROR
Definition: Socket.h:76
ListElement * ListNextElement(List *aList, ListElement **pos)
Definition: LinkedList.c:411
static int next_index
Definition: Log.c:95
static mutex_type log_mutex
Definition: Log.c:128
#define min(A, B)
Definition: Log.c:64
struct timeval ts
Definition: Log.c:79
int Thread_unlock_mutex(mutex_type mutex)
Definition: Thread.c:133
void Log(enum LOG_LEVELS log_level, int msgno, const char *format,...)
Definition: Log.c:417
int has_rc
Definition: Log.c:89
#define mutex_type
Definition: mutex_type.h:22
static char * Log_formatTraceEntry(traceEntry *cur_entry)
Definition: Log.c:311
enum LOG_LEVELS trace_level
Definition: Log.h:57
Definition: Log.h:41
int rc
Definition: Log.c:90
void Log_stackTrace(enum LOG_LEVELS log_level, int msgno, int thread_id, int current_depth, const char *name, int line, int *rc)
Definition: Log.c:448
static enum LOG_LEVELS trace_output_level
Definition: Log.c:104
Definition: Log.c:76
const char * name
static int start_index
Definition: Log.c:94
const char * Messages_get(int index, enum LOG_LEVELS log_level)
Definition: Messages.c:94
struct timeval now_ts last_ts
Definition: Log.c:118
const char * value
Definition: Log.h:72
int Log_initialize(Log_nameValue *info)
Definition: Log.c:132
#define MAX_FUNCTION_NAME_LENGTH
Definition: Log.c:74
int sametime_count
Definition: Log.c:83
void Log_traceCallback(enum LOG_LEVELS level, const char *message)
Definition: Log.h:81
static traceEntry * Log_pretrace(void)
Definition: Log.c:258
const char * name
Definition: Log.h:71
enum MQTTReasonCodes rc
Definition: test10.c:1112
static FILE * trace_destination
Definition: Log.c:99
int number
Definition: Log.c:84
static char * trace_destination_backup_name
Definition: Log.c:101
static int lines_written
Definition: Log.c:102
int line
Definition: Log.c:88
static pthread_mutex_t log_mutex_store
Definition: Log.c:127
void Log_terminate(void)
Definition: Log.c:232
static int max_lines_per_file
Definition: Log.c:103
int len
Definition: utf-8.c:46
trace_settings_type trace_settings
Definition: Log.c:67


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Dec 6 2020 03:48:09