2013-06-05 8 views
7

Aplikacja My C# winforms 4.0 używa bezpiecznego strumienia wątku do wewnętrznego, debugującego rejestrowania informacji. Kiedy moja aplikacja się otworzy, usuwa plik i odtwarza go. Po zamknięciu aplikacji zapisuje plik.Używanie StreamWriter do implementacji dziennika toczenia i usuwanie z górnej wersji

Chciałbym zmodyfikować moją aplikację tak, aby dołączała zamiast zastępować. To jest prosta naprawa.

Jednak tutaj jest moje pytanie:

Chciałbym, aby moje plik dziennika maksymalnej około 10 megabajtów. Moje ograniczenie byłoby proste. Po przejściu do zamknięcia pliku, jeśli plik jest większy niż 10 megabajtów, zmniejsz pierwsze 10%.

Czy jest 'lepszy' sposób, a następnie wykonując następujące czynności:

  1. zamknąć plik
  2. sprawdzić, czy plik jest> 10 meg
  3. Jeśli tak, otwórz plik
  4. analizowania cała sprawa
  5. Cull pierwszy 10%
  6. zapisać plik z powrotem
  7. Zamknij

Edycja: cóż, skończyłem przewracać własne (pokazane poniżej) sugestia, aby przenieść się do Log4Net jest dobre, ale czas, który zajmie, aby nauczyć się nowej biblioteki i przenieść wszystkie moje wyciągi z dziennika (Tysiące) nie jest efektywne czasowo dla niewielkiego ulepszenia, które próbowałem wykonać.

private static void PerformFileTrim(string filename) 
    { 
    var FileSize = Convert.ToDecimal((new System.IO.FileInfo(filename)).Length); 

    if (FileSize > 5000000) 
    { 
     var file = File.ReadAllLines(filename).ToList(); 
     var AmountToCull = (int)(file.Count * 0.33); 
     var trimmed = file.Skip(AmountToCull).ToList(); 
     File.WriteAllLines(filename, trimmed); 
    } 
    } 
+5

A co z wykorzystaniem konkretnej biblioteki zapisanej do logowania, która już implementuje tę funkcjonalność? (Log4Net przychodzi na myśl) – Steve

+0

Nie poświęciłem czasu na zajrzenie do Log4Net. Do tej pory moja prosta klasa streamerów świetnie się spisała. Jeśli Log4Net zezwoli na "toczący się" log, którego szukam, zdecydowanie jest to rozwiązanie, którego powinienem użyć. – greggorob64

+0

Przyszli czytelnicy: '" ... [do] przeniesienia wszystkich moich logowań (tysięcy) do góry nie jest czasochłonne "" - i dlatego polegamy na wtrysku zależności i interfejsach. :) Lepszy projekt, np. Wstrzyknięcie instancji 'ILogger' (na przykład) do wszystkich klas, które wymagają logowania - lub nawet posiadanie pojedynczej głównej klasy/funkcji rejestrowania - pozwoli ci modyfikować wszystkie funkcje rejestrowania w jednym miejscu podczas zmiany wymagań rejestrowania . – brichins

Odpowiedz

1

Szukałem przez Win32 API, a nie jestem nawet pewien, czy to możliwe, aby to zrobić z rodzimych połączeń win32 VFS, nieważne przez Net.

Jedynym rozwiązaniem byłoby użycie plików mapowanych w pamięci i ręczne przenoszenie danych, co wydaje się wspierać .Net od wersji .Net 4.0.

Memory Mapped Files

+0

mapowane pliki pamięci to przesada dla plików 10mb – VladL

6

Badałem ten jeden raz i nigdy nie wymyślił nic, ale mogę zaoferować Plan B tutaj:

używam wyboru poniżej, aby zachować maksymalnie 3 plików dziennika. Najpierw tworzony jest plik dziennika 1 i dołączany do niego. Po przekroczeniu maksymalnego rozmiaru tworzone są dzienniki 2 i później 3. Gdy log 3 jest zbyt duży, dziennik 1 zostaje usunięty, a pozostałe dzienniki zostają przesunięte w dół stosu.

