XRootD
XrdSysLogging.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d S y s L o g g i n g . c c */
4 /* */
5 /*(c) 2016 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /*Produced by Andrew Hanushevsky for Stanford University under contract */
7 /* DE-AC02-76-SFO0515 with the Deprtment of Energy */
8 /* */
9 /* This file is part of the XRootD software suite. */
10 /* */
11 /* XRootD is free software: you can redistribute it and/or modify it under */
12 /* the terms of the GNU Lesser General Public License as published by the */
13 /* Free Software Foundation, either version 3 of the License, or (at your */
14 /* option) any later version. */
15 /* */
16 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
17 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
18 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
19 /* License for more details. */
20 /* */
21 /* You should have received a copy of the GNU Lesser General Public License */
22 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
23 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
24 /* */
25 /* The copyright holder's institutional names and contributor's names may not */
26 /* be used to endorse or promote products derived from this software without */
27 /* specific prior written permission of the institution or contributor. */
28 /******************************************************************************/
29 
30 #include <stddef.h>
31 #include <cstdlib>
32 #include <unistd.h>
33 #include <cstdio>
34 
35 #include "XrdSys/XrdSysE2T.hh"
36 #include "XrdSys/XrdSysLogger.hh"
37 #include "XrdSys/XrdSysLogging.hh"
38 #include "XrdSys/XrdSysPlatform.hh"
39 
40 /******************************************************************************/
41 /* S t a t i c O b j e c t s */
42 /******************************************************************************/
43 
44 namespace
45 {
46 static const int buffOvhd = 8;
47 
48 XrdSysMutex msgMutex;
49 XrdSysSemaphore msgAlert(0);
50 XrdSysLogPI_t piLogger = 0;
51 char *pendMsg = 0; // msg to be processed, if nil means none
52 char *lastMsg = 0; // last msg in the processing queue
53 char *buffOrg = 0; // Base address of global message buffer
54 char *buffBeg = 0; // buffOrg + overhead
55 char *buffEnd = 0; // buffOrg + size of buffer
56 struct timeval todLost; // time last message was lost
57 int numLost = 0; // Number of messages lost
58 bool logDone = false;
59 bool doSync = false;
60 
61 static const int syncBSZ = 8192;
62 };
63 
64 pthread_t XrdSysLogging::lpiTID;
65 bool XrdSysLogging::lclOut = false;
66 bool XrdSysLogging::rmtOut = false;
67 
68 /******************************************************************************/
69 /* C o n f i g u r e */
70 /******************************************************************************/
71 
73 {
74  char eBuff[256];
75  int rc;
76 
77 // Set logger parameters
78 //
79  if (parms.hiRes) logr.setHiRes();
80 
81 // If we are going to send output to a local destination, configure it.
82 //
83  if (parms.logfn)
84  {if (strcmp(parms.logfn, "-") && (rc=logr.Bind(parms.logfn,parms.keepV)))
85  {sprintf(eBuff, "Error %d (%s) binding to log file %s.\n",
86  -rc, XrdSysE2T(-rc), parms.logfn);
87  return EMsg(logr, eBuff);
88  }
89  lclOut = true;
90  }
91 
92 // If we are not sending output to a remote destination, we are done
93 //
94  if (!parms.logpi) {lclOut = true; return true;}
95  piLogger= parms.logpi;
96  logDone = !lclOut;
97  rmtOut = true;
98 
99 // We have a plugin, setup the synchronous case if so desired
100 //
101  if (!parms.bufsz)
102  {logr.setForwarding(true);
103  doSync = true;
104  return true;
105  }
106 
107 // Allocate a log buffer
108 //
109  int bsz = (parms.bufsz < 0 ? 65536 : parms.bufsz);
110  rc = posix_memalign((void **)&buffOrg, getpagesize(), bsz);
111  if (rc != 0 || !buffOrg) return EMsg(logr, "Unable to allocate log buffer!\n");
112 
113  buffBeg = buffOrg + buffOvhd;
114  buffEnd = buffOrg + bsz;
115 
116 // Start the forwarding thread
117 //
118  if (XrdSysThread::Run(&lpiTID, Send2PI, (void *)0, 0, "LogPI handler"))
119  {sprintf(eBuff, "Error %d (%s) starting LogPI handler.\n",
120  errno, XrdSysE2T(errno));
121  return EMsg(logr, eBuff);
122  }
123 
124 // We are all done
125 //
126  logr.setForwarding(true);
127  return true;
128 }
129 
130 /******************************************************************************/
131 /* Private: C o p y T r u n c */
132 /******************************************************************************/
133 
134 int XrdSysLogging::CopyTrunc(char *mbuff, struct iovec *iov, int iovcnt)
135 {
136  char *mbP = mbuff;
137  int segLen, bLeft = syncBSZ - 1;
138 
139 // Copy message with truncation
140 //
141  for (int i = 0; i < iovcnt; i++)
142  {segLen = iov[i].iov_len;
143  if (segLen >= bLeft) segLen = bLeft;
144  memcpy(mbP, iov[i].iov_base, segLen);
145  mbP += segLen; bLeft -= segLen;
146  if (bLeft <= 0) break;
147  }
148  *mbP = 0;
149 
150 // Return actual length
151 //
152  return mbP - mbuff;
153 }
154 
155 /******************************************************************************/
156 /* Private: E M s g */
157 /******************************************************************************/
158 
159 bool XrdSysLogging::EMsg(XrdSysLogger &logr, const char *msg)
160 {
161  struct iovec iov[] = {{0,0}, {(char *)msg,0}};
162 
163  iov[1].iov_len = strlen((const char *)iov[1].iov_base);
164  logr.Put(2, iov);
165  return false;
166 }
167 
168 /******************************************************************************/
169 /* F o r w a r d */
170 /******************************************************************************/
171 
172 bool XrdSysLogging::Forward(struct timeval mtime, unsigned long tID,
173  struct iovec *iov, int iovcnt)
174 {
175  MsgBuff *theMsg;
176  char *fence, *freeMsg, *msgText;
177  int dwords, msgLen = 0;
178  bool doPost = false;
179 
180 // Calculate the message length
181 //
182  for (int i = 0; i < iovcnt; i++) msgLen += iov[i].iov_len;
183 
184 // If we are doing synchronous forwarding, do so now (we do not get a lock)
185 //
186  if (doSync)
187  {char *mbP, mbuff[syncBSZ];
188  if (msgLen >= syncBSZ) msgLen = CopyTrunc(mbuff, iov, iovcnt);
189  else {mbP = mbuff;
190  for (int i = 0; i < iovcnt; i++)
191  {memcpy(mbP, iov[i].iov_base, iov[i].iov_len);
192  mbP += iov[i].iov_len;
193  }
194  *mbP = 0;
195  }
196  (*piLogger)(mtime, tID, mbuff, msgLen);
197  return logDone;
198  }
199 
200 // Serialize remainder of code
201 //
202  msgMutex.Lock();
203 
204 // If the message is excessively long, treat it as a lost message
205 //
206  if (msgLen > maxMsgLen)
207  {todLost = mtime;
208  numLost++;
209  msgMutex.UnLock();
210  return logDone;
211  }
212 
213 // Get the actual doublewords bytes we need (account for null byte in the msg).
214 // We need to increase the size by the header size if there are outsanding
215 // lost messages.
216 //
217  dwords = msgLen+8 + sizeof(MsgBuff);
218  if (numLost) dwords += sizeof(MsgBuff);
219  dwords = dwords/8;
220 
221 // Compute the allocation fence. The choices are as follows:
222 // a) When pendMsg is present then the fence is the end of the buffer if
223 // lastMsg >= pendMsg and pendMsg otherwise.
224 // b) When pendMsg is nil then we can reset the buffer pointers so that the
225 // fence is the end of the buffer.
226 //
227  if (pendMsg)
228  {freeMsg = lastMsg + ((MsgBuff *)lastMsg)->buffsz*8;
229  fence = (lastMsg >= pendMsg ? buffEnd : pendMsg);
230  } else {
231  freeMsg = buffBeg;
232  fence = buffEnd;
233  lastMsg = 0;
234  doPost = true;
235  }
236 
237 // Check if there is room for this message. If not, count this as a lost
238 // message and tell the caller full forwarding did not happen.
239 //
240  if ((freeMsg + (dwords*8)) > fence)
241  {todLost = mtime;
242  numLost++;
243  msgMutex.UnLock();
244  return logDone;
245  }
246 
247 // We can allocate everything. So, check if we will be inserting a lost
248 // message entry here. We preallocated this above when numLost != 0;
249 //
250  if (numLost)
251  {theMsg = (MsgBuff *)freeMsg;
252  theMsg->msgtod = mtime;
253  theMsg->tID = tID;
254  theMsg->buffsz = mbDwords;
255  theMsg->msglen = -numLost;
256  if (lastMsg) ((MsgBuff *)lastMsg)->next = freeMsg - buffOrg;
257  lastMsg = freeMsg;
258  freeMsg += msgOff;
259  }
260 
261 // Insert the message
262 //
263  theMsg = (MsgBuff *)freeMsg;
264  theMsg->msgtod = mtime;
265  theMsg->tID = tID;
266  theMsg->next = 0;
267  theMsg->buffsz = dwords;
268  theMsg->msglen = msgLen;
269  if (lastMsg) ((MsgBuff *)lastMsg)->next = freeMsg - buffOrg;
270  lastMsg = freeMsg;
271 
272 // Copy the message text into the buffer
273 //
274  msgText = freeMsg + msgOff;
275  for (int i = 0; i < iovcnt; i++)
276  {memcpy(msgText, iov[i].iov_base, iov[i].iov_len);
277  msgText += iov[i].iov_len;
278  }
279  *msgText = 0;
280 
281 // If we need to write this to another log file do so here.
282 //
283 
284 // Do final post processing (release the lock prior to posting)
285 //
286  if (doPost) pendMsg = freeMsg;
287  msgMutex.UnLock();
288  if (doPost) msgAlert.Post();
289  return logDone;
290 }
291 
292 /******************************************************************************/
293 /* Private: g e t M s g */
294 /******************************************************************************/
295 
296 XrdSysLogging::MsgBuff *XrdSysLogging::getMsg(char **msgTxt, bool cont)
297 {
298  XrdSysMutexHelper msgHelp(msgMutex);
299  MsgBuff *theMsg;
300 
301 // If we got incorrectly posted, ignore this call
302 //
303  if (!pendMsg) return 0;
304 
305 // Check if this is a continuation. If so, skip to next message. If there is no
306 // next message, clear the pendMsg pointer to indicate we stopped pulling any
307 // messages (we will get posted when another message arrives).
308 //
309  if (cont)
310  {if (((MsgBuff *)pendMsg)->next)
311  pendMsg = buffOrg + ((MsgBuff *)pendMsg)->next;
312  else pendMsg = 0;
313  }
314 
315 // Return the message
316 //
317  theMsg = (MsgBuff *)pendMsg;
318  *msgTxt = pendMsg + msgOff;
319  return theMsg;
320 }
321 
322 /******************************************************************************/
323 /* Private: S e n d 2 P I */
324 /******************************************************************************/
325 
326 void *XrdSysLogging::Send2PI(void *arg)
327 {
328  (void)arg;
329  MsgBuff *theMsg;
330  char *msgTxt, lstBuff[80];
331  int msgLen;
332  bool cont;
333 
334 // Infinit loop feeding the logger plugin
335 //
336 do{msgAlert.Wait();
337  cont = false;
338  while((theMsg = getMsg(&msgTxt, cont)))
339  {if ((msgLen = theMsg->msglen) < 0)
340  {int n = -msgLen; // Note we will never overflow lstBuff!
341  msgLen = snprintf(lstBuff, sizeof(lstBuff), "%d message%s lost!",
342  n, (n == 1 ? "" : "s"));
343  msgTxt = lstBuff;
344  }
345  (*piLogger)(theMsg->msgtod, theMsg->tID, msgTxt, msgLen);
346  cont = true;
347  }
348  } while(true);
349 
350 // Here to keep the compiler happy
351 //
352  return (void *)0;
353 }
const char * XrdSysE2T(int errcode)
Definition: XrdSysE2T.cc:104
void(* XrdSysLogPI_t)(struct timeval const &mtime, unsigned long tID, const char *msg, int mlen)
Definition: XrdSysLogPI.hh:51
void setHiRes()
Set log file timstamp to high resolution (hh:mm:ss.uuuu).
void Put(int iovcnt, struct iovec *iov)
static void setForwarding(bool onoff)
Set call-out to logging plug-in on or off.
int Bind(const char *path, int lfh=0)
static bool Forward(struct timeval mtime, unsigned long tID, struct iovec *iov, int iovcnt)
static bool Configure(XrdSysLogger &logr, Parms &parms)
static int Run(pthread_t *, void *(*proc)(void *), void *arg, int opts=0, const char *desc=0)
Parameters to be passed to configure.
XrdSysLogPI_t logpi
-> log plugin object or nil if none
int keepV
log keep argument
const char * logfn
-> log file name or nil if none.
bool hiRes
log using high resolution timestamp
int bufsz
size of message buffer, -1 default, or 0