-
Notifications
You must be signed in to change notification settings - Fork 113
「ログ出力時に毎回ファイルを開いている」の修正 #67
base: feature/log_improvement
Are you sure you want to change the base?
Changes from 19 commits
de947fc
94b7d96
d0b4d44
36b68b0
082a089
016beb2
a9b4b62
e72f0da
b459354
cfc16a0
a00563a
03f7fab
a68cac6
b4f15d8
2f6002d
68b7d9f
f4986d2
186a323
72a4f73
c4bec33
09eaa47
c00b730
a4b32c0
bc848ce
af59231
b0a617c
ac1f49f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,7 @@ public LogPeriodicDeleteReceiver() | |
{ | ||
var essensialService = new EssentialsService(); | ||
var logPathService = new LogPathService(new LogPathServiceAndroid()); | ||
loggerService = new LoggerService(logPathService, essensialService); | ||
loggerService = new LoggerService(new LogWriter(logPathService, essensialService)); | ||
logFileService = new Covid19Radar.Services.Logs.LogFileService(loggerService, logPathService); | ||
Comment on lines
62
to
65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. この部分だけServiceLocatorから外れていて、Singleton保てていないんですね。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ | ||
|
||
#nullable enable | ||
|
||
using System; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Text; | ||
using System.Threading; | ||
using Covid19Radar.Common; | ||
|
||
namespace Covid19Radar.Services.Logs | ||
{ | ||
/// <summary> | ||
/// ログの書き込みを行う機能を提供します。 | ||
/// </summary> | ||
public interface ILogWriter | ||
{ | ||
/// <summary> | ||
/// ログを出力します。 | ||
/// </summary> | ||
/// <param name="message">出力するメッセージです。</param> | ||
/// <param name="method">実行中の関数の名前です。</param> | ||
/// <param name="filePath">実行中の関数を格納しているソースファイルの名前です。</param> | ||
/// <param name="lineNumber">実行中の関数のソースファイル内での行番号です。</param> | ||
/// <param name="logLevel">ログレベルです。</param> | ||
public void Write(string? message, string method, string filePath, int lineNumber, LogLevel logLevel); | ||
} | ||
|
||
/// <summary> | ||
/// <see cref="Covid19Radar.Services.Logs.ILogWriter"/>の実装です。 | ||
/// </summary> | ||
public sealed class LogWriter : ILogWriter | ||
{ | ||
[ThreadStatic()] | ||
private static StringBuilder? _sb; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 後述しますがこのPRではLINQ周りは変えない方がいいと思うので There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
private const string DATETIME_FORMAT = "yyyy/MM/dd HH:mm:ss.fffffff"; | ||
private static readonly string HEADER = CreateLogHeaderRow(); | ||
private readonly ILogPathService _log_path; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 名前がここだけがスネークケースなので、これはキャメルケースにした上でServiceをつけて |
||
private readonly IEssentialsService _essentials; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. こちらも |
||
private readonly Encoding _enc; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここは |
||
private File? _file; | ||
|
||
/// <summary> | ||
/// 型'<see cref="Covid19Radar.Services.Logs.LogWriter"/>'の新しいインスタンスを生成します。 | ||
/// </summary> | ||
/// <param name="logPath">ログファイルのパスを提供するサービスを指定します。</param> | ||
/// <param name="essentials">環境情報を提供するサービスを指定します。</param> | ||
public LogWriter(ILogPathService logPath, IEssentialsService essentials) | ||
{ | ||
_log_path = logPath ?? throw new ArgumentNullException(nameof(logPath)); | ||
_essentials = essentials ?? throw new ArgumentNullException(nameof(essentials)); | ||
_enc = Encoding.UTF8; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここはコンストラクタではなく、メンバを宣言するときに初期化で書いてしまった方が良い気がします。 |
||
} | ||
|
||
/// <inheritdoc/> | ||
public void Write(string? message, string method, string filePath, int lineNumber, LogLevel logLevel) | ||
{ | ||
#if !DEBUG | ||
if (logLevel == LogLevel.Verbose || logLevel == LogLevel.Debug) { | ||
return; | ||
} | ||
#endif | ||
try { | ||
var jstNow = Utils.JstNow(); | ||
string row = CreateLogContentRow(message ?? string.Empty, method, filePath, lineNumber, logLevel, jstNow, _essentials); | ||
Debug.WriteLine(row); | ||
this.WriteLine(jstNow, row); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. この部分では日付を取得してから、 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 下記の可能性が考えられます。
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} catch (Exception e) { | ||
Debug.WriteLine(e.ToString()); | ||
} | ||
} | ||
|
||
private void WriteLine(DateTime jstNow, string line) | ||
{ | ||
var file = _file; | ||
string fname = _log_path.LogFilePath(jstNow); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここは省略せず |
||
if (file is null || file.FileName != fname) { | ||
var newFile = new File(fname, _enc); | ||
do { | ||
if (Interlocked.CompareExchange(ref _file, newFile, file) == file) { | ||
newFile.Writer.WriteLine(HEADER); | ||
file?.Dispose(); | ||
file = newFile; | ||
break; | ||
} | ||
Thread.Yield(); | ||
file = _file; | ||
} while (file is null || file.FileName != fname); | ||
} | ||
file.Writer.WriteLine(line); | ||
} | ||
|
||
private static string CreateLogHeaderRow() | ||
{ | ||
return CreateLogRow( | ||
"output_date", | ||
"log_level", | ||
"message", | ||
"method", | ||
"file_path", | ||
"line_number", | ||
"platform", | ||
"platform_version", | ||
"model", | ||
"device_type", | ||
"app_version", | ||
"build_number" | ||
); | ||
} | ||
|
||
private static string CreateLogContentRow( | ||
string message, | ||
string method, | ||
string filePath, | ||
int lineNumber, | ||
LogLevel logLevel, | ||
DateTime jstDateTime, | ||
IEssentialsService essentials) | ||
{ | ||
return CreateLogRow( | ||
jstDateTime.ToString(DATETIME_FORMAT), | ||
logLevel.ToString(), | ||
message, | ||
method, | ||
filePath, | ||
lineNumber.ToString(), | ||
essentials.Platform, | ||
essentials.PlatformVersion, | ||
essentials.Model, | ||
essentials.DeviceType, | ||
essentials.AppVersion, | ||
essentials.BuildNumber | ||
); | ||
} | ||
|
||
private static string CreateLogRow(params string[] cols) | ||
{ | ||
_ = _sb is null ? _sb = new StringBuilder() | ||
: _sb.Clear(); | ||
|
||
foreach (string col in cols) { | ||
_sb.Append(",\""); | ||
foreach (char ch in col) { | ||
string? escaped = ch switch { | ||
'\t' => "\\t", '\v' => "\\v", | ||
'\r' => "\\r", '\n' => "\\n", | ||
'\\' => "\\\\", '\"' => "\"\"", | ||
_ => null | ||
}; | ||
Comment on lines
+167
to
+172
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 以前もお伝えしたとおり、 本PRの主題は『「ログ出力時に毎回ファイルを開いている」の修正』なので、この変更を含めてしまうのは主題から外れるという認識です。性能面での改善は別PRにしてもらい、そこで検討するのが適切と考えます。 |
||
_ = escaped is null ? _sb.Append(ch) | ||
: _sb.Append(escaped); | ||
} | ||
_sb.Append('\"'); | ||
} | ||
_sb.Remove(0, 1); | ||
return _sb.ToString(); | ||
} | ||
|
||
private sealed class File : IDisposable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
{ | ||
private readonly Encoding _enc; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 省略せず |
||
private readonly Lazy<StreamWriter> _sw; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 省略せず |
||
|
||
internal string FileName { get; } | ||
internal StreamWriter Writer => _sw.Value; | ||
|
||
internal File(string path, Encoding enc) | ||
{ | ||
string dir = Path.GetDirectoryName(path); | ||
if (!Directory.Exists(dir)) { | ||
Directory.CreateDirectory(dir); | ||
} | ||
|
||
this.FileName = path; | ||
_enc = enc; | ||
_sw = new Lazy<StreamWriter>(this.OpenFile, LazyThreadSafetyMode.PublicationOnly); | ||
} | ||
|
||
private StreamWriter OpenFile() | ||
{ | ||
var sw = new StreamWriter(new FileStream(this.FileName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite), _enc); | ||
sw.AutoFlush = true; | ||
return sw; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
this.Writer.Dispose(); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CONTRIBUTORに関しては新しく項目を設けますね。