You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
4.7 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. using Ionic.Zlib;
  2. using System;
  3. using System.IO;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. #if NET3
  8. using Net3_Proxy;
  9. using Path = Net3_Proxy.Path;
  10. #endif
  11. namespace IPA.Logging.Printers
  12. {
  13. /// <summary>
  14. /// A <see cref="LogPrinter"/> abstract class that provides the utilities to write to a GZip file.
  15. /// </summary>
  16. public abstract class GZFilePrinter : LogPrinter, IDisposable
  17. {
  18. [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
  19. private static extern bool CreateHardLink(
  20. string lpFileName,
  21. string lpExistingFileName,
  22. IntPtr lpSecurityAttributes
  23. );
  24. internal static Regex removeControlCodes = new Regex("\x1b\\[\\d+m", RegexOptions.Compiled);
  25. private FileInfo fileInfo;
  26. /// <summary>
  27. /// The <see cref="StreamWriter"/> that writes to the GZip file.
  28. /// </summary>
  29. /// <value>the writer to the underlying filestream</value>
  30. protected StreamWriter FileWriter;
  31. private FileStream fstream;
  32. /// <summary>
  33. /// Gets the <see cref="FileInfo"/> for the file to write to.
  34. /// </summary>
  35. /// <returns>the file to write to</returns>
  36. protected abstract FileInfo GetFileInfo();
  37. private const string latestFormat = "_latest{0}";
  38. private void InitLog()
  39. {
  40. try
  41. {
  42. if (fileInfo == null)
  43. { // first init
  44. fileInfo = GetFileInfo();
  45. var ext = fileInfo.Extension;
  46. var symlink = new FileInfo(Path.Combine(fileInfo.DirectoryName ?? throw new InvalidOperationException(), string.Format(latestFormat, ext)));
  47. if (symlink.Exists) symlink.Delete();
  48. foreach (var file in fileInfo.Directory.EnumerateFiles("*.log", SearchOption.TopDirectoryOnly))
  49. {
  50. if (file.Equals(fileInfo)) continue;
  51. if (file.Extension == ".gz") continue;
  52. CompressOldLog(file);
  53. }
  54. fileInfo.Create().Close();
  55. try
  56. {
  57. if (!CreateHardLink(symlink.FullName, fileInfo.FullName, IntPtr.Zero))
  58. {
  59. var error = Marshal.GetLastWin32Error();
  60. Logger.log.Error($"Hardlink creation failed ({error})");
  61. }
  62. }
  63. catch (Exception e)
  64. {
  65. Logger.log.Error("Error creating latest hardlink!");
  66. Logger.log.Error(e);
  67. }
  68. }
  69. }
  70. catch (Exception e)
  71. {
  72. Logger.log.Error("Error initializing log!");
  73. Logger.log.Error(e);
  74. }
  75. }
  76. private static async void CompressOldLog(FileInfo file)
  77. {
  78. Logger.log.Debug($"Compressing log file {file}");
  79. var newFile = new FileInfo(file.FullName + ".gz");
  80. using (var istream = file.OpenRead())
  81. using (var ostream = newFile.Create())
  82. using (var gz = new GZipStream(ostream, CompressionMode.Compress, CompressionLevel.BestCompression, false))
  83. await istream.CopyToAsync(gz);
  84. file.Delete();
  85. }
  86. /// <summary>
  87. /// Called at the start of any print session.
  88. /// </summary>
  89. public sealed override void StartPrint()
  90. {
  91. InitLog();
  92. fstream = fileInfo.Open(FileMode.Append, FileAccess.Write);
  93. FileWriter = new StreamWriter(fstream, new UTF8Encoding(false));
  94. }
  95. /// <summary>
  96. /// Called at the end of any print session.
  97. /// </summary>
  98. public sealed override void EndPrint()
  99. {
  100. FileWriter.Flush();
  101. fstream.Flush();
  102. FileWriter.Dispose();
  103. fstream.Dispose();
  104. FileWriter = null;
  105. fstream = null;
  106. }
  107. /// <inheritdoc />
  108. public void Dispose()
  109. {
  110. Dispose(true);
  111. GC.SuppressFinalize(this);
  112. }
  113. /// <summary>
  114. /// Disposes the file printer.
  115. /// </summary>
  116. /// <param name="disposing">does nothing</param>
  117. protected virtual void Dispose(bool disposing)
  118. {
  119. if (disposing)
  120. {
  121. FileWriter.Flush();
  122. fstream.Flush();
  123. FileWriter.Close();
  124. fstream.Close();
  125. FileWriter.Dispose();
  126. fstream.Dispose();
  127. }
  128. }
  129. }
  130. }