using System; using System.IO; using System.Threading; namespace SteamWare { /// /// classe gesione log files applicazioni /// public class logger { #region dichiarazione variabili /// /// directory base x logs /// protected string _logBaseDir; /// /// nome del file corrente /// protected string _logfileName; /// /// max mb di log da accumulare /// protected int _logMaxMb; /// /// controlla se si debba mantenere sotto controllo la dimensioen della cartella logs /// protected bool _doShrinkFolder; /// /// Ultima verifica directory (x shrink) /// private static DateTime lastDirCheck; /// /// metodo gestione wirteLock su file log /// private static ReaderWriterLockSlim _readWriteLock = new ReaderWriterLockSlim(); /// /// Indica se sia abilitato log diagnostico esteso... /// protected bool enableDumpDiag = false; #endregion #region metodi esposti /// /// singleton del logger /// public static logger lg = new logger(); /// /// avvio del logger nella dir desiderata /// public logger() { try { _logBaseDir = SteamwareStrings.getFilePath(memLayer.ML.confReadString("_logDir")); } catch { _logBaseDir = "~/logs/"; } _logMaxMb = 100; // di default 100 mb... try { _doShrinkFolder = memLayer.ML.confReadBool("doShrinkFolder"); } catch { _doShrinkFolder = true; } try { enableDumpDiag = memLayer.ML.confReadBool("enableDumpDiag"); } catch { enableDumpDiag = false; } // all'avvio imposto ultimo check a 23 ore fa... così farà shrink DOPO 1 h da avvio lastDirCheck = DateTime.Now.AddHours(-23); } /// /// livello di log applicazione (da web.config, chiave '_logLevel') /// public int logLevel { get { int answ = -1; try { answ = memLayer.ML.CRI("_logLevel"); } catch { scriviLog("non ho trovato chiave di registro x logLevel...", tipoLog.ERROR); } return answ; } } /// /// avvio del logger nella dir desiderata /// public logger(string _baseDir) { _logBaseDir = _baseDir; _logMaxMb = 40; // di default 40 mb... } /// /// avvio del logger nella dir desiderata con il max di dati indicato /// public logger(string _baseDir, int _logMaxMB) { _logBaseDir = _baseDir; _logMaxMb = _logMaxMB; } /// /// resetta il logfile odierno /// public void resetLogFile() { newLogfileName(); FileInfo fi = new FileInfo(_logfileName); fi.Delete(); scriviLog("ResetFile", tipoLog.STARTUP); } /// /// scrive sul file log di default il valore della variabile string passata su una riga... (tab delim?!?) /// /// testo iniziale del log /// public bool scriviLog(string _testoPre) { bool needWrite = false; bool fatto = false; // preparo hash redis string hKey = _testoPre.Replace(":", "_").Replace("/", "_").Replace("#", "_").Replace("(", "").Replace(")", "").Replace("|", "_").Replace(" ", "_").Replace("__", "_").Replace("__", "_").Replace("__", "_").Replace("__", "_"); //se troppo lunga trimmo... if (hKey.Length > 100) { hKey = hKey.Substring(0, 100); } string hVetoErrore = memLayer.ML.redHash("Logger:Veto:" + hKey); string hCountErrore = memLayer.ML.redHash("Logger:Counter:" + hKey); bool redisEnabled = memLayer.ML.confReadBool("cacheOnRedis"); // verifico mitigazione int logMitigSec = 30; try { if (memLayer.ML.confReadString("logMitigSec") != "") { logMitigSec = memLayer.ML.confReadInt("logMitigSec"); } } catch { } // verifico SE HO redis (in quel caso gli errori li loggo 1 volta poi metto un VETO ed un counter) if (redisEnabled) { // cerco se ho un veto precedente in redis string vetoPar = memLayer.ML.getRSV(hVetoErrore); if (vetoPar != null && vetoPar != "") { needWrite = false; } else { // metto veto per TTL default... (durata std = 30 sec) memLayer.ML.setRSV(hVetoErrore, DateTime.UtcNow.ToString(), logMitigSec); // abilito log... needWrite = true; } memLayer.ML.setRCntI(hCountErrore); } else { // altrimenti log direttamente needWrite = true; } // se devo DAVVERO scrivere if (needWrite) { if (redisEnabled) { int numRep = memLayer.ML.getRCnt(hCountErrore); if (numRep > 1) { // cambio messaggio con un inizio pari al num di ripetizioni... _testoPre = string.Format("{0} x {1}", numRep, _testoPre); } // reset counter memLayer.ML.resetRCnt(hCountErrore); } fatto = doScriviLog(_testoPre); } return fatto; } /// /// Vera chiamata scrittura log x override con gestione REDIS di calmierazione /// /// /// private bool doScriviLog(string _testoPre) { bool fatto = false; // attenzione: rimpiazzo eventuali "
" con newline... string _testo = string.Format("{0:H:mm:ss ffff} \t{1}", DateTime.Now, _testoPre.Replace("
", Environment.NewLine).Replace("
", Environment.NewLine).Replace("
", Environment.NewLine)); // se è configurato x shrink ed è passato almeno 1 gg da ultimo check... if (_doShrinkFolder && lastDirCheck.AddDays(1) < DateTime.Now) { // verifica dim directory ed eventualmente cancella... - opzionale shrinkDir(); // salvo nuova ora di check lastDirCheck = DateTime.Now; } // (ri)genera il nome del file di log... newLogfileName(); // scrivo thread safe fatto = WriteToFileThreadSafe(_logfileName, _testo); return fatto; } /// /// Esecuzione scrittura ThreadSafe /// /// /// public bool WriteToFileThreadSafe(string logPath, string text2write) { bool answ = false; // Set Status to Locked _readWriteLock.EnterWriteLock(); try { // Append text to the file using (StreamWriter sw = File.AppendText(logPath)) { sw.WriteLine(text2write); sw.Close(); answ = true; } } finally { // Release lock _readWriteLock.ExitWriteLock(); } return answ; } /// /// scrive un messaggio di log con etichetta pre /// /// testo messaggio /// tipo di log da registrare (etichetta [...]) /// public bool scriviLog(string testoLog, tipoLog tipo) { bool answ = false; answ = scriviLog(string.Format("[{0}] - {1}", tipo, testoLog)); // se è un tipo EXCEPTION allora scrivo anche log diagnostico if (tipo == tipoLog.EXCEPTION && enableDumpDiag) { try { string[] pingTagets = SteamWare.StringSplitter.CSplitter.Split(memLayer.ML.CRS("pingTargets"), "#", true, -1, SteamWare.StringSplitter.ComparisonMethod.Text); string[] wwwTagets = SteamWare.StringSplitter.CSplitter.Split(memLayer.ML.CRS("wwwTargets"), "#", true, -1, SteamWare.StringSplitter.ComparisonMethod.Text); // chiamo scrittura diagnostica scriviDiagnostica("DUMP diagnostico causa Eccezione", pingTagets, wwwTagets); } catch { } } return answ; } /// /// scrive su log un dump di diagnostica /// /// Causale diagnostica /// target per test PING /// target x download www page public void scriviDiagnostica(string testoLog, string[] pingTargets, string[] wwwTargets) { string txtDiag = ""; try { scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, SteamwareStrings.charLine('+', 80))); scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, SteamwareStrings.charLine('+', 80))); scriviLog(string.Format("[{0}] - {1}{2}", tipoLog.DUMP_DIAGN, testoLog, Environment.NewLine)); } catch { } try { scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, formDiagBlock("UPTIME", Diagnostica.uptime))); } catch { } try { scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, formDiagBlock("STORAGE", Diagnostica.diskUsage))); } catch { } try { scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, formDiagBlock("USB", Diagnostica.usbDevices))); } catch { } try { scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, formDiagBlock("SERIAL", Diagnostica.serialPorts))); } catch { } try { scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, formDiagBlock("NETWORKING (interfaces)", Diagnostica.networkInterfaces))); } catch { } try { // se richiesto faccio PING test if (pingTargets.Length > 0) { txtDiag = ""; foreach (string target in pingTargets) { txtDiag += SteamwareStrings.addLine(Diagnostica.pingIp(target)); } scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, formDiagBlock("NETWORKING (ping)", txtDiag))); } } catch { } try { // se richiesto faccio PING test if (wwwTargets.Length > 0) { txtDiag = ""; foreach (string target in wwwTargets) { txtDiag += SteamwareStrings.addLine(string.Format("WebPage Test ({2}): {0}{1}", Environment.NewLine, Diagnostica.getPageContent(target), target)); } scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, formDiagBlock("NETWORKING (www)", txtDiag))); } } catch { } // chiudo scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, SteamwareStrings.charLine('-', 80))); scriviLog(string.Format("[{0}]{1}{2}", tipoLog.DUMP_DIAGN, Environment.NewLine, SteamwareStrings.charLine('-', 80))); } /// /// formatta un blococ di diagnostica (titolo, contenuto / eccezione) /// /// /// /// protected string formDiagBlock(string titolo, string contenuto) { string answ = SteamwareStrings.charTitle(titolo, '-', 60); try { answ += SteamwareStrings.addLine(contenuto); } catch { answ += string.Format("Errore diagnostica {0}", titolo); } return answ; } #endregion #region metodi privati /// /// fornisce il nome del file in cui loggare (ed eventualmente crea...) /// protected void newLogfileName() { DateTime CurrentDateTime = DateTime.Now; _logfileName = _logBaseDir + String.Format("{0}.log", CurrentDateTime.ToString("yyyyMMdd")); } /// /// provvede a verificare la dim della cartella dei log e cancella i + vecchi fino a restare a dim inferiori a _logMaxMb /// protected void shrinkDir() { // ottengo elenco files *.log fileMover.obj.setDirectory(_logBaseDir); FileInfo[] _fis = fileMover.obj.elencoFiles_FI("*.log"); compressAndRemove(_fis); // acnhe per i "Vecchi" formati .txt _fis = fileMover.obj.elencoFiles_FI("*.txt"); compressAndRemove(_fis); } private void compressAndRemove(FileInfo[] _fis) { foreach (FileInfo _file in _fis) { if (_file.CreationTime < DateTime.Now.AddDays(-2)) // zippo files + vecchi di 2 gg... { fileMover.obj.zippaSingoloFile(_file); // cancello l'originale... fileMover.obj.eliminaFile(_file); } } // verifico directory e dimensione... while (fileMover.obj.totalMb() > _logMaxMb) { // cancello i + vecchi fino a rientrare alla dimensione max... fileMover.obj.deleteOldest(); } } /// /// fornisce il file + vecchio /// /// /// protected void deleteOldest(DirectoryInfo _di) { FileInfo[] _fis = _di.GetFiles(); DateTime _oldest = DateTime.Now; string _nome = ""; foreach (FileInfo _file in _fis) { if (_file.CreationTime < _oldest) { _nome = _file.Name; } } FileInfo fi = new FileInfo(_nome); fi.Delete(); } #endregion } /// /// tipo di log ammesso /// public enum tipoLog { /// /// informazioni di debug /// DEBUG, /// /// dump diagnostica /// DUMP_DIAGN, /// /// errori /// ERROR, /// /// eccezioni nell'esecuzione try/catch /// EXCEPTION, /// /// errori fatali /// FATAL, /// /// informazioni opzionali /// INFO, /// /// log dei lemmi invocati per traduzione da vocabolario /// LEMMA, /// /// fase di avvio componente /// STARTUP, /// /// avvisi /// WARNING } }