string[] logFileList = Directory.GetFiles(Path.GetTempPath(), "add_all_*.log", SearchOption.TopDirectoryOnly); 
if (logFileList.Count() > 1) 
{ 
    Array.Sort(logFileList, 0, logFileList.Count()); 
} 

if (logFileList.Any()) 
{ 
    string currFilePath = logFileList.Last(); 
    string[] dotSplit = currFilePath.Split('.'); 
    string lastChars = dotSplit[0].Substring(dotSplit[0].Length - 3); 
    ctr = Int32.Parse(lastChars); 
    FileInfo f = new FileInfo(currFilePath); 

    if (f.Length > MaxLogSize) 
    { 
     if (logFileList.Count() > MaxLogCount) 
     { 
      File.Delete(logFileList[0]); 
      for (int i = 1; i < MaxLogCount + 1; i++) 
      { 
       Debug.WriteLine(string.Format("moving: {0} {1}", logFileList[i], logFileList[i - 1])); 
       File.Move(logFileList[i], logFileList[i - 1]); // push older log files back, in order to pop new log on top 
      } 
     } 
     else 
     { 
      ctr++; 
     } 
    } 
} 
+0

Musiałem wprowadzić kilka modyfikacji, aby uruchomić to dla mnie - dodałem go jako osobną odpowiedź ze względu na ograniczenia rozmiaru ... – user3902302

2

ten wywodzi się z odpowiedzią bigtech za:

private static string RollLogFile() 
    { 
     string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); 
     string appName = Path.GetFileNameWithoutExtension(Environment.GetCommandLineArgs()[0]); 
     string wildLogName = string.Format("{0}*.log",appName); 

     int fileCounter = 0; 
     string[] logFileList = Directory.GetFiles(path, wildLogName, SearchOption.TopDirectoryOnly); 
     if (logFileList.Length > 0) 
     { 
      Array.Sort(logFileList, 0, logFileList.Length); 
      fileCounter = logFileList.Length - 1; 
      //Make sure we apply the MaxLogCount (but only once to reduce the delay) 
      if (logFileList.Length > MaxLogCount) 
      { 
       //Too many files - remove one and rename the others 
       File.Delete(logFileList[0]); 
       for (int i = 1; i < logFileList.Length; i++) 
       { 
        File.Move(logFileList[i], logFileList[i - 1]); 
       } 
       --fileCounter; 
      } 

      string currFilePath = logFileList[fileCounter]; 
      FileInfo f = new FileInfo(currFilePath); 
      if (f.Length < MaxLogSize) 
      { 
       //still room in the current file 
       return currFilePath; 
      } 
      else 
      { 
       //need another filename 
       ++fileCounter;     
      } 

     } 
     return string.Format("{0}{1}{2}{3:00}.log", path, Path.DirectorySeparatorChar, appName, fileCounter); 
    } 

Zastosowanie:

string logFileName = RollLogFile(); 
using (StreamWriter sw = new StreamWriter(logFileName, true)) 
{ 
    sw.AutoFlush = true; 
    sw.WriteLine(string.Format("{0:u} {1}", DateTime.Now, message)); 
} 
2

Tutejsze rozwiązania nie naprawdę pracować dla mnie. Wziąłem odpowiedź user3902302, która ponownie opierała się na odpowiedzi firmy bigtech i napisała pełną klasę. Ponadto NIE używam StreamWriter, można zmienić jedną linię (AppendAllText przeciwko równoważnikowi StreamWrite).

Występuje niewielka obsługa błędów (np. Ponownie spróbuj uzyskać dostęp, gdy się nie powiedzie, chociaż blokada powinna przechwytywać cały wewnętrzny dostęp równoległy).

Może to wystarczyć dla niektórych osób, które wcześniej musiały użyć dużego rozwiązania, takiego jak log4net lub nlog. (I log4net RollingAppender nawet nie jest bezpieczny wątku, ten jest :).)

