Instrument Neutral Distributed Interface INDI 2.2.5
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
31namespace INDI
32{
33char Logger::Tags[Logger::nlevels][MAXINDINAME] = { "ERROR", "WARNING", "INFO", "DEBUG",
34 "DBG_EXTRA_1", "DBG_EXTRA_2", "DBG_EXTRA_3", "DBG_EXTRA_4"
35 };
36
37struct 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
47struct 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
62INDI::DefaultDevice *Logger::parentDevice = nullptr;
63unsigned int Logger::fileVerbosityLevel_ = Logger::defaultlevel;
64unsigned int Logger::screenVerbosityLevel_ = Logger::defaultlevel;
65unsigned int Logger::rememberscreenlevel_ = Logger::defaultlevel;
67std::string Logger::logDir_;
68std::string Logger::logFile_;
69unsigned int Logger::nDevices = 0;
70unsigned int Logger::customLevel = 4;
71
72int Logger::addDebugLevel(const char *debugLevelName, const char *loggingLevelName)
73{
74 // Cannot create any more levels
75 if (customLevel == nlevels)
76 return -1;
77
78 snprintf(Tags[customLevel], MAXINDINAME, "%s", loggingLevelName);
79 snprintf(DebugLevelSInit[customLevel].label, MAXINDINAME, "%s", debugLevelName);
80 snprintf(LoggingLevelSInit[customLevel].label, MAXINDINAME, "%s", debugLevelName);
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
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
145bool 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
256Logger *Logger::m_ = nullptr;
257
258#ifdef LOGGER_MULTITHREAD
259pthread_mutex_t Logger::lock_ = PTHREAD_MUTEX_INITIALIZER;
260inline void Logger::lock()
261{
262 pthread_mutex_lock(&lock_);
263}
264
265inline void Logger::unlock()
266{
267 pthread_mutex_unlock(&lock_);
268}
269#else
270void Logger::lock()
271{
272}
273void Logger::unlock()
274{
275}
276#endif
277
278Logger::Logger() : configured_(false)
279{
280 gettimeofday(&initialTime_, nullptr);
281}
282
283void 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
329Logger::~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
348unsigned 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
372void 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#elif defined(__USE_TIME64_REDIRECTS)
407 snprintf(usec, 7, "%06lld", resTime.tv_usec);
408#else
409 snprintf(usec, 7, "%06ld", resTime.tv_usec);
410#endif
411 Logger::lock();
412
413 if ((configuration_ & file_on) && filelog)
414 {
415 if (nDevices == 1)
416 out_ << Tags[rank(verbosityLevel)] << "\t" << (resTime.tv_sec) << "." << (usec) << " sec"
417 << "\t: " << msg << std::endl;
418 else
419 out_ << Tags[rank(verbosityLevel)] << "\t" << (resTime.tv_sec) << "." << (usec) << " sec"
420 << "\t: [" << devicename << "] " << msg << std::endl;
421 }
422
423 if ((configuration_ & screen_on) && screenlog)
424 IDMessage(devicename, "[%s] %s", Tags[rank(verbosityLevel)], msg);
425
426 Logger::unlock();
427}
428}
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:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_NOFMANY
Definition indiapi.h:173
#define MAXINDINAME
Definition indiapi.h:189
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
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:376
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
Definition indidevapi.c:317
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:453
#define INDI_UNUSED(x)
Definition indidevapi.h:132
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
void IDMessage(const char *dev, const char *fmt,...)
Definition indidriver.c:983
#define DEBUGFDEVICE(device, priority, msg,...)
Definition indilogger.h:61
Namespace to encapsulate INDI client, drivers, and mediator classes.
int mkpath(std::string s, mode_t mode)
The Logger class is a simple logger to log messages to file and INDI clients. This is the implementat...
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.
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)
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,...
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,...)
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
static bool initProperties(INDI::DefaultDevice *device)
static bool updateProperties(bool enable)
static bool saveConfigItems(FILE *fp)
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.
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:365
char name[MAXINDINAME]
Definition indiapi.h:369