| 
2 | 2 | #include "TkUtil/ApplicationStats.h"  | 
3 | 3 | #include "TkUtil/Exception.h"  | 
4 | 4 | #include "TkUtil/File.h"  | 
 | 5 | +#include "TkUtil/NumericConversions.h"  | 
5 | 6 | #include "TkUtil/UTF8String.h"  | 
6 | 7 | 
 
  | 
7 | 8 | #include <TkUtil/Date.h>  | 
 | 
16 | 17 | #include <sys/file.h>	// flock  | 
17 | 18 | #include <stdio.h>		// fopen, fseek, fscanf, fprintf  | 
18 | 19 | #include <string.h>		// strerror  | 
 | 20 | +#include <sys/stat.h>	// fchmod  | 
19 | 21 | #include <sys/types.h>  | 
20 | 22 | #include <unistd.h>		// fork, setsid  | 
21 | 23 | 
 
  | 
@@ -49,6 +51,15 @@ ApplicationStats::~ApplicationStats ( )  | 
49 | 51 | }	// ApplicationStats::~ApplicationStats  | 
50 | 52 | 
 
  | 
51 | 53 | 
 
  | 
 | 54 | +string ApplicationStats::getFileName (const string& appName, const string& logDir, size_t month, size_t year)  | 
 | 55 | +{  | 
 | 56 | +	UTF8String	fileName (Charset::UTF_8);  | 
 | 57 | +	fileName << logDir << "/" << appName << "_" << NumericConversions::toStr (year, 4) << NumericConversions::toStr (month, 2) << ".logs";  | 
 | 58 | + | 
 | 59 | +	return fileName.utf8 ( );  | 
 | 60 | +}	// ApplicationStats::getFileName  | 
 | 61 | + | 
 | 62 | +	  | 
52 | 63 | void ApplicationStats::logUsage (const string& appName, const string& logDir)  | 
53 | 64 | {  | 
54 | 65 | 	// En vue de ne pas altérer le comportement de l'application tout est effectuée dans un processus fils.  | 
@@ -81,11 +92,11 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)  | 
81 | 92 | 	// Le nom du fichier :  | 
82 | 93 | 	const Date		date;  | 
83 | 94 | 	const string	user (UserData (true).getName ( ));  | 
84 |  | -	UTF8String	fileName;  | 
85 |  | -	fileName << logDir << "/" << appName << "_" << IN_UTIL setw (4) << date.getYear ( ) << setw (2) << (unsigned long)date.getMonth ( ) << ".logs";  | 
 | 95 | +	UTF8String	fileName (getFileName (appName, logDir, (unsigned long)date.getMonth ( ), date.getYear ( )), Charset::UTF_8);  | 
86 | 96 | 
 
  | 
87 | 97 | 	// On ouvre le fichier en lecture/écriture :  | 
88 |  | -	FILE* file	= fopen (fileName.utf8 ( ).c_str ( ), "r+");		// Ne créé pas le fichier => on le créé ci-dessous si nécessaire :  | 
 | 98 | +	FILE* 		file	= fopen (fileName.utf8 ( ).c_str ( ), "r+");		// Ne créé pas le fichier => on le créé ci-dessous si nécessaire :  | 
 | 99 | +	const bool	created	= NULL == file ? true : false;  | 
89 | 100 | 	file		= NULL == file ? fopen (fileName.utf8 ( ).c_str ( ), "a+") : file;  | 
90 | 101 | 	if (NULL == file)  | 
91 | 102 | 	{  | 
@@ -134,6 +145,18 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)  | 
134 | 145 | 		fclose (file);  | 
135 | 146 | 		return;  | 
136 | 147 | 	}	// if (0 != flock (fd, LOCK_EX))  | 
 | 148 | +	  | 
 | 149 | +	// Conférer aufichier les droits en écriture pour tous le monde si il vient d'être créé :  | 
 | 150 | +	if (true == created)  | 
 | 151 | +	{  | 
 | 152 | +		if (0 != fchmod (fd, S_IRWXU | S_IRWXG | S_IRWXO))  | 
 | 153 | +		{  | 
 | 154 | +			ConsoleOutput::cerr ( ) << "Erreur lors du confèrement à autrui des droits en écriture sur le fichier de logs " << fileName << " : " << strerror (errno) << co_endl;  | 
 | 155 | +			fclose (file);  | 
 | 156 | +			return;  | 
 | 157 | + | 
 | 158 | +		}	// if (0 != fchmod (fd, S_IRWXU | S_IRWXG | S_IRWXO))  | 
 | 159 | +	}	// if (true == created)  | 
