Instrument Neutral Distributed Interface INDI  2.0.0
indilogger.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright (C) 2012 Evidence Srl - www.evidence.eu.com
3 
4  Adapted to INDI Library by Jasem Mutlaq & Geehalel.
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License version 2 as published by the Free Software Foundation.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 *******************************************************************************/
20 
21 #include "indilogger.h"
22 #include "indiutility.h"
23 
24 #include <dirent.h>
25 #include <cerrno>
26 #include <iostream>
27 #include <cstdlib>
28 #include <cstring>
29 #include <sys/stat.h>
30 
31 namespace INDI
32 {
33 char Logger::Tags[Logger::nlevels][MAXINDINAME] = { "ERROR", "WARNING", "INFO", "DEBUG",
34  "DBG_EXTRA_1", "DBG_EXTRA_2", "DBG_EXTRA_3", "DBG_EXTRA_4"
35  };
36 
37 struct Logger::switchinit Logger::DebugLevelSInit[] = { { "DBG_ERROR", "Errors", ISS_ON, DBG_ERROR },
38  { "DBG_WARNING", "Warnings", ISS_ON, DBG_WARNING },
39  { "DBG_SESSION", "Messages", ISS_ON, DBG_SESSION },
40  { "DBG_DEBUG", "Driver Debug", ISS_OFF, DBG_DEBUG },
41  { "DBG_EXTRA_1", "Debug Extra 1", ISS_OFF, DBG_EXTRA_1 },
42  { "DBG_EXTRA_2", "Debug Extra 2", ISS_OFF, DBG_EXTRA_2 },
43  { "DBG_EXTRA_3", "Debug Extra 3", ISS_OFF, DBG_EXTRA_3 },
44  { "DBG_EXTRA_4", "Debug Extra 4", ISS_OFF, DBG_EXTRA_4 }
45 };
46 
47 struct Logger::switchinit Logger::LoggingLevelSInit[] =
48 {
49  { "LOG_ERROR", "Errors", ISS_ON, DBG_ERROR }, { "LOG_WARNING", "Warnings", ISS_ON, DBG_WARNING },
50  { "LOG_SESSION", "Messages", ISS_ON, DBG_SESSION }, { "LOG_DEBUG", "Driver Debug", ISS_OFF, DBG_DEBUG },
51  { "LOG_EXTRA_1", "Log Extra 1", ISS_OFF, DBG_EXTRA_1 }, { "LOG_EXTRA_2", "Log Extra 2", ISS_OFF, DBG_EXTRA_2 },
52  { "LOG_EXTRA_3", "Log Extra 3", ISS_OFF, DBG_EXTRA_3 }, { "LOG_EXTRA_4", "Log Extra 4", ISS_OFF, DBG_EXTRA_4 }
53 };
54 
61 
62 INDI::DefaultDevice *Logger::parentDevice = nullptr;
63 unsigned int Logger::fileVerbosityLevel_ = Logger::defaultlevel;
64 unsigned int Logger::screenVerbosityLevel_ = Logger::defaultlevel;
65 unsigned int Logger::rememberscreenlevel_ = Logger::defaultlevel;
66 Logger::loggerConf Logger::configuration_ = Logger::screen_on | Logger::file_off;
67 std::string Logger::logDir_;
68 std::string Logger::logFile_;
69 unsigned int Logger::nDevices = 0;
70 unsigned int Logger::customLevel = 4;
71 
72 int Logger::addDebugLevel(const char *debugLevelName, const char *loggingLevelName)
73 {
74  // Cannot create any more levels
75  if (customLevel == nlevels)
76  return -1;
77 
78  strncpy(Tags[customLevel], loggingLevelName, MAXINDINAME);
79  strncpy(DebugLevelSInit[customLevel].label, debugLevelName, MAXINDINAME);
80  strncpy(LoggingLevelSInit[customLevel].label, debugLevelName, MAXINDINAME);
81 
83 }
84 
86 {
87  nDevices++;
88 
89  for (unsigned int i = 0; i < customLevel; i++)
90  {
92  DebugLevelS[i].aux = (void *)&DebugLevelSInit[i].levelmask;
94  LoggingLevelSInit[i].state);
95  LoggingLevelS[i].aux = (void *)&LoggingLevelSInit[i].levelmask;
96  }
97 
98  IUFillSwitchVector(&DebugLevelSP, DebugLevelS, customLevel, device->getDeviceName(), "DEBUG_LEVEL", "Debug Levels",
100  IUFillSwitchVector(&LoggingLevelSP, LoggingLevelS, customLevel, device->getDeviceName(), "LOGGING_LEVEL",
101  "Logging Levels", OPTIONS_TAB, IP_RW, ISR_NOFMANY, 0, IPS_IDLE);
102 
103  IUFillSwitch(&ConfigurationS[0], "CLIENT_DEBUG", "To Client", ISS_ON);
104  IUFillSwitch(&ConfigurationS[1], "FILE_DEBUG", "To Log File", ISS_OFF);
105  IUFillSwitchVector(&ConfigurationSP, ConfigurationS, 2, device->getDeviceName(), "LOG_OUTPUT", "Log Output",
107 
108  parentDevice = device;
109 
110  return true;
111 }
112 
113 bool Logger::updateProperties(bool enable)
114 {
115  if (enable)
116  {
117  parentDevice->defineProperty(&DebugLevelSP);
118  parentDevice->defineProperty(&LoggingLevelSP);
119 
120  screenVerbosityLevel_ = rememberscreenlevel_;
121 
122  parentDevice->defineProperty(&ConfigurationSP);
123  }
124  else
125  {
126  parentDevice->deleteProperty(DebugLevelSP.name);
127  parentDevice->deleteProperty(LoggingLevelSP.name);
128  parentDevice->deleteProperty(ConfigurationSP.name);
129  rememberscreenlevel_ = screenVerbosityLevel_;
130  screenVerbosityLevel_ = defaultlevel;
131  }
132 
133  return true;
134 }
135 
137 {
141 
142  return true;
143 }
144 
145 bool Logger::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
146 {
147  int debug_level = 0, log_level = 0, bitmask = 0, verbose_level = 0;
148  if (strcmp(name, "DEBUG_LEVEL") == 0)
149  {
150  ISwitch *sw;
151  IUUpdateSwitch(&DebugLevelSP, states, names, n);
153  if (sw == nullptr)
154  {
156  IDSetSwitch(&DebugLevelSP, nullptr);
157  screenVerbosityLevel_ = 0;
158  return true;
159  }
160 
161  for (int i = 0; i < DebugLevelSP.nsp; i++)
162  {
163  sw = &DebugLevelSP.sp[i];
164  bitmask = *((unsigned int *)sw->aux);
165  if (sw->s == ISS_ON)
166  {
167  debug_level = i;
168  verbose_level |= bitmask;
169  }
170  else
171  verbose_level &= ~bitmask;
172  }
173 
174  screenVerbosityLevel_ = verbose_level;
175 
176  DEBUGFDEVICE(dev, Logger::DBG_DEBUG, "Toggle Debug Level -- %s", DebugLevelSInit[debug_level].label);
178  IDSetSwitch(&DebugLevelSP, nullptr);
179  return true;
180  }
181 
182  if (strcmp(name, "LOGGING_LEVEL") == 0)
183  {
184  ISwitch *sw;
185  IUUpdateSwitch(&LoggingLevelSP, states, names, n);
187  if (sw == nullptr)
188  {
189  fileVerbosityLevel_ = 0;
191  IDSetSwitch(&LoggingLevelSP, nullptr);
192  return true;
193  }
194 
195  for (int i = 0; i < LoggingLevelSP.nsp; i++)
196  {
197  sw = &LoggingLevelSP.sp[i];
198  bitmask = *((unsigned int *)sw->aux);
199  if (sw->s == ISS_ON)
200  {
201  log_level = i;
202  fileVerbosityLevel_ |= bitmask;
203  }
204  else
205  fileVerbosityLevel_ &= ~bitmask;
206  }
207 
208  DEBUGFDEVICE(dev, Logger::DBG_DEBUG, "Toggle Logging Level -- %s", LoggingLevelSInit[log_level].label);
210  IDSetSwitch(&LoggingLevelSP, nullptr);
211  return true;
212  }
213 
214  if (!strcmp(name, "LOG_OUTPUT"))
215  {
216  ISwitch *sw;
217  IUUpdateSwitch(&ConfigurationSP, states, names, n);
219 
220  if (sw == nullptr)
221  {
222  configuration_ = screen_off | file_off;
224  IDSetSwitch(&ConfigurationSP, nullptr);
225  return true;
226  }
227 
228  bool wasFileOff = configuration_ & file_off;
229 
230  configuration_ = (loggerConf)0;
231 
232  if (ConfigurationS[1].s == ISS_ON)
233  configuration_ = configuration_ | file_on;
234  else
235  configuration_ = configuration_ | file_off;
236 
237  if (ConfigurationS[0].s == ISS_ON)
238  configuration_ = configuration_ | screen_on;
239  else
240  configuration_ = configuration_ | screen_off;
241 
242  // If file was off, then on again
243  if (wasFileOff && (configuration_ & file_on))
244  Logger::getInstance().configure(logFile_, configuration_, fileVerbosityLevel_, screenVerbosityLevel_);
245 
247  IDSetSwitch(&ConfigurationSP, nullptr);
248 
249  return true;
250  }
251 
252  return false;
253 }
254 
255 // Definition (and initialization) of static attributes
256 Logger *Logger::m_ = nullptr;
257 
258 #ifdef LOGGER_MULTITHREAD
259 pthread_mutex_t Logger::lock_ = PTHREAD_MUTEX_INITIALIZER;
260 inline void Logger::lock()
261 {
262  pthread_mutex_lock(&lock_);
263 }
264 
265 inline void Logger::unlock()
266 {
267  pthread_mutex_unlock(&lock_);
268 }
269 #else
270 void Logger::lock()
271 {
272 }
273 void Logger::unlock()
274 {
275 }
276 #endif
277 
278 Logger::Logger() : configured_(false)
279 {
280  gettimeofday(&initialTime_, nullptr);
281 }
282 
283 void Logger::configure(const std::string &outputFile, const loggerConf configuration, const int fileVerbosityLevel,
284  const int screenVerbosityLevel)
285 {
286  Logger::lock();
287 
288  fileVerbosityLevel_ = fileVerbosityLevel;
289  screenVerbosityLevel_ = screenVerbosityLevel;
290  rememberscreenlevel_ = screenVerbosityLevel_;
291  // Close the old stream, if needed
292  if (configuration_ & file_on)
293  out_.close();
294 
295  // Compute a new file name, if needed
296  if (outputFile != logFile_)
297  {
298  char ts_date[32], ts_time[32];
299  struct tm *tp;
300  time_t t;
301 
302  time(&t);
303  tp = gmtime(&t);
304  strftime(ts_date, sizeof(ts_date), "%Y-%m-%d", tp);
305  strftime(ts_time, sizeof(ts_time), "%H:%M:%S", tp);
306 
307  char dir[MAXRBUF];
308  snprintf(dir, MAXRBUF, "%s/.indi/logs/%s/%s", getenv("HOME"), ts_date, outputFile.c_str());
309  logDir_ = dir;
310 
311  char logFileBuf[MAXRBUF];
312  snprintf(logFileBuf, MAXRBUF, "%s/%s_%s.log", dir, outputFile.c_str(), ts_time);
313  logFile_ = logFileBuf;
314  }
315 
316  // Open a new stream, if needed
317  if (configuration & file_on)
318  {
319  INDI::mkpath(logDir_.c_str(), 0775);
320  out_.open(logFile_.c_str(), std::ios::app);
321  }
322 
323  configuration_ = configuration;
324  configured_ = true;
325 
326  Logger::unlock();
327 }
328 
329 Logger::~Logger()
330 {
331  Logger::lock();
332  if (configuration_ & file_on)
333  out_.close();
334 
335  m_ = nullptr;
336  Logger::unlock();
337 }
338 
340 {
341  Logger::lock();
342  if (m_ == nullptr)
343  m_ = new Logger;
344  Logger::unlock();
345  return *m_;
346 }
347 
348 unsigned int Logger::rank(unsigned int l)
349 {
350  switch (l)
351  {
352  case DBG_ERROR:
353  return 0;
354  case DBG_WARNING:
355  return 1;
356  case DBG_SESSION:
357  return 2;
358  case DBG_EXTRA_1:
359  return 4;
360  case DBG_EXTRA_2:
361  return 5;
362  case DBG_EXTRA_3:
363  return 6;
364  case DBG_EXTRA_4:
365  return 7;
366  case DBG_DEBUG:
367  default:
368  return 3;
369  }
370 }
371 
372 void Logger::print(const char *devicename, const unsigned int verbosityLevel, const std::string &file, const int line,
373  //const std::string& message,
374  const char *message, ...)
375 {
376  // 0 is ignored
377  if (verbosityLevel == 0)
378  return;
379 
380  INDI_UNUSED(file);
381  INDI_UNUSED(line);
382  bool filelog = (verbosityLevel & fileVerbosityLevel_) != 0;
383  bool screenlog = (verbosityLevel & screenVerbosityLevel_) != 0;
384 
385  va_list ap;
386  char msg[257];
387  char usec[7];
388 
389  msg[256] = '\0';
390  va_start(ap, message);
391  vsnprintf(msg, 257, message, ap);
392  va_end(ap);
393 
394  if (!configured_)
395  {
396  //std::cerr << "Warning! Logger not configured!" << std::endl;
397  std::cerr << msg << std::endl;
398  return;
399  }
400  struct timeval currentTime, resTime;
401  usec[6] = '\0';
402  gettimeofday(&currentTime, nullptr);
403  timersub(&currentTime, &initialTime_, &resTime);
404 #if defined(__APPLE__)
405  snprintf(usec, 7, "%06d", resTime.tv_usec);
406 #else
407  snprintf(usec, 7, "%06ld", resTime.tv_usec);
408 #endif
409  Logger::lock();
410 
411  if ((configuration_ & file_on) && filelog)
412  {
413  if (nDevices == 1)
414  out_ << Tags[rank(verbosityLevel)] << "\t" << (resTime.tv_sec) << "." << (usec) << " sec"
415  << "\t: " << msg << std::endl;
416  else
417  out_ << Tags[rank(verbosityLevel)] << "\t" << (resTime.tv_sec) << "." << (usec) << " sec"
418  << "\t: [" << devicename << "] " << msg << std::endl;
419  }
420 
421  if ((configuration_ & screen_on) && screenlog)
422  IDMessage(devicename, "[%s] %s", Tags[rank(verbosityLevel)], msg);
423 
424  Logger::unlock();
425 }
426 }
hid_device * device
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_NOFMANY
Definition: indiapi.h:175
#define MAXINDINAME
Definition: indiapi.h:191
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
Definition: indidevapi.c:108
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
void IDMessage(const char *dev, const char *fmt,...)
Definition: indidriver.c:960
#define DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
#define MAXRBUF
Definition: indiserver.cpp:102
Namespace to encapsulate INDI client, drivers, and mediator classes.
int mkpath(std::string s, mode_t mode)
Definition: indiutility.cpp:51
unsigned int levelmask
Definition: indilogger.h:207
The Logger class is a simple logger to log messages to file and INDI clients. This is the implementat...
Definition: indilogger.cpp:37
static ISwitch DebugLevelS[nlevels]
Definition: indilogger.h:271
static ISwitch LoggingLevelS[nlevels]
Definition: indilogger.h:213
static unsigned int customLevel
Definition: indilogger.h:222
static unsigned int rank(unsigned int l)
Method used to print message called by the DEBUG() macro.
Definition: indilogger.cpp:348
static const loggerConf screen_on
Definition: indilogger.h:220
static struct switchinit DebugLevelSInit[nlevels]
Definition: indilogger.h:270
static const loggerConf file_off
Definition: indilogger.h:219
static ISwitchVectorProperty DebugLevelSP
Definition: indilogger.h:272
static const unsigned int nlevels
Definition: indilogger.h:211
static bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Definition: indilogger.cpp:145
void configure(const std::string &outputFile, const loggerConf configuration, const int fileVerbosityLevel, const int screenVerbosityLevel)
Method to configure the logger. Called by the DEBUG_CONF() macro. To make implementation easier,...
Definition: indilogger.cpp:283
static ISwitchVectorProperty LoggingLevelSP
Definition: indilogger.h:214
void print(const char *devicename, const unsigned int verbosityLevel, const std::string &sourceFile, const int codeLine, const char *message,...)
Definition: indilogger.cpp:372
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
Definition: indilogger.cpp:339
static bool initProperties(INDI::DefaultDevice *device)
Definition: indilogger.cpp:85
static bool updateProperties(bool enable)
Definition: indilogger.cpp:113
static bool saveConfigItems(FILE *fp)
Definition: indilogger.cpp:136
loggerConf_ loggerConf
Definition: indilogger.h:217
static struct switchinit LoggingLevelSInit[nlevels]
Definition: indilogger.h:212
static const loggerConf file_on
Definition: indilogger.h:218
static ISwitch ConfigurationS[2]
Definition: indilogger.h:215
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.
Definition: indilogger.cpp:72
static char Tags[nlevels][MAXINDINAME]
Definition: indilogger.h:276
static const unsigned int defaultlevel
Definition: indilogger.h:210
static const loggerConf screen_off
Definition: indilogger.h:221
static unsigned int nDevices
Definition: indilogger.h:223
static ISwitchVectorProperty ConfigurationSP
Definition: indilogger.h:216
One switch descriptor.
Switch vector property descriptor.
Definition: indiapi.h:367
char name[MAXINDINAME]
Definition: indiapi.h:371