|
1 /* |
|
2 * Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * Main log server engine. |
|
16 * Process log requests from multiple clients simultaneously. |
|
17 * |
|
18 */ |
|
19 |
|
20 |
|
21 |
|
22 /** |
|
23 @file server.cpp |
|
24 */ |
|
25 #include "server.h" |
|
26 |
|
27 CLogServer* CLogServer::NewL() |
|
28 /** |
|
29 * @return - Instance of the log server |
|
30 */ |
|
31 { |
|
32 CLogServer * server = new (ELeave) CLogServer(); |
|
33 CleanupStack::PushL(server); |
|
34 server->ConstructL(); |
|
35 // CServer base class call |
|
36 server->StartL(KFileLogrerServerName); |
|
37 CleanupStack::Pop(server); |
|
38 return server; |
|
39 } |
|
40 |
|
41 void CLogServer::ConstructL() |
|
42 /** |
|
43 * Second phase construction |
|
44 */ |
|
45 { |
|
46 User::LeaveIfError(Fs().Connect()); |
|
47 } |
|
48 |
|
49 |
|
50 CLogServer::CLogServer() : CServer2(EPriorityStandard,ESharableSessions) |
|
51 /** |
|
52 * Constructor |
|
53 */ |
|
54 { |
|
55 } |
|
56 |
|
57 CLogServer::~CLogServer() |
|
58 /** |
|
59 * Destructor |
|
60 */ |
|
61 { |
|
62 // Close the array of control structures |
|
63 LogControl().Close(); |
|
64 Fs().Close(); |
|
65 } |
|
66 |
|
67 |
|
68 CSession2* CLogServer::NewSessionL(const TVersion& /*aVersion*/,const RMessage2& /*aMessage*/) const |
|
69 /** |
|
70 * @param RMessage - RMessage for the session open |
|
71 */ |
|
72 { |
|
73 // Just create the session |
|
74 CLogSession* session = new (ELeave) CLogSession(); |
|
75 return session; |
|
76 } |
|
77 |
|
78 void CLogServer::ControlComplete(CLogFileControl& aControl) |
|
79 /** |
|
80 * @param aControl - Logfile control class reference |
|
81 * |
|
82 * Checks to see if this control session can be removed |
|
83 */ |
|
84 { |
|
85 // Check session count and the data buffers on the queue |
|
86 if(aControl.SessionCount() || !aControl.QueueEmpty()) |
|
87 return; |
|
88 |
|
89 // There are no subsessions mapped to the logfile control class and |
|
90 // no data buffers on its write queue |
|
91 // Loop through the server's control array and remove it then delete it |
|
92 TInt i; |
|
93 for(i=0;i<LogControl().Count();i++) |
|
94 { |
|
95 if(&aControl == LogControl()[i]) |
|
96 { |
|
97 // Done |
|
98 LogControl().Remove(i); |
|
99 delete &aControl; |
|
100 break; |
|
101 } |
|
102 } |
|
103 // If it's the last one then exit the server |
|
104 if(!LogControl().Count()) |
|
105 CActiveScheduler::Stop(); |
|
106 } |
|
107 |
|
108 |
|
109 /////// |
|
110 |
|
111 CLogSession::CLogSession() |
|
112 /** |
|
113 * Constructor |
|
114 */ |
|
115 { |
|
116 } |
|
117 |
|
118 CLogSession::~CLogSession() |
|
119 /** |
|
120 * Destructor |
|
121 */ |
|
122 { |
|
123 // Check for null |
|
124 // Session close without a createlog call leaves iControl null |
|
125 if(!iControl) |
|
126 return; |
|
127 // A logfile control structure can have multiple server sessions |
|
128 // decrement its server session count |
|
129 iControl->RemoveSession(); |
|
130 CLogServer* p=(CLogServer*) Server(); |
|
131 // Shuts Down the server if this is the last open session |
|
132 p->ControlComplete(*iControl); |
|
133 } |
|
134 |
|
135 void CLogSession::ServiceL(const RMessage2& aMessage) |
|
136 /** |
|
137 * @param aMessage - Function and data for the session |
|
138 */ |
|
139 { |
|
140 switch(aMessage.Function()) |
|
141 { |
|
142 // API CreateLog() call |
|
143 case RFileFlogger::ECreateLog : |
|
144 { |
|
145 // Sanity check to make sure it's not been called multiple times |
|
146 if(iControl) |
|
147 { |
|
148 aMessage.Complete(KErrInUse); |
|
149 break; |
|
150 } |
|
151 // Get the filepath |
|
152 // size policed on the client side |
|
153 TBuf<KMaxLoggerFilePath> logFilePath; |
|
154 // Read it |
|
155 aMessage.ReadL(0,logFilePath); |
|
156 // Get the log mode in the second argument |
|
157 RFileFlogger::TLogMode logMode; |
|
158 logMode = (RFileFlogger::TLogMode)aMessage.Int1(); |
|
159 // Get a pointer to the parent server |
|
160 CLogServer* server=(CLogServer*) Server(); |
|
161 // For compare's convert the whole path to lower case |
|
162 logFilePath.LowerCase(); |
|
163 // Get rid of leading and trailing spaces. |
|
164 logFilePath.Trim(); |
|
165 |
|
166 // Loop the through the server's logfile control class list |
|
167 // to see if there's a match. |
|
168 TInt i; |
|
169 for(i=0;i<server->LogControl().Count();i++) |
|
170 { |
|
171 if(server->LogControl()[i]->LogFile() == logFilePath) |
|
172 // This file's already in open so we don't have to open it |
|
173 break; |
|
174 } |
|
175 TInt err = KErrNone; |
|
176 // Check the count |
|
177 if(i < server->LogControl().Count()) |
|
178 // Map this session to an existing logfile control class in the list |
|
179 iControl = server->LogControl()[i]; |
|
180 else |
|
181 { |
|
182 // Create a new logfile control class |
|
183 // creates/opens the logfile |
|
184 TRAP(err,iControl = CLogFileControl::NewL(*server,logFilePath,logMode)); |
|
185 if(!err) |
|
186 { // nancy |
|
187 // find out the type of output format and assign to this new created control |
|
188 TInt error = logFilePath.Find(_L(".xml")); |
|
189 if(error==KErrNotFound) iControl->SetLogType(CLogFileControl::ETxt); |
|
190 else iControl->SetLogType(CLogFileControl::EXml); |
|
191 // end nancy |
|
192 // Append it to the logfile control class list |
|
193 server->LogControl().Append(iControl); |
|
194 } |
|
195 } |
|
196 if(!err) |
|
197 // Increment its session count |
|
198 iControl->AddSession(); |
|
199 aMessage.Complete(err); |
|
200 } |
|
201 break; |
|
202 // One of the API write calls |
|
203 case RFileFlogger::EWriteLog : |
|
204 { |
|
205 // Sanity check |
|
206 if(!iControl) |
|
207 { |
|
208 aMessage.Complete(KErrNotFound); |
|
209 break; |
|
210 } |
|
211 // Data can be any size |
|
212 // Get the length from second argument |
|
213 TInt bufferLength = aMessage.Int1(); |
|
214 // Get a heap buffer of the right size |
|
215 HBufC8* buffer = HBufC8::NewLC(bufferLength); |
|
216 TPtr8 ptr(buffer->Des()); |
|
217 // read the data |
|
218 aMessage.ReadL(0,ptr); |
|
219 // Get a buffer control class contructed with the heap data |
|
220 // takes ownership |
|
221 CLogBuffer* logBuffer = new (ELeave) CLogBuffer(*buffer); |
|
222 CleanupStack::Pop(buffer); |
|
223 // Add it to the logfile control class buffer queue |
|
224 iControl->AddLogBuffer(*logBuffer); |
|
225 if(!iControl->IsActive()) |
|
226 // AO is idle, kick into life |
|
227 iControl->Kick(); |
|
228 aMessage.Complete(KErrNone); |
|
229 } |
|
230 break; |
|
231 |
|
232 default: |
|
233 break; |
|
234 } |
|
235 } |
|
236 |
|
237 /////// |
|
238 |
|
239 CLogFileControl* CLogFileControl::NewL(CLogServer& aParent, const TDesC& aLogFilePath,RFileFlogger::TLogMode aMode) |
|
240 /** |
|
241 * @param aParent - Server reference for callback |
|
242 * @param aLogFilePath - Full path and filename of the logfile |
|
243 * @param aMode - Overwrite or Append |
|
244 * |
|
245 * First phase construction for logfile control class |
|
246 */ |
|
247 { |
|
248 CLogFileControl* self = new (ELeave) CLogFileControl(aParent,aLogFilePath); |
|
249 CleanupStack::PushL(self); |
|
250 self->ConstructL(aMode); |
|
251 CleanupStack::Pop(); |
|
252 return self; |
|
253 } |
|
254 |
|
255 CLogFileControl::CLogFileControl(CLogServer& aParent,const TDesC& aLogFilePath) : |
|
256 iParent(aParent), |
|
257 iLogFileName(aLogFilePath), |
|
258 iTransmitted(EFalse) |
|
259 /** |
|
260 * @param aParent - Server reference for callback |
|
261 * @param aLogFilePath - Full path and filename of the logfile |
|
262 * |
|
263 * Constructor - Safe initialisation |
|
264 */ |
|
265 { |
|
266 iQueue.SetOffset(CLogBuffer::LinkOffset()); |
|
267 } |
|
268 |
|
269 void CLogFileControl::ConstructL(RFileFlogger::TLogMode aMode) |
|
270 /** |
|
271 * @param aMode - Overwrite or Append |
|
272 * |
|
273 * Second phase construction - Create or open the logfile |
|
274 */ |
|
275 { |
|
276 if(aMode == RFileFlogger::ELogModeOverWrite) |
|
277 // In overwrite mode replace |
|
278 User::LeaveIfError(iLogFile.Replace(iParent.Fs(),iLogFileName,EFileWrite)); |
|
279 else |
|
280 { |
|
281 // For append try open then replace |
|
282 TInt err = iLogFile.Open(iParent.Fs(),iLogFileName,EFileWrite); |
|
283 if(err != KErrNone) |
|
284 // Bomb out if replace fails |
|
285 User::LeaveIfError(iLogFile.Replace(iParent.Fs(),iLogFileName,EFileWrite)); |
|
286 else |
|
287 { |
|
288 // Open worked. Position at EOF |
|
289 TInt pos; |
|
290 iLogFile.Size(pos); |
|
291 User::LeaveIfError(iLogFile.Seek(ESeekEnd,pos)); |
|
292 } |
|
293 } |
|
294 } |
|
295 |
|
296 CLogFileControl::~CLogFileControl() |
|
297 /** |
|
298 * Destructor |
|
299 * The server maintains a list of these classes and will not destruct one if there |
|
300 * is data on its queue |
|
301 * Destructor just closes the file handle. |
|
302 */ |
|
303 { |
|
304 iLogFile.Close(); |
|
305 } |
|
306 |
|
307 void CLogFileControl::RunL() |
|
308 /** |
|
309 * Main File writing pump |
|
310 * Called on write completion or Kick() by the session that accepts the data |
|
311 */ |
|
312 { |
|
313 #if (defined _DEBUG) |
|
314 _LIT(KPanic,"LogEng RunL()"); |
|
315 #endif |
|
316 __ASSERT_DEBUG(iStatus.Int() == KErrNone,User::Panic(KPanic,iStatus.Int())); |
|
317 // Check to see if this is the result of write completion |
|
318 if(iTransmitted) |
|
319 { |
|
320 // Write completed |
|
321 // Remove the buffer at the head of the queue and free it |
|
322 CLogBuffer* buffer = iQueue.First(); |
|
323 iQueue.Remove(*buffer); |
|
324 delete buffer; |
|
325 } |
|
326 // Check to see if there's more on the queue |
|
327 if(!iQueue.IsEmpty()) |
|
328 { |
|
329 // There is so write the head of the queue |
|
330 CLogBuffer* buffer = iQueue.First(); |
|
331 SetActive(); |
|
332 // Set the flag to say we've transmitted |
|
333 iTransmitted = ETrue; |
|
334 |
|
335 // ------------------------------------ |
|
336 if(iLogFormat==ETxt) WriteTxt(buffer->Buf()); |
|
337 else WriteXml(buffer->Buf()); |
|
338 } |
|
339 else |
|
340 { |
|
341 // Nothing on the queue |
|
342 iTransmitted = EFalse; |
|
343 // Call into the server to check if this resource can be freed |
|
344 iParent.ControlComplete(*this); |
|
345 } |
|
346 } |
|
347 |
|
348 void CLogFileControl::WriteXml(const TDesC8 &aDes) |
|
349 /** |
|
350 * @param aDes - send a aDes string in xml format to a log file |
|
351 */ |
|
352 { |
|
353 /*--------- Maintaince Warning: ----------------------------------- |
|
354 ******* the fomat of below is sensible from client original format. |
|
355 ******* Any change should match actual string formated from client. |
|
356 ******* Double check the code on client side |
|
357 |
|
358 * The current assumtion of format: |
|
359 * First string values are seperated by sign " - " and extra pair of fields are |
|
360 * seperated from main log message by long unusual string "LogFieldsRequiredBeingAddedToAboveLogMessage" |
|
361 * The \t used to seperate field name and field value and \r\n used to seperate |
|
362 * each other from those pairs of field |
|
363 * \t\t\t\t\t\t is used to end of whole string |
|
364 --------------------------------------------------------------------------------*/ |
|
365 _LIT8(KxmlHeader,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n<LOGFILE>"); |
|
366 //The order of variables: |
|
367 // time - aTime |
|
368 // Severity - aSeverity |
|
369 // Thread - aThread |
|
370 // Filename - aFilename |
|
371 // Linenumber - aLinenumber |
|
372 // Text - aText |
|
373 |
|
374 // Start of string retrive/deformating |
|
375 HBufC8* pBuf1 = HBufC8::NewL(KMaxLoggerLineLength*2); //(aDes.Length()+200); |
|
376 if(!pBuf1) |
|
377 { |
|
378 return; // no memory |
|
379 } |
|
380 TPtr8 aPtr(pBuf1->Des()); // used for searching |
|
381 TPtr8 alogbuf(pBuf1->Des()); //used for final log |
|
382 aPtr.Append(aDes); |
|
383 TPtrC8 SearchBuf; |
|
384 TInt aCount[8]; |
|
385 aCount[0]=0; |
|
386 TInt posI(0); |
|
387 |
|
388 // retrive log message: |
|
389 // Retrive common part of log message: |
|
390 for(TInt i = 1; i<6; i++) |
|
391 { |
|
392 SearchBuf.Set(aPtr.Mid(posI)); |
|
393 aCount[i]=SearchBuf.Find(KSeperation8)+posI; |
|
394 posI=aCount[i]+3; |
|
395 if(aCount[i]<aCount[i-1]) return; // wrong format string from client |
|
396 } |
|
397 // seperating common log message and extra log fields will be easy for future maintaince. |
|
398 TLogField8* alogField = new TLogField8[6]; // the common part of log message for both |
|
399 // with and without extra log fields |
|
400 if(!alogField) return; // no memory |
|
401 |
|
402 TLogField8* extralogField=NULL; // only applied to extra log fields |
|
403 |
|
404 TInt alength=0; // a length of array of extra log fields |
|
405 |
|
406 alogField[0].iLogTag8.Copy(_L8("TIME")); |
|
407 alogField[1].iLogTag8.Copy(_L8("SEVERITY")); |
|
408 alogField[2].iLogTag8.Copy(_L8("THREAD")); |
|
409 alogField[3].iLogTag8.Copy(_L8("FILENAME")); |
|
410 alogField[4].iLogTag8.Copy(_L8("LINENUMBER")); |
|
411 alogField[5].iLogTag8.Copy(_L8("TEXT")); |
|
412 |
|
413 alogField[0].iLogValue8.Copy(aPtr.Mid(aCount[0],aCount[1]-aCount[0])); |
|
414 for(TInt i=1; i<5; i++) |
|
415 { |
|
416 alogField[i].iLogValue8.Copy(aPtr.Mid(aCount[i]+3,aCount[i+1]-aCount[i]-3)); |
|
417 } |
|
418 |
|
419 SearchBuf.Set(aPtr.Mid(posI)); |
|
420 aCount[6]=SearchBuf.Find(_L8("LogFieldsRequiredBeingAddedToAboveLogMessage"))+posI; |
|
421 if(aCount[6]<posI) // no addtional fields. Find return value is KErrNotFound or >0 |
|
422 { |
|
423 TInt currentLength = alogField[5].iLogValue8.Length() ; |
|
424 //this sentence will append the log info without buffer limit. |
|
425 TInt newAppendLength = aDes.Length()-aCount[5]-5; |
|
426 //The max length for log buff in this |
|
427 TInt bufferLeft = alogField[5].iLogValue8.MaxLength() - currentLength; |
|
428 // get the smaller one between the two |
|
429 TInt enabledLength = bufferLeft > newAppendLength ? newAppendLength : bufferLeft ; |
|
430 alogField[5].iLogValue8.Copy(aPtr.Mid(aCount[5]+3,enabledLength)); |
|
431 //alogField[5].iLogValue8.Copy(aPtr.Mid(aCount[5]+3,aDes.Length()-aCount[5]-5)); |
|
432 } |
|
433 else |
|
434 { |
|
435 alogField[5].iLogValue8.Copy(aPtr.Mid(aCount[5]+3,aCount[6]-aCount[5]-5)); |
|
436 posI=aCount[6]+45; //45 is from the length of long string and a tab |
|
437 SearchBuf.Set(aPtr.Mid(posI)); |
|
438 aCount[7]=SearchBuf.Find(_L8("\r\n"))+posI; |
|
439 TLex8 lex(aPtr.Mid(posI,aCount[7]-posI)); // get the length |
|
440 TInt err=lex.Val(alength); |
|
441 if (err) alength=0; // ignor the extra log fields. Let the log go |
|
442 |
|
443 // Retrive the extra log fields |
|
444 extralogField = new TLogField8[alength]; |
|
445 if(!extralogField) return; // no memory |
|
446 for(TInt i=0; i<alength; i++) |
|
447 { |
|
448 aCount[6]=aCount[7]+2; |
|
449 SearchBuf.Set(aPtr.Mid(aCount[6])); |
|
450 aCount[7]=SearchBuf.Find(_L8("\t"))+aCount[6]; |
|
451 extralogField[i].iLogTag8.Copy(aPtr.Mid(aCount[6],aCount[7]-aCount[6])); |
|
452 aCount[6]=aCount[7]+1; |
|
453 SearchBuf.Set(aPtr.Mid(aCount[6])); |
|
454 aCount[7]=SearchBuf.Find(_L8("\r\n"))+aCount[6]; |
|
455 extralogField[i].iLogValue8.Copy(aPtr.Mid(aCount[6],aCount[7]-aCount[6])); |
|
456 } |
|
457 } |
|
458 |
|
459 // Start to organize an XML format: |
|
460 TInt afileSize; |
|
461 _LIT(KLogMutex, "LoggingServerMutex"); |
|
462 RMutex mutex; |
|
463 TInt r = mutex.CreateGlobal(KLogMutex); |
|
464 if(r==KErrAlreadyExists) |
|
465 r = mutex.OpenGlobal(KLogMutex); |
|
466 |
|
467 if(!r) mutex.Wait(); // if still failed, let logging go |
|
468 //without bother the mutex |
|
469 |
|
470 iLogFile.Size(afileSize); |
|
471 if(afileSize<12) // 12 is from charters of "\r\n</LOGFILE>" |
|
472 //It shoud happened once at the beginning of the file |
|
473 // such as overwrite mode |
|
474 { |
|
475 afileSize=12; // used for lock position |
|
476 alogbuf.Copy(KxmlHeader); |
|
477 } |
|
478 alogbuf.Append(_L8("\r\n<MESSAGE>\r\n")); |
|
479 for(TInt i=0; i<6; i++) |
|
480 { |
|
481 alogbuf.Append(_L8(" <")); |
|
482 alogbuf.Append(alogField[i].iLogTag8); |
|
483 alogbuf.Append(_L8(">")); |
|
484 alogbuf.Append(alogField[i].iLogValue8); |
|
485 alogbuf.Append(_L8("</")); |
|
486 alogbuf.Append(alogField[i].iLogTag8); |
|
487 alogbuf.Append(_L8(">\r\n")); |
|
488 } |
|
489 for(TInt i=0; i<alength; i++) |
|
490 { |
|
491 alogbuf.Append(_L8(" <")); |
|
492 alogbuf.Append(extralogField[i].iLogTag8); |
|
493 alogbuf.Append(_L8(">")); |
|
494 alogbuf.Append(extralogField[i].iLogValue8); |
|
495 alogbuf.Append(_L8("</")); |
|
496 alogbuf.Append(extralogField[i].iLogTag8); |
|
497 alogbuf.Append(_L8(">\r\n")); |
|
498 } |
|
499 |
|
500 alogbuf.Append(_L8("</MESSAGE>")); |
|
501 alogbuf.Append(_L8("\r\n</LOGFILE>")); |
|
502 |
|
503 iLogFile.Write(afileSize-12,alogbuf,iStatus); |
|
504 iLogFile.Flush(); |
|
505 |
|
506 if(!r) |
|
507 { |
|
508 mutex.Signal(); |
|
509 mutex.Close(); |
|
510 } |
|
511 |
|
512 if(extralogField) delete[] extralogField; |
|
513 delete[] alogField; |
|
514 delete pBuf1; |
|
515 |
|
516 } |
|
517 |
|
518 void CLogFileControl::WriteTxt(const TDesC8 &aDes) |
|
519 /** |
|
520 * @param aDes - send a aDes string in xml format to a log file |
|
521 */ |
|
522 { |
|
523 /*--------- Maintaince Warning for aLogBuffer ----------------------------------- |
|
524 ******* the fomat of below is sensible from client original format. |
|
525 ******* Any change should match actual string formated from client. |
|
526 ******* Double check the code on client side |
|
527 |
|
528 * The current assumtion of format: |
|
529 * First string values are seperated by sign " - " and extra pair of fields are |
|
530 * seperated from main log message by long unusual string "LogFieldsRequiredBeingAddedToAboveLogMessage" |
|
531 * The \t used to seperate field name and field value and \r\n used to seperate |
|
532 * each other from those pairs of field |
|
533 --------------------------------------------------------------------------------*/ |
|
534 iLogFile.Write(aDes,iStatus); |
|
535 } |
|
536 /////// |
|
537 CLogBuffer::CLogBuffer(HBufC8& aLogBuffer) : iLogBuffer(aLogBuffer) |
|
538 /** |
|
539 * @param aLogBuffer - Heap descriptor. This class takes ownership |
|
540 * Constructor |
|
541 */ |
|
542 { |
|
543 } |
|
544 |
|
545 CLogBuffer::~CLogBuffer() |
|
546 /** |
|
547 * Destructor |
|
548 * This class owns a heap buffer so just free it |
|
549 */ |
|
550 { |
|
551 delete &iLogBuffer; |
|
552 } |