Remoto - VFS: VFS_logger.cpp Source File
Remoto - VFS
VFS_logger.cpp
Go to the documentation of this file.
1 
2 #include <iostream>
3 
4 #include <QtMath>
5 #include <QDateTime>
6 #include <QCoreApplication>
7 
8 #include "defines.h"
9 #include "VFS.h"
10 #include "VFS_logger.h"
12 
33 VFS_logger::VFS_logger(int logLevel, int logTime, QString adminEmail)
34 : VFS_node()
35 , _logLevel(logLevel)
36 , _logTime(logTime)
37 , _adminEmail(adminEmail)
38 , cp_bold(UNDEF,BOLD)
39 , cp_warn(YELLOW, BOLD)
40 , cp_error(RED, BOLD)
41 {
42  _log = append( "log", new VFS_stream(655360,true) );
43  _critical = append( "critical", new VFS_stream(40960,true) );
44  _errors = append( "errors", new VFS_stream(40960,true) );
45  _warnings = append( "warnings", new VFS_stream(40960,true) );
46  _messages = append( "messages", new VFS_stream(655360,true) );
47 
48  //subscribe _log to the various nodes as a master log
49  issueRequest( this, _log->createRequest(VFS_request::subscribe,"messages","logger") );
50  issueRequest( this, _log->createRequest(VFS_request::subscribe,"warnings","logger") );
51  issueRequest( this, _log->createRequest(VFS_request::subscribe,"errors", "logger") );
52  issueRequest( this, _log->createRequest(VFS_request::subscribe,"critical","logger") );
53 
54  //subscribe this to the various nodes so they can get to stdout and stderr
55  issueRequest( this, createRequest(VFS_request::subscribe,"messages","logger") );
56  issueRequest( this, createRequest(VFS_request::subscribe,"warnings","logger") );
57  issueRequest( this, createRequest(VFS_request::subscribe,"errors", "logger") );
58  issueRequest( this, createRequest(VFS_request::subscribe,"critical","logger") );
59 }
60 
62 {
63  //will delete children in the VFS_node destructor
64 }
65 
73 {
74  if (r->_path == "settings")
75  return this;
76 
77  return VFS_node::find(r);
78 }
79 
91 {
92  if (r->_path == "settings")
93  {
94  //printf("Settings: %s\n",qUtf8Printable(r->_data.toJson()));
95 
96  QDateTime now = QDateTime::currentDateTime();
97 
98  QJsonObject o = r->_data.object();
99 
100  if (o.contains("adminemail"))
101  { QString e = o["adminemail"].toString();
102  _adminEmail = e;
103 
104  QJsonObject m;
105  m["data"] = QString("%1,LOG,%2,%3,Set admin email to '%4'\n").arg(now.toMSecsSinceEpoch()).arg(r->_user).arg(_logLevel).arg(e);
106  VFS_request *s = createRequest(VFS_request::diff,"",r->_user,QJsonDocument(m));
107  _messages->applyDiff(s);
108  delete s;
109  //VFS::LOG( QString("Set admin email to '%1'").arg(e),0,"logger");
110  }
111 
112  if (o.contains("logtime"))
113  { int l = qBound(0,o["logtime"].toInt(),4);
114  _logTime = l;
115 
116  QJsonObject m;
117  m["data"] = QString("%1,LOG,%2,%3,Set log time format to %4\n").arg(now.toMSecsSinceEpoch()).arg(r->_user).arg(_logLevel).arg(l);
118  VFS_request *s = createRequest(VFS_request::diff,"",r->_user,QJsonDocument(m));
119  _messages->applyDiff(s);
120  delete s;
121  //VFS::LOG( QString("Set log time format to %1").arg(l),0,"logger");
122  }
123 
124  if (o.contains("loglevel"))
125  { int l = qBound(0,o["loglevel"].toInt(),9);
126  _logLevel = l;
127 
128  QJsonObject m;
129  m["data"] = QString("%1,LOG,%2,%3,Set log level to %4\n").arg(now.toMSecsSinceEpoch()).arg(r->_user).arg(_logLevel).arg(l);
130  VFS_request *s = createRequest(VFS_request::diff,"",r->_user,QJsonDocument(m));
131  _messages->applyDiff(s);
132  delete s;
133  //VFS::LOG( QString("Set log level to %1").arg(l),0,"logger");
134  }
135 
136  r->_success = true;
137  return;
138  }
139 
140  r->_reason = "Logger Invalid path: "+r->_path;
141  r->_success = false;
142 }
143 
191 //routes data written from subscribed files as diffs out to stdout, stderr, etc.
193 {
194  //QMutexLocker l(&_lock);
195 
196  VFS_node::applyDiff(t); //distribute the diff to subscribers regardless of log level.
197 
198  VFS_node *origin = t->_origin;
199 
200  QJsonDocument diffdoc = t->_data;
201  QString diff = diffdoc.object()["data"].toString();
202 
203  QString time = diff.section(",",0,0);
204  QByteArray which = diff.section(",",1,1).toUtf8();
205  //QByteArray user = diff.section(",",2,2).toUtf8();
206  QByteArray level = diff.section(",",3,3).toUtf8();
207  QByteArray message = diff.section(",",4).toUtf8();
208 
209  int ll = qBound(0,level.toInt(),9);
210  if (ll > _logLevel)
211  { t->_success = true;
212  return;
213  }
214 
215  std::string timeString = "";
216  std::string timeOffset = "";
217  //QDateTime now = QDateTime::currentDateTime();
218  QDateTime now = QDateTime::fromMSecsSinceEpoch( time.toLongLong() );
219  switch (_logTime)
220  {
221  case 0: timeString = ""; break;
222  case 1: timeString = now.toString("hh:mm:ss.zzz ").toStdString(); break;
223  case 2: timeString = now.toString("yyyy/MM/dd hh:mm:ss:zzz t ").toStdString(); break;
224  case 3: timeString = now.toString("yyyy/MM/dd hh:mm:ss:zzz ").toStdString();
225  {
226  int offh = qFloor(now.offsetFromUtc()/(60.0*60.0)); //seconds resolved to hours
227  int offm = qFloor(now.offsetFromUtc()/(60.0)) % 60; //seconds resolved to minutes, mod-ed by an hour
228  timeOffset = QString("%1%2:%3 GMT ").arg(offh >= 0 ? "+" : "").arg(offh).arg(offm,2,10,QChar('0')).toStdString();
229  }
230  break;
231  case 4:
232  default: //timeString = QString("%1").arg(now.toSecsSinceEpoch()).toStdString();
233  timeString = time.toStdString()+" ";
234  break;
235  }
236 
237  if (origin == _messages)
238  std::cout << cp_bold.cp_s_init() << timeString << timeOffset << "[ LOG " << level.data() << " ] " << qUtf8Printable(t->_user) << " " << cp_bold.cp_s_rst() << message.data();
239  else if (origin == _warnings)
240  std::cout << cp_warn.cp_s_init() << timeString << timeOffset << "[ WARN " << level.data() << " ] " << qUtf8Printable(t->_user) << " " << message.data() << cp_warn.cp_s_rst();
241  else if (origin == _errors)
242  std::cerr << cp_error.cp_s_init() << timeString << timeOffset << "[ ERROR ] " << qUtf8Printable(t->_user) << " " << message.data() << cp_error.cp_s_rst();
243  else if (origin == _critical)
244  {
245  std::cerr << cp_error.cp_s_init() << timeString << timeOffset << "[ CRITICAL ] " << diff.toUtf8().data() << cp_error.cp_s_rst();
246  email(_adminEmail,APPLICATION_NAME" CRITICAL",diff.toUtf8().data());
247  std::cerr << cp_error.cp_s_init() << timeString << timeOffset << " notification email sent to: " << qUtf8Printable(_adminEmail) << std::endl << cp_error.cp_s_rst();
248  }
249  else
250  // std::cerr << cp_error.cp_s_init() << "[ (" << which.data() << ") ] " << qUtf8Printable(t->_user) << cp_error.cp_s_rst() << " " << diff.toUtf8().data() << (diff.right(1)!="\n"? "\n" : "");
251  std::cerr << cp_error.cp_s_init() << timeString << timeOffset << "[ UNKNOWN (" << which.data() << ") ] " << qUtf8Printable(t->_user) << cp_error.cp_s_rst() << " " << diffdoc.toJson().data() << (diff.right(1)!="\n"? "\n" : "");
252 
253  std::cout << std::flush; //to make the output grep-able, we need to flush the streams.
254  std::cerr << std::flush;
255 }
256 
266 void VFS_logger::email(QString address, QString subject, QString message)
267 {
268  //system("mail -s "This is the subject" somebody@example.com <<< 'This is the message'");
269  //system("echo "This is the body" | mail -s "Subject" -aFrom:Harry<harry@gmail.com> someone@example.com");
270 
271  //FIXME: potential security issue here with raw system commands
272  //http://stackoverflow.com/questions/288038/how-to-safely-escape-a-string-from-c
273  address = address.replace("'","\"'\"").trimmed();
274  subject = subject.replace("'","\"'\"").trimmed();
275  message = message.replace("'","\"'\"").trimmed();
276 
277  if (address == "")
278  { VFS::ERROR("Cannot send CRITICAL email when address is empty. Provide a space-separated list of adminemail addresses in the root VFS env config.\n");
279  return;
280  }
281 
282  //QString command = QString("echo '%3' | mail -s '%1' '%2'").arg(subject).arg(address).arg(message);
283  QString command = QString("mail -s '%1' '%2' <<< '%3'").arg(subject).arg(address).arg(message);
284  printf("%s\n",qUtf8Printable(command));
285 
286  //int s = QProcess::execute( command );
287  int s = system(qUtf8Printable(command));
288  if (s != 0)
289  VFS::ERROR( QString("Could not send email with command:\n%1").arg(command));
290 }
int _logLevel
Filter log entries by this value... lower entries are very important, higher ones are more fine-grain...
Definition: VFS_logger.h:28
int _logTime
Include log entry time, where 0 = none, 1 = locale time, 2 = locale time and date,...
Definition: VFS_logger.h:29
virtual void submit(VFS_request *r)
Submit settings to this logger.
Definition: VFS_logger.cpp:90
virtual VFS_node * find(VFS_request *r)
This method will return this if the path is settings, otherwise VFS_node::find().
Definition: VFS_logger.cpp:72
VFS_node * _critical
Crash may be imminent, the administrator address will be emailed this message if it is possible.
Definition: VFS_logger.h:23
ColorPrint cp_warn
Color settings for warning messages.
Definition: VFS_logger.h:33
VFS_node * _warnings
Warning logs, which provide non-critical information.
Definition: VFS_logger.h:21
ColorPrint cp_error
Color settings for error messages.
Definition: VFS_logger.h:34
VFS_node * _log
Message collector node, which subscribes to the other nodes as an aggregator.
Definition: VFS_logger.h:19
ColorPrint cp_bold
Color settings for bold printing.
Definition: VFS_logger.h:32
virtual void applyDiff(VFS_request *t)
Receive log information from a logging mechanism.
Definition: VFS_logger.cpp:192
void email(QString address, QString subject, QString message)
Email an administrator about a problem.
Definition: VFS_logger.cpp:266
QString _adminEmail
For CRITICAL() messages, this is the address that will be emailed.
Definition: VFS_logger.h:30
VFS_node * _messages
A message logging node.
Definition: VFS_logger.h:20
VFS_node * _errors
Error logs, which mean there is a problem but not a crash.
Definition: VFS_logger.h:22
Q_INVOKABLE VFS_logger(int logLevel, int logTime, QString adminAddress)
VFS_logger constructor.
Definition: VFS_logger.cpp:33
VFS_node is the base class from which all other VFS_node classes derive.
Definition: VFS_node.h:143
virtual VFS_node * append(QString name, VFS_node *node, bool containerCheck=true, QString user="server")
Append a VFS_node as a child of this node.
Definition: VFS_node.cpp:1566
virtual VFS_request * createRequest(VFS_request::requestType type, QString path, QString user="unknown", QJsonDocument data=QJsonDocument(), QJsonObject metadata=QJsonObject(), bool dontDelete=false)
Create a new VFS_request with this object as _origin.
Definition: VFS_node.cpp:1913
virtual void issueRequest(VFS_request *t)
A convenience function.
Definition: VFS_node.cpp:1933
VFS_node * find(QString path)
Find a node by string path.
Definition: VFS_node.cpp:1100
void diff(VFS_node *origin, VFS_request *t)
Emit a diff, which will trigger notifySubscribers() for a mounted node.
virtual void applyDiff(VFS_request *r)
Apply a diff received via subscription.
Definition: VFS_node.cpp:1463
The base class for all requests between nodes.
Definition: VFS_node.h:54
@ diff
send a diff (7)
Definition: VFS_node.h:71
@ subscribe
subscribe to a path (9)
Definition: VFS_node.h:73
VFS_node * _origin
the origin of the request
Definition: VFS_node.h:89
QString _user
who initiated this request, mostly for logging
Definition: VFS_node.h:106
QString _reason
if something (probably bad) happened, this is the reason
Definition: VFS_node.h:108
QString _path
the target path remnant... the remaining path element once the request has found its target
Definition: VFS_node.h:95
bool _success
if the request was successfully completed
Definition: VFS_node.h:107
QJsonDocument _data
the request payload
Definition: VFS_node.h:102
VFS_stream stores data sequentially within a buffer.
Definition: VFS_stream.h:7
static void ERROR(QString message, int level=0, QString user="server")
Send a message to the VFS::_errors VFS_stream.
Definition: VFS.cpp:307
message(m)
Change the message of an existing arrowMessage.
#define APPLICATION_NAME
Definition: defines.h:6