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.

147 lines
4.7 KiB

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