|
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