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