137 | 160 | 
 
  | 
138 | 161 | 	// Lecture et actualisation des logs existants :  | 
139 | 162 | 	map<string, size_t>		logs;  | 
@@ -200,8 +223,111 @@ void ApplicationStats::logUsage (const string& appName, const string& logDir)  | 
200 | 223 | 	{  | 
201 | 224 | 		ConsoleOutput::cerr ( ) << "Erreur lors du déverrouillage du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;  | 
202 | 225 | 		fclose (file);  | 
203 |  | -	}	// if (0 != flock (fd, LOCK_UN))	  | 
 | 226 | +	}	// if (0 != flock (fd, LOCK_UN))  | 
 | 227 | +	  | 
 | 228 | +	fclose (file);  | 
 | 229 | +	file	= NULL;  | 
 | 230 | +	fd		= -1;  | 
204 | 231 | }	// ApplicationStats::logUsage  | 
205 | 232 | 
 
  | 
206 | 233 | 
 
  | 
 | 234 | +void ApplicationStats::logStats (std::ostream& output, const std::string& appName, const string& from, const string& to, const std::string& logDir)  | 
 | 235 | +{  | 
 | 236 | +	map<string, size_t> logs;  | 
 | 237 | +	const size_t		fromMonth	= NumericConversions::strToULong (UTF8String (from).substring (0, 1));  | 
 | 238 | +	const size_t		fromYear	= NumericConversions::strToULong (UTF8String (from).substring (2));  | 
 | 239 | +	const size_t		toMonth		= NumericConversions::strToULong (UTF8String (to).substring (0, 1));  | 
 | 240 | +	const size_t		toYear		= NumericConversions::strToULong (UTF8String (to).substring (2));  | 
 | 241 | + | 
 | 242 | +	if (fromYear == toYear)  | 
 | 243 | +	{  | 
 | 244 | +		for (size_t m = fromMonth; m <= toMonth; m++)  | 
 | 245 | +		{  | 
 | 246 | +			cout << "Performing " << m << "/" << fromYear << endl;  | 
 | 247 | +			UTF8String	fileName (getFileName (appName, logDir, m, fromYear), Charset::UTF_8);  | 
 | 248 | + | 
 | 249 | +			if (0 != readLogs (fileName, logs))  | 
 | 250 | +				ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;  | 
 | 251 | +		}	// for (size_t m = fromMonth; m <= toMonth; m++)  | 
 | 252 | +	}	// if (fromYear == toYear)  | 
 | 253 | +	else  | 
 | 254 | +	{  | 
 | 255 | +		for (size_t m = fromMonth; m <= 12; m++)  | 
 | 256 | +		{  | 
 | 257 | +			cout << "Performing " << m << "/" << fromYear << endl;  | 
 | 258 | +			UTF8String	fileName (getFileName (appName, logDir, m, fromYear), Charset::UTF_8);  | 
 | 259 | + | 
 | 260 | +			if (0 != readLogs (fileName, logs))  | 
 | 261 | +				ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;  | 
 | 262 | +		}  | 
 | 263 | +		for (size_t y = fromYear + 1; y < toYear; y++)  | 
 | 264 | +			for (size_t m = 1; m <= 12; m++)  | 
 | 265 | +			{  | 
 | 266 | +				cout << "Performing " << m << "/" << y << endl;  | 
 | 267 | +				UTF8String	fileName (getFileName (appName, logDir, m, y), Charset::UTF_8);  | 
 | 268 | + | 
 | 269 | +				if (0 != readLogs (fileName, logs))  | 
 | 270 | +					ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;  | 
 | 271 | +			}  | 
 | 272 | +		for (size_t m = 1; m <= toMonth; m++)  | 
 | 273 | +		{  | 
 | 274 | +			cout << "Performing " << m << "/" << toYear << endl;  | 
 | 275 | +			UTF8String	fileName (getFileName (appName, logDir, m, toYear), Charset::UTF_8);  | 
 | 276 | + | 
 | 277 | +			if (0 != readLogs (fileName, logs))  | 
 | 278 | +				ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " : " << strerror (errno) << co_endl;  | 
 | 279 | +		}  | 
 | 280 | +	}	// else if (fromYear == toYear)  | 
 | 281 | +	  | 
 | 282 | +	// On imprime les résultats dans le flux :  | 
 | 283 | +	for (map<string, size_t>::const_iterator itl =  logs.begin ( ); logs.end ( ) != itl; itl++)  | 
 | 284 | +		output << (*itl).first << "\t\t" << (*itl).second << endl;  | 
 | 285 | +}	// ApplicationStats::logStats  | 
 | 286 | + | 
 | 287 | + | 
 | 288 | +int ApplicationStats::readLogs (const string& fileName, map<string, size_t>& logs)  | 
 | 289 | +{  | 
 | 290 | +	File	logFile (fileName);  | 
 | 291 | +	if (false == logFile.isReadable ( ))  | 
 | 292 | +		return 0;  | 
 | 293 | + | 
 | 294 | +	int	retval	= 0;  | 
 | 295 | +	errno		= 0;  | 
 | 296 | +	FILE*	file	= fopen (fileName.c_str ( ), "r");  | 
 | 297 | +	if (NULL != file)  | 
 | 298 | +	{  | 
 | 299 | +		retval	= readLogs (*file, fileName, logs);  | 
 | 300 | +		fclose (file);  | 
 | 301 | +	}	// if (NULL != file)  | 
 | 302 | + | 
 | 303 | +	return retval;  | 
 | 304 | +}	// ApplicationStats::readLogs  | 
 | 305 | + | 
 | 306 | + | 
 | 307 | +int ApplicationStats::readLogs (FILE& file, const string& fileName, map<string, size_t>& logs)  | 
 | 308 | +{  | 
 | 309 | +	errno			= 0;  | 
 | 310 | +	size_t	line	= 1, count	= 0;  | 
 | 311 | +	int		flag	= 0;  | 
 | 312 | +	char	name [256];  | 
 | 313 | +	while (2 == (flag = fscanf (&file, "%s\t%u", name, &count)))  | 
 | 314 | +	{  | 
 | 315 | +		line++;  | 
 | 316 | +		map<string, size_t>::iterator	itl	= logs.find (name);  | 
 | 317 | +		if (logs.end ( ) == itl)  | 
 | 318 | +			logs.insert (pair<string, size_t> (name, count));  | 
 | 319 | +		else  | 
 | 320 | +			(*itl).second	+= count;  | 
 | 321 | +		count	= 0;  | 
 | 322 | +	}	// while (2 == fscanf (file, "%s\t%u", name, &count))  | 
 | 323 | +	if ((flag < 2) && (EOF != flag))  | 
 | 324 | +	{  | 
 | 325 | +		ConsoleOutput::cerr ( ) << "Erreur lors de la lecture du fichier de logs " << fileName << " en ligne " << (unsigned long)line << " : fichier probablement corrompu." << co_endl;  | 
 | 326 | +		return -1;  | 
 | 327 | +	}	// if (flag < 2)  | 
 | 328 | + | 
 | 329 | +	return errno;  | 
 | 330 | +}	// ApplicationStats::readLogs  | 
 | 331 | + | 
 | 332 | + | 
207 | 333 | END_NAMESPACE_UTIL  | 
0 commit comments