EMANE  1.2.1
logservice.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2016 - Adjacent Link LLC, Bridgewater, New Jersey
3  * Copyright (c) 2008 - DRS CenGen, LLC, Columbia, Maryland
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of DRS CenGen, LLC nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 
35 #define _GLIBCXX_USE_NANOSLEEP // gets us sleep_for()
36 
37 #include "logservice.h"
38 
39 #include "loggerrecordmessage.h"
40 #include "loggerlevelconvert.h"
41 #include "loggermessage.pb.h"
42 
43 #include <cstdarg>
44 #include <cstdio>
45 #include <chrono>
46 #include <thread>
47 #include <iostream>
48 #include <algorithm>
49 #include <sys/eventfd.h>
50 #include <sys/epoll.h>
51 #include <unistd.h>
52 
53 namespace
54 {
55  const char * LEVELSTRING[] =
56  {
57  "NONE", // 0
58  "ABORT", // 1
59  "ERROR", // 2
60  "INFO", // 3
61  "DEBUG", // 4
62  };
63 
64  const unsigned short MAX_PACKET_LEN = 0xffff;
65 }
66 
67 #define TOTAL_LEVELS (static_cast<int>(sizeof(LEVELSTRING) / sizeof(char *)))
68 #define MAX_LEVEL (TOTAL_LEVELS - 1)
69 
71  thread_{},
72  level_{ABORT_LEVEL},
73  bOpenBackend_{false},
74  u32LogSequenceNumber_{},
75  pStream_{&std::cout},
76  iEventFd_{},
77  iepollFd_{}
78 {
79  bOpenBackend_ = true;
80 
81  udpLoggerTxSocket_.open(INETAddr{"127.0.0.1",0});
82 
83  localSocketAddress_ = udpLoggerTxSocket_.getLocalAddress();
84 
85  iEventFd_ = eventfd(0,0);
86 
87  iepollFd_ = epoll_create1(0);
88 
89  // add the eventfd socket to the epoll instance
90  struct epoll_event ev;
91  ev.events = EPOLLIN;
92  ev.data.fd = iEventFd_;
93  epoll_ctl(iepollFd_,EPOLL_CTL_ADD,iEventFd_,&ev);
94 
95  // add the backend logger socket to the epoll instance
96  ev.events = EPOLLIN;
97  ev.data.fd = udpLoggerTxSocket_.getHandle();
98  epoll_ctl(iepollFd_,EPOLL_CTL_ADD,udpLoggerTxSocket_.getHandle(),&ev);
99 
100  thread_ = std::thread{&LogService::processControlMessages, this};
101 };
102 
104 {
105  std::this_thread::sleep_for(DoubleSeconds(0.5));
106 
107  // signal thread procedure shutdown
108  const uint64_t one{1};
109  write(iEventFd_,&one,sizeof(one));
110 
111  thread_.join();
112 
113  close(iepollFd_);
114  close(iEventFd_);
115 };
116 
117 void EMANE::LogService::log(LogLevel level, const char *fmt, ...)
118 {
119  if(level <= level_)
120  {
121  va_list ap;
122 
123  va_start(ap, fmt);
124 
125  vlog_i(level,fmt,ap);
126 
127  va_end(ap);
128  }
129 }
130 
131 
132 void EMANE::LogService::log(LogLevel level, const Strings & strings)
133 {
134  if(level <= level_)
135  {
136  log_i(level, strings);
137  }
138 }
139 
140 
141 void EMANE::LogService::vlog(LogLevel level, const char *fmt, va_list ap)
142 {
143  if(level <= level_)
144  {
145  vlog_i(level, fmt, ap);
146  }
147 }
148 
149 
150 void EMANE::LogService::redirectLogsToFile(const std::string & file)
151 {
152  ofs_.open(file,std::ofstream::trunc);
153  pStream_ = &ofs_;
154 }
155 
156 
158 {
159  level_ = level;
160 }
161 
162 
163 bool EMANE::LogService::isLogAllowed(LogLevel level) const
164 {
165  return level <= level_;
166 }
167 
168 
170 {
171  if(!bOpenBackend_)
172  {
173  bOpenBackend_ = true;
174 
175  udpLoggerTxSocket_.open(INETAddr{"127.0.0.1",0});
176 
177  localSocketAddress_ = udpLoggerTxSocket_.getLocalAddress();
178 
179  thread_ = std::thread{&LogService::processControlMessages, this};
180  }
181 }
182 
183 
184 void EMANE::LogService::processControlMessages(void)
185 {
186  char buf[MAX_PACKET_LEN] ={0};
187 
188  char errmsg[1024] = {0};
189 
190  int iRxLength{};
191 
192  std::uint16_t * pu16HeaderLength = reinterpret_cast<std::uint16_t *> (buf);
193 
194  int iMinExpectedLength = static_cast<int> (sizeof(*pu16HeaderLength));
195 
196  struct epoll_event events[2];
197 
198  int nfds{};
199 
200  bool bDone{};
201 
202  while(!bDone)
203  {
204  nfds = epoll_wait(iepollFd_,events,2,-1);
205 
206  if(nfds == -1)
207  {
208  break;
209  }
210 
211  for(int n = 0; n < nfds; ++n)
212  {
213  if(events[n].data.fd == udpLoggerTxSocket_.getHandle())
214  {
215  // wait for message here, the header len is the fisrt 2 bytes in network byte order
216  if((iRxLength = udpLoggerTxSocket_.recv(buf,sizeof(buf),0)) > iMinExpectedLength)
217  {
218  EMANEMessage::LoggerMessage loggerMessage;
219 
220  if(!loggerMessage.ParseFromArray(buf + sizeof(*pu16HeaderLength),
221  ntohs(*pu16HeaderLength)))
222  {
223  snprintf(errmsg,
224  sizeof(errmsg),
225  "!!! logger message failed ParseFromArray !!!");
226 
227  writeLogString(errmsg);
228  }
229  else
230  {
231  if(loggerMessage.type() == EMANEMessage::LoggerMessage_Type_RECORD)
232  {
233  if(loggerMessage.has_record())
234  {
235  writeLogString(loggerMessage.record().record().c_str());
236  }
237  else
238  {
239  snprintf(errmsg,
240  sizeof(errmsg),
241  "!!! logger message type RECORD does NOT have record !!!");
242 
243  writeLogString(errmsg);
244  }
245  }
246  else
247  {
248  snprintf(errmsg,
249  sizeof(errmsg),
250  "!!! unhandled logger message type %d !!!",
251  loggerMessage.type());
252 
253  writeLogString(errmsg);
254  }
255  }
256  }
257  else if(iRxLength < 0)
258  {
259  // socket read error
260  snprintf(errmsg,
261  sizeof(errmsg),
262  "!!! socket read error (%s) !!!",
263  strerror(errno));
264 
265  writeLogString(errmsg);
266 
267  bDone = true;
268 
269  break;
270  }
271  else if(iRxLength == 0)
272  {
273  // socket empty
274  snprintf(errmsg, sizeof(errmsg), "socket empty, bye");
275 
276  writeLogString(errmsg);
277 
278  bDone = true;
279 
280  break;
281  }
282  else
283  {
284  // socket read length short
285  snprintf(errmsg, sizeof(errmsg),
286  "!!! socket read len of %d, msg length must be greater than %d, ignore !!!",
287  iRxLength, iMinExpectedLength);
288 
289  writeLogString(errmsg);
290  }
291  }
292  else
293  {
294  bDone = true;
295  break;
296  }
297  }
298  }
299 }
300 
301 
302 
303 
304 
305 
306 void EMANE::LogService::log_i(LogLevel level, const char *fmt, ...)
307 {
308  va_list ap;
309 
310  va_start(ap, fmt);
311 
312  vlog_i(level,fmt,ap);
313 
314  va_end(ap);
315 }
316 
317 
318 
319 void EMANE::LogService::log_i(LogLevel level, const Strings & strings)
320 {
321  std::string sMessage{};
322 
323  std::for_each(strings.begin(),
324  strings.end(),
325  [&sMessage](const std::string & sPart){sMessage += sPart; sMessage.push_back(' ');});
326 
327  log_i(level,"%s",sMessage.c_str());
328 }
329 
330 
331 void EMANE::LogService::vlog_i(LogLevel level, const char *fmt, va_list ap)
332 {
333  auto now = Clock::now();
334 
335  std::time_t t{Clock::to_time_t(now)};
336 
337  std::tm ltm;
338 
339  localtime_r(&t, &ltm);
340 
341  // buffer size is MAX_LOG_LENGTH + timestamp + level
342  const int iBuffSize{MAX_LOG_LENGTH + 22};
343  char buff[iBuffSize] = {0};
344 
345  int iLen = snprintf(buff, sizeof(buff),"%02d:%02d:%02d.%06lu %5s ",
346  ltm.tm_hour,
347  ltm.tm_min,
348  ltm.tm_sec,
349  std::chrono::duration_cast<Microseconds>(now.time_since_epoch()).count()%1000000,
350  level >= 0 && level < TOTAL_LEVELS ? LEVELSTRING[level] : "?");
351 
352  if(iLen < 0 || iLen > iBuffSize)
353  {
354  // silently discard
355  return;
356  }
357 
358  int iAppended = vsnprintf(buff + iLen, iBuffSize - iLen, fmt, ap);
359 
360  if(iAppended > 0)
361  {
362  iLen += iAppended;
363 
364  if(iLen > iBuffSize)
365  {
366  iLen = iBuffSize;
367  }
368  }
369 
370  Utils::VectorIO outputVector(2);
371 
372  // set buff, len, level and seq num in msg hdr, len does not include terminationg byte
374  static_cast<size_t>(iLen),
375  level,
376  u32LogSequenceNumber_++};
377 
378  Serialization serialization{msg.serialize()};
379 
380  // the header len in net byte order
381  std::uint16_t u16HeaderLen = htons(serialization.size());
382 
383  outputVector[0] = Utils::make_iovec((void *) &u16HeaderLen, sizeof(u16HeaderLen));
384  outputVector[1] = Utils::make_iovec((void *) serialization.c_str(), serialization.size());
385 
386  udpLoggerTxSocket_.send(static_cast<const iovec*>(&outputVector[0]),
387  static_cast<int>(outputVector.size()),
388  localSocketAddress_);
389 }
390 
391 
392 void EMANE::LogService::writeLogString(const char * pzLogMessage)
393 {
394  (*pStream_)<<pzLogMessage<<std::endl;
395 }
std::string Serialization
Definition: serializable.h:42
void open(const INETAddr &address, bool bReuseAddress=false)
void void vlog(LogLevel level, const char *format, va_list ap)
Definition: logservice.cc:141
#define TOTAL_LEVELS
Definition: logservice.cc:67
iovec make_iovec(void *base, std::size_t len)
Definition: vectorio.h:46
ssize_t send(const iovec *iov, int iovcnt, const INETAddr &remoteAddress, int flags=0) const
std::vector< iovec > VectorIO
Definition: vectorio.h:43
std::uint8_t buff[ETH_ALEN]
Definition: netutils.h:390
void log(LogLevel level, const char *format,...) __attribute__((format(printf
Definition: logservice.cc:117
void redirectLogsToFile(const std::string &file)
Definition: logservice.cc:150
std::chrono::duration< double > DoubleSeconds
Definition: types.h:47
std::list< std::string > Strings
Definition: types.h:66
ssize_t recv(void *buf, size_t len, int flags=0)
void setLogLevel(LogLevel level)
Definition: logservice.cc:157
INETAddr getLocalAddress() const