public class RollingLogger 
{ 
    readonly static string LOG_FILE = @"c:\temp\logfile.log"; 
    readonly static int MaxRolledLogCount = 3; 
    readonly static int MaxLogSize = 1024; // 1 * 1024 * 1024; <- small value for testing that it works, you can try yourself, and then use a reasonable size, like 1M-10M 

    public static void LogMessage(string msg) 
    { 
     lock (LOG_FILE) // lock is optional, but.. should this ever be called by multiple threads, it is safer 
     { 
      RollLogFile(LOG_FILE); 
      File.AppendAllText(LOG_FILE, msg + Environment.NewLine, Encoding.UTF8); 
     } 
    } 

    private static void RollLogFile(string logFilePath) 
    { 
     try 
     { 
      var length = new FileInfo(logFilePath).Length; 

      if (length > MaxLogSize) 
      { 
       var path = Path.GetDirectoryName(logFilePath); 
       var wildLogName = Path.GetFileNameWithoutExtension(logFilePath) + "*" + Path.GetExtension(logFilePath); 
       var bareLogFilePath = Path.Combine(path, Path.GetFileNameWithoutExtension(logFilePath)); 
       string[] logFileList = Directory.GetFiles(path, wildLogName, SearchOption.TopDirectoryOnly); 
       if (logFileList.Length > 0) 
       { 
        // only take files like logfilename.log and logfilename.0.log, so there also can be a maximum of 10 additional rolled files (0..9) 
        var rolledLogFileList = logFileList.Where(fileName => fileName.Length == (logFilePath.Length + 2)).ToArray(); 
        Array.Sort(rolledLogFileList, 0, rolledLogFileList.Length); 
        if (rolledLogFileList.Length >= MaxRolledLogCount) 
        { 
         File.Delete(rolledLogFileList[MaxRolledLogCount - 1]); 
         var list = rolledLogFileList.ToList(); 
         list.RemoveAt(MaxRolledLogCount - 1); 
         rolledLogFileList = list.ToArray(); 
        } 
        // move remaining rolled files 
        for (int i = rolledLogFileList.Length; i > 0; --i) 
         File.Move(rolledLogFileList[i - 1], bareLogFilePath + "." + i + Path.GetExtension(logFilePath)); 
        var targetPath = bareLogFilePath + ".0" + Path.GetExtension(logFilePath); 
        // move original file 
        File.Move(logFilePath, targetPath); 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      System.Diagnostics.Debug.WriteLine(ex.ToString()); 
     } 
    } 
} 

edit:
Od Właśnie zauważyłem, że pytasz nieco inne pytanie: czy wasze linie różnią się znacznie pod względem wielkości To byłaby odmiana (w 90% przypadków nie poprawiła się nad twoją i może być nieznacznie szybsza, wprowadzono także nowy nieobsługiwany błąd (\ n nie jest obecny)):

private static void PerformFileTrim(string filename) 
    { 
     var fileSize = (new System.IO.FileInfo(filename)).Length; 

     if (fileSize > 5000000) 
     { 
      var text = File.ReadAllText(filename); 
      var amountToCull = (int)(text.Length * 0.33); 
      amountToCull = text.IndexOf('\n', amountToCull); 
      var trimmedText = text.Substring(amountToCull + 1); 
      File.WriteAllText(filename, trimmedText); 
     } 
    } 
2

Ta funkcja umożliwia obracanie dziennika na podstawie dni roboczych. Po raz pierwszy nasza aplikacja uruchomi się w poniedziałek, sprawdzi istniejący wpis na poniedziałkową datę, jeśli jeszcze nie została zainicjowana na dzisiaj, odrzuci stare wpisy i ponownie zainicjuje nowy plik. Przez cały ten dzień plik będzie nadal dołączał tekst do tego samego pliku dziennika.

Zostanie utworzonych 7 plików dziennika. debug-Mon.txt, debog-Tue.txt ...

doda również nazwę metody, która faktycznie zalogowała wiadomość wraz z datą. bardzo przydatne do ogólnego zastosowania.

Powiązane problemy