Instrument Neutral Distributed Interface INDI  1.9.2
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 struct Logger::switchinit Logger::DebugLevelSInit[] = { { "DBG_ERROR", "Errors", ISS_ON, DBG_ERROR },
37  { "DBG_WARNING", "Warnings", ISS_ON, DBG_WARNING },
38  { "DBG_SESSION", "Messages", ISS_ON, DBG_SESSION },
39  { "DBG_DEBUG", "Driver Debug", ISS_OFF, DBG_DEBUG },
40  { "DBG_EXTRA_1", "Debug Extra 1", ISS_OFF, DBG_EXTRA_1 },
41  { "DBG_EXTRA_2", "Debug Extra 2", ISS_OFF, DBG_EXTRA_2 },
42  { "DBG_EXTRA_3", "Debug Extra 3", ISS_OFF, DBG_EXTRA_3 },
43  { "DBG_EXTRA_4", "Debug Extra 4", ISS_OFF, DBG_EXTRA_4 } };
44 
45 struct Logger::switchinit Logger::LoggingLevelSInit[] = {
46  { "LOG_ERROR", "Errors", ISS_ON, DBG_ERROR }, { "LOG_WARNING", "Warnings", ISS_ON, DBG_WARNING },
47  { "LOG_SESSION", "Messages", ISS_ON, DBG_SESSION }, { "LOG_DEBUG", "Driver Debug", ISS_OFF, DBG_DEBUG },
48  { "LOG_EXTRA_1", "Log Extra 1", ISS_OFF, DBG_EXTRA_1 }, { "LOG_EXTRA_2", "Log Extra 2", ISS_OFF, DBG_EXTRA_2 },
49  { "LOG_EXTRA_3", "Log Extra 3", ISS_OFF, DBG_EXTRA_3 }, { "LOG_EXTRA_4", "Log Extra 4", ISS_OFF, DBG_EXTRA_4 }
50 };
51 
58 
59 INDI::DefaultDevice *Logger::parentDevice = nullptr;
60 unsigned int Logger::fileVerbosityLevel_ = Logger::defaultlevel;
61 unsigned int Logger::screenVerbosityLevel_ = Logger::defaultlevel;
62 unsigned int Logger::rememberscreenlevel_ = Logger::defaultlevel;
63 Logger::loggerConf Logger::configuration_ = Logger::screen_on | Logger::file_off;
64 std::string Logger::logDir_;
65 std::string Logger::logFile_;
66 unsigned int Logger::nDevices = 0;
67 unsigned int Logger::customLevel = 4;
68 
69 int Logger::addDebugLevel(const char *debugLevelName, const char *loggingLevelName)
70 {
71  // Cannot create any more levels
72  if (customLevel == nlevels)
73  return -1;
74 
75  strncpy(Tags[customLevel], loggingLevelName, MAXINDINAME);
76  strncpy(DebugLevelSInit[customLevel].label, debugLevelName, MAXINDINAME);
77  strncpy(LoggingLevelSInit[customLevel].label, debugLevelName, MAXINDINAME);
78 
80 }
81 
83 {
84  nDevices++;
85 
86  for (unsigned int i = 0; i < customLevel; i++)
87  {
89  DebugLevelS[i].aux = (void *)&DebugLevelSInit[i].levelmask;
91  LoggingLevelSInit[i].state);
92  LoggingLevelS[i].aux = (void *)&LoggingLevelSInit[i].levelmask;
93  }
94 
95  IUFillSwitchVector(&DebugLevelSP, DebugLevelS, customLevel, device->getDeviceName(), "DEBUG_LEVEL", "Debug Levels",
97  IUFillSwitchVector(&LoggingLevelSP, LoggingLevelS, customLevel, device->getDeviceName(), "LOGGING_LEVEL",
98  "Logging Levels", OPTIONS_TAB, IP_RW, ISR_NOFMANY, 0, IPS_IDLE);
99 
100  IUFillSwitch(&ConfigurationS[0], "CLIENT_DEBUG", "To Client", ISS_ON);
101  IUFillSwitch(&ConfigurationS[1], "FILE_DEBUG", "To Log File", ISS_OFF);
102  IUFillSwitchVector(&ConfigurationSP, ConfigurationS, 2, device->getDeviceName(), "LOG_OUTPUT", "Log Output",
104 
105  parentDevice = device;
106 
107  return true;
108 }
109 
110 bool Logger::updateProperties(bool enable)
111 {
112  if (enable)
113  {
114  parentDevice->defineProperty(&DebugLevelSP);
115  parentDevice->defineProperty(&LoggingLevelSP);
116 
117  screenVerbosityLevel_ = rememberscreenlevel_;
118 
119  parentDevice->defineProperty(&ConfigurationSP);
120  }
121  else
122  {
123  parentDevice->deleteProperty(DebugLevelSP.name);
124  parentDevice->deleteProperty(LoggingLevelSP.name);
125  parentDevice->deleteProperty(ConfigurationSP.name);
126  rememberscreenlevel_ = screenVerbosityLevel_;
127  screenVerbosityLevel_ = defaultlevel;
128  }
129 
130  return true;
131 }
132 
133 bool Logger::saveConfigItems(FILE *fp)
134 {
138 
139  return true;
140 }
141 
142 bool Logger::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
143 {
144  int debug_level = 0, log_level = 0, bitmask = 0, verbose_level = 0;
145  if (strcmp(name, "DEBUG_LEVEL") == 0)
146  {
147  ISwitch *sw;
148  IUUpdateSwitch(&DebugLevelSP, states, names, n);
150  if (sw == nullptr)
151  {
153  IDSetSwitch(&DebugLevelSP, nullptr);
154  screenVerbosityLevel_ = 0;
155  return true;
156  }
157 
158  for (int i = 0; i < DebugLevelSP.nsp; i++)
159  {
160  sw = &DebugLevelSP.sp[i];
161  bitmask = *((unsigned int *)sw->aux);
162  if (sw->s == ISS_ON)
163  {
164  debug_level = i;
165  verbose_level |= bitmask;
166  }
167  else
168  verbose_level &= ~bitmask;
169  }
170 
171  screenVerbosityLevel_ = verbose_level;
172 
173  DEBUGFDEVICE(dev, Logger::DBG_DEBUG, "Toggle Debug Level -- %s", DebugLevelSInit[debug_level].label);
175  IDSetSwitch(&DebugLevelSP, nullptr);
176  return true;
177  }
178 
179  if (strcmp(name, "LOGGING_LEVEL") == 0)
180  {
181  ISwitch *sw;
182  IUUpdateSwitch(&LoggingLevelSP, states, names, n);
184  if (sw == nullptr)
185  {
186  fileVerbosityLevel_ = 0;
188  IDSetSwitch(&LoggingLevelSP, nullptr);
189  return true;
190  }
191 
192  for (int i = 0; i < LoggingLevelSP.nsp; i++)
193  {
194  sw = &LoggingLevelSP.sp[i];
195  bitmask = *((unsigned int *)sw->aux);
196  if (sw->s == ISS_ON)
197  {
198  log_level = i;
199  fileVerbosityLevel_ |= bitmask;
200  }
201  else
202  fileVerbosityLevel_ &= ~bitmask;
203  }
204 
205  DEBUGFDEVICE(dev, Logger::DBG_DEBUG, "Toggle Logging Level -- %s", LoggingLevelSInit[log_level].label);
207  IDSetSwitch(&LoggingLevelSP, nullptr);
208  return true;
209  }
210 
211  if (!strcmp(name, "LOG_OUTPUT"))
212  {
213  ISwitch *sw;
214  IUUpdateSwitch(&ConfigurationSP, states, names, n);
216 
217  if (sw == nullptr)
218  {
219  configuration_ = screen_off | file_off;
221  IDSetSwitch(&ConfigurationSP, nullptr);
222  return true;
223  }
224 
225  bool wasFileOff = configuration_ & file_off;
226 
227  configuration_ = (loggerConf)0;
228 
229  if (ConfigurationS[1].s == ISS_ON)
230  configuration_ = configuration_ | file_on;
231  else
232  configuration_ = configuration_ | file_off;
233 
234  if (ConfigurationS[0].s == ISS_ON)
235  configuration_ = configuration_ | screen_on;
236  else
237  configuration_ = configuration_ | screen_off;
238 
239  // If file was off, then on again
240  if (wasFileOff && (configuration_ & file_on))
241  Logger::getInstance().configure(logFile_, configuration_, fileVerbosityLevel_, screenVerbosityLevel_);
242 
244  IDSetSwitch(&ConfigurationSP, nullptr);
245 
246  return true;
247  }
248 
249  return false;
250 }
251 
252 // Definition (and initialization) of static attributes
253 Logger *Logger::m_ = nullptr;
254 
255 #ifdef LOGGER_MULTITHREAD
256 pthread_mutex_t Logger::lock_ = PTHREAD_MUTEX_INITIALIZER;
257 inline void Logger::lock()
258 {
259  pthread_mutex_lock(&lock_);
260 }
261 
262 inline void Logger::unlock()
263 {
264  pthread_mutex_unlock(&lock_);
265 }
266 #else
267 void Logger::lock()
268 {
269 }
270 void Logger::unlock()
271 {
272 }
273 #endif
274 
275 Logger::Logger() : configured_(false)
276 {
277  gettimeofday(&initialTime_, nullptr);
278 }
279 
280 void Logger::configure(const std::string &outputFile, const loggerConf configuration, const int fileVerbosityLevel,
281  const int screenVerbosityLevel)
282 {
283  Logger::lock();
284 
285  fileVerbosityLevel_ = fileVerbosityLevel;
286  screenVerbosityLevel_ = screenVerbosityLevel;
287  rememberscreenlevel_ = screenVerbosityLevel_;
288  // Close the old stream, if needed
289  if (configuration_ & file_on)
290  out_.close();
291 
292  // Compute a new file name, if needed
293  if (outputFile != logFile_)
294  {
295  char ts_date[32], ts_time[32];
296  struct tm *tp;
297  time_t t;
298 
299  time(&t);
300  tp = gmtime(&t);
301  strftime(ts_date, sizeof(ts_date), "%Y-%m-%d", tp);
302  strftime(ts_time, sizeof(ts_time), "%H:%M:%S", tp);
303 
304  char dir[MAXRBUF];
305  snprintf(dir, MAXRBUF, "%s/.indi/logs/%s/%s", getenv("HOME"), ts_date, outputFile.c_str());
306  logDir_ = dir;
307 
308  char logFileBuf[MAXRBUF];
309  snprintf(logFileBuf, MAXRBUF, "%s/%s_%s.log", dir, outputFile.c_str(), ts_time);
310  logFile_ = logFileBuf;
311  }
312 
313  // Open a new stream, if needed
314  if (configuration & file_on)
315  {
316  INDI::mkpath(logDir_.c_str(), 0775);
317  out_.open(logFile_.c_str(), std::ios::app);
318  }
319 
320  configuration_ = configuration;
321  configured_ = true;
322 
323  Logger::unlock();
324 }
325 
326 Logger::~Logger()
327 {
328  Logger::lock();
329  if (configuration_ & file_on)
330  out_.close();
331 
332  m_ = nullptr;
333  Logger::unlock();
334 }
335 
336 Logger &Logger::getInstance()
337 {
338  Logger::lock();
339  if (m_ == nullptr)
340  m_ = new Logger;
341  Logger::unlock();
342  return *m_;
343 }
344 
345 unsigned int Logger::rank(unsigned int l)
346 {
347  switch (l)
348  {
349  case DBG_ERROR:
350  return 0;
351  case DBG_WARNING:
352  return 1;
353  case DBG_SESSION:
354  return 2;
355  case DBG_EXTRA_1:
356  return 4;
357  case DBG_EXTRA_2:
358  return 5;
359  case DBG_EXTRA_3:
360  return 6;
361  case DBG_EXTRA_4:
362  return 7;
363  case DBG_DEBUG:
364  default:
365  return 3;
366  }
367 }
368 
369 void Logger::print(const char *devicename, const unsigned int verbosityLevel, const std::string &file, const int line,
370  //const std::string& message,
371  const char *message, ...)
372 {
373  // 0 is ignored
374  if (verbosityLevel == 0)
375  return;
376 
377  INDI_UNUSED(file);
378  INDI_UNUSED(line);
379  bool filelog = (verbosityLevel & fileVerbosityLevel_) != 0;
380  bool screenlog = (verbosityLevel & screenVerbosityLevel_) != 0;
381 
382  va_list ap;
383  char msg[257];
384  char usec[7];
385 
386  msg[256] = '\0';
387  va_start(ap, message);
388  vsnprintf(msg, 257, message, ap);
389  va_end(ap);
390 
391  if (!configured_)
392  {
393  //std::cerr << "Warning! Logger not configured!" << std::endl;
394  std::cerr << msg << std::endl;
395  return;
396  }
397  struct timeval currentTime, resTime;
398  usec[6] = '\0';
399  gettimeofday(&currentTime, nullptr);
400  timersub(&currentTime, &initialTime_, &resTime);
401 #if defined(__APPLE__)
402  snprintf(usec, 7, "%06d", resTime.tv_usec);
403 #else
404  snprintf(usec, 7, "%06ld", resTime.tv_usec);
405 #endif
406  Logger::lock();
407 
408  if ((configuration_ & file_on) && filelog)
409  {
410  if (nDevices == 1)
411  out_ << Tags[rank(verbosityLevel)] << "\t" << (resTime.tv_sec) << "." << (usec) << " sec"
412  << "\t: " << msg << std::endl;
413  else
414  out_ << Tags[rank(verbosityLevel)] << "\t" << (resTime.tv_sec) << "." << (usec) << " sec"
415  << "\t: [" << devicename << "] " << msg << std::endl;
416  }
417 
418  if ((configuration_ & screen_on) && screenlog)
419  IDMessage(devicename, "[%s] %s", Tags[rank(verbosityLevel)], msg);
420 
421  Logger::unlock();
422 }
423 }
indiutility.h
INDI::Logger::print
void print(const char *devicename, const unsigned int verbosityLevel, const std::string &sourceFile, const int codeLine, const char *message,...)
Definition: indilogger.cpp:387
INDI::Logger::getInstance
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
Definition: indilogger.cpp:354
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
INDI::Logger::DBG_EXTRA_2
@ DBG_EXTRA_2
Definition: indilogger.h:197
ISwitch
One switch descriptor.
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
INDI::Logger::Tags
static char Tags[nlevels][MAXINDINAME]
Definition: indilogger.h:270
INDI::Logger::DBG_WARNING
@ DBG_WARNING
Definition: indilogger.h:193
MAXINDINAME
#define MAXINDINAME
Definition: indiapi.h:190
ISR_NOFMANY
@ ISR_NOFMANY
Definition: indiapi.h:174
INDI::DefaultDevice::defineProperty
void defineProperty(INumberVectorProperty *property)
Definition: defaultdevice.cpp:997
OPTIONS_TAB
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
Definition: defaultdevice.cpp:39
INDI::Logger::DBG_EXTRA_1
@ DBG_EXTRA_1
Definition: indilogger.h:196
INDI::Logger::DBG_ERROR
@ DBG_ERROR
Definition: indilogger.h:192
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
INDI::Logger::file_on
static const loggerConf file_on
Definition: indilogger.h:218
INDI::Logger::DBG_SESSION
@ DBG_SESSION
Definition: indilogger.h:194
INDI::Logger::LoggingLevelS
static ISwitch LoggingLevelS[nlevels]
Definition: indilogger.h:213
INDI::Logger::updateProperties
static bool updateProperties(bool enable)
Definition: indilogger.cpp:128
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
INDI::Logger::DBG_DEBUG
@ DBG_DEBUG
Definition: indilogger.h:195
IUFindOnSwitch
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *sp)
Returns the first ON switch it finds in the vector switch property.
Definition: indicom.c:1393
indilogger.h
INDI::Logger::LoggingLevelSInit
static struct switchinit LoggingLevelSInit[nlevels]
Definition: indilogger.h:212
INDI::mkpath
int mkpath(std::string s, mode_t mode)
Create a path directory - this function uses 'mkdir'.
Definition: indiutility.cpp:29
INDI::Logger::file_off
static const loggerConf file_off
Definition: indilogger.h:219
device
hid_device * device
Definition: activefocuser_utils.cpp:92
INDI::Logger::initProperties
static bool initProperties(INDI::DefaultDevice *device)
Definition: indilogger.cpp:100
INDI::Logger::switchinit::levelmask
unsigned int levelmask
Definition: indilogger.h:207
_ISwitchVectorProperty::nsp
int nsp
Definition: indiapi.h:386
INDI::Logger::DebugLevelS
static ISwitch DebugLevelS[nlevels]
Definition: indilogger.h:265
INDI::Logger::saveConfigItems
static bool saveConfigItems(FILE *fp)
Definition: indilogger.cpp:151
IUFillSwitchVector
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: indidriver.c:412
INDI::Logger::configure
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:298
INDI::Logger::DBG_EXTRA_3
@ DBG_EXTRA_3
Definition: indilogger.h:198
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
INDI::Logger::ConfigurationSP
static ISwitchVectorProperty ConfigurationSP
Definition: indilogger.h:216
IDMessage
void IDMessage(const char *dev, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Function Drivers call to send log messages to Clients.
INDI::Logger::DBG_EXTRA_4
@ DBG_EXTRA_4
Definition: indilogger.h:199
INDI::Logger::ConfigurationS
static ISwitch ConfigurationS[2]
Definition: indilogger.h:215
IUUpdateSwitch
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:171
INDI::Logger::nDevices
static unsigned int nDevices
Definition: indilogger.h:223
INDI::Logger::defaultlevel
static const unsigned int defaultlevel
Definition: indilogger.h:210
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
DEBUGFDEVICE
#define DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
INDI::Logger::DebugLevelSInit
static struct switchinit DebugLevelSInit[nlevels]
Definition: indilogger.h:264
INDI
Namespace to encapsulate INDI client, drivers, and mediator classes.
Definition: AlignmentSubsystemForClients.cpp:11
INDI::Logger::addDebugLevel
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.
Definition: indilogger.cpp:87
INDI::Logger::screen_off
static const loggerConf screen_off
Definition: indilogger.h:221
INDI::Logger::loggerConf
loggerConf_ loggerConf
Definition: indilogger.h:217
IP_RW
@ IP_RW
Definition: indiapi.h:185
ISState
ISState
Switch state.
Definition: indiapi.h:148
_ISwitchVectorProperty::sp
ISwitch * sp
Definition: indiapi.h:384
IUSaveConfigSwitch
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indicom.c:1444
INDI::DefaultDevice
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
Definition: defaultdevice.h:118
INDI::Logger::rank
static unsigned int rank(unsigned int l)
Method used to print message called by the DEBUG() macro.
Definition: indilogger.cpp:363
INDI::Logger::LoggingLevelSP
static ISwitchVectorProperty LoggingLevelSP
Definition: indilogger.h:214
INDI::DefaultDevice::deleteProperty
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
Definition: defaultdevice.cpp:965
INDI::Logger::ISNewSwitch
static bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Definition: indilogger.cpp:160
INDI::Logger::screen_on
static const loggerConf screen_on
Definition: indilogger.h:220
IDSetSwitch
void void void void void IDSetSwitch(const ISwitchVectorProperty *s, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing switch vector property.
IUFillSwitch
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: indidriver.c:320
INDI::Logger::DebugLevelSP
static ISwitchVectorProperty DebugLevelSP
Definition: indilogger.h:266
INDI::Logger::customLevel
static unsigned int customLevel
Definition: indilogger.h:222
_ISwitchVectorProperty
Switch vector property descriptor.
Definition: indiapi.h:365
INDI::Logger::nlevels
static const unsigned int nlevels
Definition: indilogger.h:211
_ISwitchVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:370
ISS_ON
@ ISS_ON
Definition: indiapi.h:151